Skip to content

Commit f465ab8

Browse files
original-brownbeardakrone
authored andcommitted
#26260 Allow ip_range to accept CIDR notation (#27192)
* #26260 Allow ip_range to accept CIDR notation * #26260 added non-byte-alligned cidr test cases
1 parent 46b9ccf commit f465ab8

File tree

2 files changed

+110
-1
lines changed

2 files changed

+110
-1
lines changed

modules/mapper-extras/src/main/java/org/elasticsearch/index/mapper/RangeFieldMapper.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
import org.elasticsearch.Version;
4343
import org.elasticsearch.common.Explicit;
4444
import org.elasticsearch.common.Nullable;
45+
import org.elasticsearch.common.collect.Tuple;
4546
import org.elasticsearch.common.geo.ShapeRelation;
4647
import org.elasticsearch.common.joda.DateMathParser;
4748
import org.elasticsearch.common.joda.FormatDateTimeFormatter;
@@ -57,6 +58,7 @@
5758

5859
import java.io.IOException;
5960
import java.net.InetAddress;
61+
import java.net.UnknownHostException;
6062
import java.util.ArrayList;
6163
import java.util.HashSet;
6264
import java.util.Iterator;
@@ -357,7 +359,8 @@ protected void parseCreateField(ParseContext context, List<IndexableField> field
357359
range = context.parseExternalValue(Range.class);
358360
} else {
359361
XContentParser parser = context.parser();
360-
if (parser.currentToken() == XContentParser.Token.START_OBJECT) {
362+
final XContentParser.Token start = parser.currentToken();
363+
if (start == XContentParser.Token.START_OBJECT) {
361364
RangeFieldType fieldType = fieldType();
362365
RangeType rangeType = fieldType.rangeType;
363366
String fieldName = null;
@@ -397,6 +400,8 @@ protected void parseCreateField(ParseContext context, List<IndexableField> field
397400
}
398401
}
399402
range = new Range(rangeType, from, to, includeFrom, includeTo);
403+
} else if (fieldType().rangeType == RangeType.IP && start == XContentParser.Token.VALUE_STRING) {
404+
range = parseIpRangeFromCidr(parser);
400405
} else {
401406
throw new MapperParsingException("error parsing field ["
402407
+ name() + "], expected an object but got " + parser.currentName());
@@ -448,6 +453,23 @@ && fieldType().dateTimeFormatter().locale() != Locale.ROOT))) {
448453
}
449454
}
450455

456+
private static Range parseIpRangeFromCidr(final XContentParser parser) throws IOException {
457+
final Tuple<InetAddress, Integer> cidr = InetAddresses.parseCidr(parser.text());
458+
// create the lower value by zeroing out the host portion, upper value by filling it with all ones.
459+
byte[] lower = cidr.v1().getAddress();
460+
byte[] upper = lower.clone();
461+
for (int i = cidr.v2(); i < 8 * lower.length; i++) {
462+
int m = 1 << 7 - (i & 7);
463+
lower[i >> 3] &= ~m;
464+
upper[i >> 3] |= m;
465+
}
466+
try {
467+
return new Range(RangeType.IP, InetAddress.getByAddress(lower), InetAddress.getByAddress(upper), true, true);
468+
} catch (UnknownHostException bogus) {
469+
throw new AssertionError(bogus);
470+
}
471+
}
472+
451473
/** Enum defining the type of range */
452474
public enum RangeType {
453475
IP("ip_range") {
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
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+
package org.elasticsearch.index.mapper;
20+
21+
import java.util.Collection;
22+
import java.util.HashMap;
23+
import java.util.Map;
24+
import org.apache.lucene.index.DocValuesType;
25+
import org.apache.lucene.index.IndexableField;
26+
import org.elasticsearch.common.compress.CompressedXContent;
27+
import org.elasticsearch.common.network.InetAddresses;
28+
import org.elasticsearch.common.xcontent.XContentBuilder;
29+
import org.elasticsearch.common.xcontent.XContentFactory;
30+
import org.elasticsearch.common.xcontent.XContentType;
31+
import org.elasticsearch.index.IndexService;
32+
import org.elasticsearch.plugins.Plugin;
33+
import org.elasticsearch.test.ESSingleNodeTestCase;
34+
import org.junit.Before;
35+
36+
import static org.hamcrest.Matchers.containsString;
37+
38+
public class IpRangeFieldMapperTests extends ESSingleNodeTestCase {
39+
40+
private IndexService indexService;
41+
private DocumentMapperParser parser;
42+
43+
@Override
44+
protected Collection<Class<? extends Plugin>> getPlugins() {
45+
return pluginList(MapperExtrasPlugin.class);
46+
}
47+
48+
@Before
49+
public void setup() {
50+
indexService = createIndex("test");
51+
parser = indexService.mapperService().documentMapperParser();
52+
}
53+
54+
public void testStoreCidr() throws Exception {
55+
XContentBuilder mapping = XContentFactory.jsonBuilder().startObject().startObject("type")
56+
.startObject("properties").startObject("field").field("type", "ip_range")
57+
.field("store", true);
58+
mapping = mapping.endObject().endObject().endObject().endObject();
59+
DocumentMapper mapper = parser.parse("type", new CompressedXContent(mapping.string()));
60+
assertEquals(mapping.string(), mapper.mappingSource().toString());
61+
final Map<String, String> cases = new HashMap<>();
62+
cases.put("192.168.0.0/15", "192.169.255.255");
63+
cases.put("192.168.0.0/16", "192.168.255.255");
64+
cases.put("192.168.0.0/17", "192.168.127.255");
65+
for (final Map.Entry<String, String> entry : cases.entrySet()) {
66+
ParsedDocument doc =
67+
mapper.parse(SourceToParse.source("test", "type", "1", XContentFactory.jsonBuilder()
68+
.startObject()
69+
.field("field", entry.getKey())
70+
.endObject().bytes(),
71+
XContentType.JSON
72+
));
73+
IndexableField[] fields = doc.rootDoc().getFields("field");
74+
assertEquals(3, fields.length);
75+
IndexableField dvField = fields[0];
76+
assertEquals(DocValuesType.BINARY, dvField.fieldType().docValuesType());
77+
IndexableField pointField = fields[1];
78+
assertEquals(2, pointField.fieldType().pointDimensionCount());
79+
IndexableField storedField = fields[2];
80+
assertTrue(storedField.fieldType().stored());
81+
String strVal =
82+
InetAddresses.toAddrString(InetAddresses.forString("192.168.0.0")) + " : " +
83+
InetAddresses.toAddrString(InetAddresses.forString(entry.getValue()));
84+
assertThat(storedField.stringValue(), containsString(strVal));
85+
}
86+
}
87+
}

0 commit comments

Comments
 (0)