Skip to content

Commit 638ada3

Browse files
author
Christoph Büscher
committed
WIP First draft version field
1 parent afd26e5 commit 638ada3

File tree

14 files changed

+1490
-5
lines changed

14 files changed

+1490
-5
lines changed

server/src/internalClusterTest/java/org/elasticsearch/search/simple/SimpleSearchIT.java

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@
4444
import static org.elasticsearch.index.query.QueryBuilders.matchAllQuery;
4545
import static org.elasticsearch.index.query.QueryBuilders.queryStringQuery;
4646
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
47+
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
4748
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertAcked;
4849
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertFailures;
4950
import static org.elasticsearch.test.hamcrest.ElasticsearchAssertions.assertHitCount;
@@ -106,6 +107,95 @@ public void testSimpleIp() throws Exception {
106107
assertHitCount(search, 1L);
107108
}
108109

110+
public void testSimpleVersionRange() throws Exception {
111+
prepareCreate("test").setSettings(Settings.builder().put(SETTING_NUMBER_OF_SHARDS, 1).put(SETTING_NUMBER_OF_REPLICAS, 0)).get();
112+
ensureGreen();
113+
114+
client().admin().indices().preparePutMapping("test")
115+
116+
.setSource(XContentFactory.jsonBuilder().startObject().startObject("_doc").startObject("properties")
117+
.startObject("versionrange").field("type", "version_range").endObject()
118+
.endObject().endObject().endObject())
119+
.get();
120+
121+
client().prepareIndex("test")
122+
.setId("1")
123+
.setSource("{\"versionrange\":{\"gte\":\"1.1.0.0.0.0.0.0.0.0.1\",\"lte\":\"2.2.3\"}}", XContentType.JSON)
124+
.setRefreshPolicy(IMMEDIATE)
125+
.get();
126+
127+
client().prepareIndex("test")
128+
.setId("2")
129+
.setSource("{\"versionrange\":{\"gte\":\"4.0.0-beta.2\",\"lte\":\"4.0.0-rc5\"}}", XContentType.JSON)
130+
.setRefreshPolicy(IMMEDIATE)
131+
.get();
132+
133+
client().prepareIndex("test")
134+
.setId("3")
135+
.setSource("{\"versionrange\":{\"gte\":\"8.0.0-alpha.1\",\"lte\":\"8.0.0-alpha.9\"}}", XContentType.JSON)
136+
.setRefreshPolicy(IMMEDIATE)
137+
.get();
138+
139+
client().prepareIndex("test")
140+
.setId("4")
141+
.setSource("{\"versionrange\":{\"gte\":\"8.0.0-alpha.0\",\"lte\":\"8.0.0-alpha.0.1\"}}", XContentType.JSON)
142+
.setRefreshPolicy(IMMEDIATE)
143+
.get();
144+
145+
SearchResponse search = client().prepareSearch()
146+
.setQuery(rangeQuery("versionrange").gte("0.1.6").lte("2.7.0").relation("within"))
147+
.get();
148+
149+
assertHitCount(search, 1L);
150+
151+
search = client().prepareSearch()
152+
.setQuery(rangeQuery("versionrange").gte("1.1.6").lte("1.7.0").relation("contains"))
153+
.get();
154+
155+
assertHitCount(search, 1L);
156+
157+
search = client().prepareSearch()
158+
.setQuery(rangeQuery("versionrange").gte("1.1.6").lte("3.7.0").relation("contains"))
159+
.get();
160+
161+
assertHitCount(search, 0L);
162+
163+
164+
search = client().prepareSearch()
165+
.setQuery(rangeQuery("versionrange").gte("1.8.6").lte("3.7.0").relation("intersects"))
166+
.get();
167+
168+
assertHitCount(search, 1L);
169+
170+
search = client().prepareSearch()
171+
.setQuery(rangeQuery("versionrange").gte("3.0.0").lte("4.0.0").relation("within"))
172+
.get();
173+
174+
assertHitCount(search, 1L);
175+
176+
search = client().prepareSearch()
177+
.setQuery(rangeQuery("versionrange").gte("3.0.0").lte("4.0.0-rc3").relation("within"))
178+
.get();
179+
180+
assertHitCount(search, 0L);
181+
182+
search = client().prepareSearch()
183+
.setQuery(termQuery("versionrange", "4.0.0-rc3"))
184+
.get();
185+
186+
assertHitCount(search, 1L);
187+
188+
search = client().prepareSearch()
189+
.setQuery(rangeQuery("versionrange").gte("8.0.0-alpha.2").lte("8.0.0-alpha.4").relation("contains"))
190+
.get();
191+
192+
assertHitCount(search, 1L);
193+
}
194+
195+
196+
197+
198+
109199
public void testIpCidr() throws Exception {
110200
createIndex("test");
111201

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
/*
2+
* Licensed to Elasticsearch under one or more contributor
3+
* license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright
5+
* ownership. Elasticsearch licenses this file to you under
6+
* the Apache License, Version 2.0 (the "License"); you may
7+
* not use this file except in compliance with the License.
8+
* You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
package org.apache.lucene.document;
21+
22+
import org.apache.lucene.search.BooleanClause;
23+
import org.apache.lucene.search.BooleanClause.Occur;
24+
import org.apache.lucene.search.BooleanQuery;
25+
import org.apache.lucene.search.Query;
26+
import org.apache.lucene.util.BytesRef;
27+
import org.apache.lucene.util.FutureArrays;
28+
import org.elasticsearch.index.mapper.VersionEncoder;
29+
import org.elasticsearch.index.mapper.VersionEncoder.SortMode;
30+
31+
public class VersionRangeField extends Field {
32+
33+
public static final int BYTES = 16;
34+
35+
private static final FieldType TYPE;
36+
static {
37+
TYPE = new FieldType();
38+
TYPE.setDimensions(2, BYTES);
39+
TYPE.freeze();
40+
}
41+
42+
public VersionRangeField(String name, final BytesRef min, final BytesRef max) {
43+
super(name, TYPE);
44+
setRangeValues(min, max);
45+
}
46+
47+
/**
48+
* Change (or set) the min/max values of the field.
49+
* @param min range min value
50+
* @param max range max value
51+
*/
52+
public void setRangeValues(BytesRef min, BytesRef max) {
53+
final byte[] bytes;
54+
if (fieldsData == null) {
55+
bytes = new byte[BYTES * 2];
56+
fieldsData = new BytesRef(bytes);
57+
} else {
58+
bytes = ((BytesRef) fieldsData).bytes;
59+
}
60+
encode(min, max, bytes);
61+
}
62+
63+
/** encode the min/max range into the provided byte array */
64+
private static void encode(final BytesRef min, final BytesRef max, final byte[] bytes) {
65+
if (FutureArrays.compareUnsigned(min.bytes, 0, BYTES, max.bytes, 0, BYTES) > 0) {
66+
throw new IllegalArgumentException("min value cannot be greater than max value for version field");
67+
}
68+
System.arraycopy(min.bytes, 0, bytes, 0, BYTES);
69+
System.arraycopy(max.bytes, 0, bytes, BYTES, BYTES);
70+
}
71+
72+
/** encode the min/max range and return the byte array */
73+
private static byte[] encode(BytesRef min, BytesRef max) {
74+
byte[] b = new byte[BYTES*2];
75+
encode(min, max, b);
76+
return b;
77+
}
78+
79+
public static Query newIntersectsQuery(String field, BytesRef from, BytesRef to, Query dvQuery) {
80+
return newRelationQuery(field, from, to, RangeFieldQuery.QueryType.INTERSECTS, dvQuery);
81+
}
82+
83+
public static Query newWithinQuery(String field, BytesRef from, BytesRef to, Query dvQuery) {
84+
return newRelationQuery(field, from, to, RangeFieldQuery.QueryType.WITHIN, dvQuery);
85+
}
86+
87+
public static Query newContainsQuery(String field, BytesRef from, BytesRef to, Query dvQuery) {
88+
return newRelationQuery(field, from, to, RangeFieldQuery.QueryType.CONTAINS, dvQuery);
89+
}
90+
91+
/** helper method for creating the desired relational query */
92+
private static Query newRelationQuery(
93+
String field,
94+
final BytesRef min,
95+
final BytesRef max,
96+
RangeFieldQuery.QueryType relation,
97+
Query dvQuery
98+
) {
99+
RangeFieldQuery rangeFieldQuery = new RangeFieldQuery(field, encode(min, max), 1, relation) {
100+
@Override
101+
protected String toString(byte[] ranges, int dimension) {
102+
return VersionRangeField.toString(ranges, dimension);
103+
}
104+
};
105+
BooleanQuery conjunctionQuery = new BooleanQuery.Builder().add(new BooleanClause(rangeFieldQuery, Occur.MUST))
106+
.add(new BooleanClause(dvQuery, Occur.MUST)).build();
107+
return conjunctionQuery;
108+
}
109+
110+
/**
111+
* Returns the String representation for the range at the given dimension
112+
* @param ranges the encoded ranges, never null
113+
* @param dimension the dimension of interest (not used for this field)
114+
* @return The string representation for the range at the provided dimension
115+
*/
116+
private static String toString(byte[] ranges, int dimension) {
117+
byte[] min = new byte[BYTES];
118+
System.arraycopy(ranges, 0, min, 0, BYTES);
119+
byte[] max = new byte[BYTES];
120+
System.arraycopy(ranges, BYTES, max, 0, BYTES);
121+
return "["
122+
+ VersionEncoder.decodeVersion(new BytesRef(min), SortMode.SEMVER)
123+
+ " : "
124+
+ VersionEncoder.decodeVersion(new BytesRef(max), SortMode.SEMVER)
125+
+ "]";
126+
}
127+
128+
}

server/src/main/java/org/apache/lucene/queries/BinaryDocValuesRangeQuery.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,11 +87,13 @@ public boolean matches() throws IOException {
8787
int offset = in.getPosition();
8888
for (int i = 0; i < numRanges; i++) {
8989
int length = lengthType.readLength(bytes, offset);
90+
offset += lengthType.advanceBy();
9091
otherFrom.offset = offset;
9192
otherFrom.length = length;
9293
offset += length;
9394

9495
length = lengthType.readLength(bytes, offset);
96+
offset += lengthType.advanceBy();
9597
otherTo.offset = offset;
9698
otherTo.length = length;
9799
offset += length;

server/src/main/java/org/elasticsearch/index/mapper/BinaryRangeUtil.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -130,11 +130,11 @@ static List<RangeFieldMapper.Range> decodeRanges(BytesRef encodedRanges, RangeTy
130130
for (int i = 0; i < numRanges; i++) {
131131
int length = lengthType.readLength(bytes, offset);
132132
Object from = decodeBytes.apply(bytes, offset, length);
133-
offset += length;
133+
offset += length + lengthType.advanceBy();
134134

135135
length = lengthType.readLength(bytes, offset);
136136
Object to = decodeBytes.apply(bytes, offset, length);
137-
offset += length;
137+
offset += length + lengthType.advanceBy();
138138
// TODO: Support for exclusive ranges, pending resolution of #40601
139139
RangeFieldMapper.Range decodedRange = new RangeFieldMapper.Range(rangeType, from, to, true, true);
140140
ranges.add(decodedRange);

server/src/main/java/org/elasticsearch/index/mapper/RangeFieldMapper.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -489,6 +489,7 @@ public String toString() {
489489
sb.append(includeFrom ? '[' : '(');
490490
Object f = includeFrom || from.equals(type.minValue()) ? from : type.nextDown(from);
491491
Object t = includeTo || to.equals(type.maxValue()) ? to : type.nextUp(to);
492+
// TODO also handle new VERSION range type, maybe move "toString" to RangeType?
492493
sb.append(type == RangeType.IP ? InetAddresses.toAddrString((InetAddress)f) : f.toString());
493494
sb.append(" : ");
494495
sb.append(type == RangeType.IP ? InetAddresses.toAddrString((InetAddress)t) : t.toString());

0 commit comments

Comments
 (0)