Add index-time scripts to IP field mapper (#71617)
This commit allows you to set 'script' and 'on_script_error' parameters on IP field mappers, meaning that runtime IP fields can be made indexed simply by moving their definitions from the runtime section of the mappings to the properties section.
This commit is contained in:
parent
57e6c78a52
commit
67db2538f8
|
@ -54,7 +54,8 @@ The following parameters are accepted by `ip` fields:
|
|||
<<ignore-malformed,`ignore_malformed`>>::
|
||||
|
||||
If `true`, malformed IP addresses are ignored. If `false` (default), malformed
|
||||
IP addresses throw an exception and reject the whole document.
|
||||
IP addresses throw an exception and reject the whole document. Note that this
|
||||
cannot be set if the `script` parameter is used.
|
||||
|
||||
<<mapping-index,`index`>>::
|
||||
|
||||
|
@ -63,7 +64,28 @@ The following parameters are accepted by `ip` fields:
|
|||
<<null-value,`null_value`>>::
|
||||
|
||||
Accepts an IPv4 or IPv6 value which is substituted for any explicit `null` values.
|
||||
Defaults to `null`, which means the field is treated as missing.
|
||||
Defaults to `null`, which means the field is treated as missing. Note that
|
||||
this cannot be set if the `script` parameter is used.
|
||||
|
||||
`on_script_error`::
|
||||
|
||||
Defines what to do if the script defined by the `script` parameter
|
||||
throws an error at indexing time. Accepts `reject` (default), which
|
||||
will cause the entire document to be rejected, and `ignore`, which
|
||||
will register the field in the document's
|
||||
<<mapping-ignored-field,`_ignored`>> metadata field and continue
|
||||
indexing. This parameter can only be set if the `script` field is
|
||||
also set.
|
||||
|
||||
`script`::
|
||||
|
||||
If this parameter is set, then the field will index values generated
|
||||
by this script, rather than reading the values directly from the
|
||||
source. If a value is set for this field on the input document, then
|
||||
the document will be rejected with an error.
|
||||
Scripts are in the same format as their
|
||||
<<runtime-mapping-fields,runtime equivalent>>, and should emit strings
|
||||
containing IPv4 or IPv6 formatted addresses.
|
||||
|
||||
<<mapping-store,`store`>>::
|
||||
|
||||
|
|
|
@ -0,0 +1,161 @@
|
|||
---
|
||||
setup:
|
||||
- do:
|
||||
indices.create:
|
||||
index: http_logs
|
||||
body:
|
||||
settings:
|
||||
number_of_shards: 1
|
||||
number_of_replicas: 0
|
||||
mappings:
|
||||
properties:
|
||||
ip:
|
||||
type: ip
|
||||
script:
|
||||
source: |
|
||||
Matcher m = /([^ ]+) .+/.matcher(doc["message"].value);
|
||||
if (m.matches()) {
|
||||
emit(m.group(1));
|
||||
}
|
||||
# Test fetching from _source
|
||||
ip_from_source:
|
||||
type: ip
|
||||
script:
|
||||
source: |
|
||||
Matcher m = /([^ ]+) .+/.matcher(params._source.message);
|
||||
if (m.matches()) {
|
||||
emit(m.group(1));
|
||||
}
|
||||
# Test emitting many values
|
||||
ip_many:
|
||||
type: ip
|
||||
script:
|
||||
source: |
|
||||
String m = doc["message"].value;
|
||||
int end = m.indexOf(" ");
|
||||
end = m.lastIndexOf(".", end);
|
||||
String stem = m.substring(0, end + 1);
|
||||
for (int i = 0; i < 5; i++) {
|
||||
emit(stem + i);
|
||||
}
|
||||
timestamp:
|
||||
type: date
|
||||
message:
|
||||
type: keyword
|
||||
- do:
|
||||
bulk:
|
||||
index: http_logs
|
||||
refresh: true
|
||||
body: |
|
||||
{"index":{}}
|
||||
{"timestamp": "1998-04-30T14:30:17-05:00", "message" : "40.135.0.0 - - [1998-04-30T14:30:17-05:00] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
|
||||
{"index":{}}
|
||||
{"timestamp": "1998-04-30T14:30:53-05:00", "message" : "232.0.0.0 - - [1998-04-30T14:30:53-05:00] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
|
||||
{"index":{}}
|
||||
{"timestamp": "1998-04-30T14:31:12-05:00", "message" : "26.1.0.0 - - [1998-04-30T14:31:12-05:00] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
|
||||
{"index":{}}
|
||||
{"timestamp": "1998-04-30T14:31:19-05:00", "message" : "247.37.0.0 - - [1998-04-30T14:31:19-05:00] \"GET /french/splash_inet.html HTTP/1.0\" 200 3781"}
|
||||
{"index":{}}
|
||||
{"timestamp": "1998-04-30T14:31:22-05:00", "message" : "247.37.0.0 - - [1998-04-30T14:31:22-05:00] \"GET /images/hm_nbg.jpg HTTP/1.0\" 304 0"}
|
||||
{"index":{}}
|
||||
{"timestamp": "1998-04-30T14:31:27-05:00", "message" : "252.0.0.0 - - [1998-04-30T14:31:27-05:00] \"GET /images/hm_bg.jpg HTTP/1.0\" 200 24736"}
|
||||
|
||||
|
||||
---
|
||||
"get mapping":
|
||||
- do:
|
||||
indices.get_mapping:
|
||||
index: http_logs
|
||||
- match: {http_logs.mappings.properties.ip.type: ip }
|
||||
- match:
|
||||
http_logs.mappings.properties.ip.script.source: |
|
||||
Matcher m = /([^ ]+) .+/.matcher(doc["message"].value);
|
||||
if (m.matches()) {
|
||||
emit(m.group(1));
|
||||
}
|
||||
- match: {http_logs.mappings.properties.ip.script.lang: painless }
|
||||
|
||||
---
|
||||
"fetch fields":
|
||||
- do:
|
||||
search:
|
||||
index: http_logs
|
||||
body:
|
||||
sort: timestamp
|
||||
fields: [ip, ip_from_source, ip_many]
|
||||
- match: {hits.total.value: 6}
|
||||
- match: {hits.hits.0.fields.ip: ["40.135.0.0"] }
|
||||
- match: {hits.hits.0.fields.ip_from_source: ["40.135.0.0"] }
|
||||
- match:
|
||||
hits.hits.0.fields.ip_many:
|
||||
- 40.135.0.0
|
||||
- 40.135.0.1
|
||||
- 40.135.0.2
|
||||
- 40.135.0.3
|
||||
- 40.135.0.4
|
||||
|
||||
---
|
||||
"docvalue_fields":
|
||||
- do:
|
||||
search:
|
||||
index: http_logs
|
||||
body:
|
||||
sort: timestamp
|
||||
docvalue_fields: [ip, ip_from_source, ip_many]
|
||||
- match: {hits.total.value: 6}
|
||||
- match: {hits.hits.0.fields.ip: ["40.135.0.0"] }
|
||||
- match: {hits.hits.0.fields.ip_from_source: ["40.135.0.0"] }
|
||||
- match:
|
||||
hits.hits.0.fields.ip_many:
|
||||
- 40.135.0.0
|
||||
- 40.135.0.1
|
||||
- 40.135.0.2
|
||||
- 40.135.0.3
|
||||
- 40.135.0.4
|
||||
|
||||
---
|
||||
"terms agg":
|
||||
- do:
|
||||
search:
|
||||
index: http_logs
|
||||
body:
|
||||
aggs:
|
||||
ip:
|
||||
terms:
|
||||
field: ip
|
||||
- match: {hits.total.value: 6}
|
||||
- match: {aggregations.ip.buckets.0.key: 247.37.0.0}
|
||||
- match: {aggregations.ip.buckets.0.doc_count: 2}
|
||||
- match: {aggregations.ip.buckets.1.key: 26.1.0.0}
|
||||
- match: {aggregations.ip.buckets.1.doc_count: 1}
|
||||
|
||||
---
|
||||
"use in scripts":
|
||||
- do:
|
||||
search:
|
||||
index: http_logs
|
||||
body:
|
||||
aggs:
|
||||
ip:
|
||||
terms:
|
||||
script:
|
||||
String v = doc['ip'].value;
|
||||
return v.substring(0, v.indexOf('.'));
|
||||
- match: {hits.total.value: 6}
|
||||
- match: {aggregations.ip.buckets.0.key: '247'}
|
||||
- match: {aggregations.ip.buckets.0.doc_count: 2}
|
||||
- match: {aggregations.ip.buckets.1.key: '232'}
|
||||
- match: {aggregations.ip.buckets.1.doc_count: 1}
|
||||
|
||||
---
|
||||
"term query":
|
||||
- do:
|
||||
search:
|
||||
index: http_logs
|
||||
body:
|
||||
query:
|
||||
term:
|
||||
ip: 252.0.0.0
|
||||
- match: {hits.total.value: 1}
|
||||
- match: {hits.hits.0._source.timestamp: "1998-04-30T14:31:27-05:00"}
|
||||
|
|
@ -11,6 +11,7 @@ package org.elasticsearch.index.mapper;
|
|||
import org.apache.lucene.document.InetAddressPoint;
|
||||
import org.apache.lucene.document.SortedSetDocValuesField;
|
||||
import org.apache.lucene.document.StoredField;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.apache.lucene.index.SortedSetDocValues;
|
||||
import org.apache.lucene.search.MatchNoDocsQuery;
|
||||
import org.apache.lucene.search.Query;
|
||||
|
@ -26,8 +27,12 @@ import org.elasticsearch.index.fielddata.IndexFieldData;
|
|||
import org.elasticsearch.index.fielddata.ScriptDocValues;
|
||||
import org.elasticsearch.index.fielddata.plain.SortedSetOrdinalsIndexFieldData;
|
||||
import org.elasticsearch.index.query.SearchExecutionContext;
|
||||
import org.elasticsearch.script.IpFieldScript;
|
||||
import org.elasticsearch.script.Script;
|
||||
import org.elasticsearch.script.ScriptCompiler;
|
||||
import org.elasticsearch.search.DocValueFormat;
|
||||
import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
|
||||
import org.elasticsearch.search.lookup.FieldValues;
|
||||
import org.elasticsearch.search.lookup.SearchLookup;
|
||||
|
||||
import java.io.IOException;
|
||||
|
@ -38,6 +43,7 @@ import java.util.Collection;
|
|||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
|
@ -62,17 +68,31 @@ public class IpFieldMapper extends FieldMapper {
|
|||
private final Parameter<String> nullValue
|
||||
= Parameter.stringParam("null_value", false, m -> toType(m).nullValueAsString, null).acceptsNull();
|
||||
|
||||
private final Parameter<Script> script = Parameter.scriptParam(m -> toType(m).script);
|
||||
private final Parameter<String> onScriptError = Parameter.onScriptErrorParam(m -> toType(m).onScriptError, script);
|
||||
|
||||
private final Parameter<Map<String, String>> meta = Parameter.metaParam();
|
||||
|
||||
private final boolean ignoreMalformedByDefault;
|
||||
private final Version indexCreatedVersion;
|
||||
private final ScriptCompiler scriptCompiler;
|
||||
|
||||
public Builder(String name, boolean ignoreMalformedByDefault, Version indexCreatedVersion) {
|
||||
public Builder(String name, ScriptCompiler scriptCompiler, boolean ignoreMalformedByDefault, Version indexCreatedVersion) {
|
||||
super(name);
|
||||
this.scriptCompiler = Objects.requireNonNull(scriptCompiler);
|
||||
this.ignoreMalformedByDefault = ignoreMalformedByDefault;
|
||||
this.indexCreatedVersion = indexCreatedVersion;
|
||||
this.ignoreMalformed
|
||||
= Parameter.boolParam("ignore_malformed", true, m -> toType(m).ignoreMalformed, ignoreMalformedByDefault);
|
||||
this.script.precludesParameters(nullValue, ignoreMalformed);
|
||||
this.script.setValidator(s -> {
|
||||
if (s != null && indexed.get() == false && hasDocValues.get() == false) {
|
||||
throw new MapperParsingException("Cannot define script on field with index:false and doc_values:false");
|
||||
}
|
||||
if (s != null && multiFieldsBuilder.hasMultiFields()) {
|
||||
throw new MapperParsingException("Cannot define multifields on a field with a script");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
Builder nullValue(String nullValue) {
|
||||
|
@ -98,16 +118,27 @@ public class IpFieldMapper extends FieldMapper {
|
|||
}
|
||||
}
|
||||
|
||||
private FieldValues<InetAddress> scriptValues() {
|
||||
if (this.script.get() == null) {
|
||||
return null;
|
||||
}
|
||||
IpFieldScript.Factory factory = scriptCompiler.compile(this.script.get(), IpFieldScript.CONTEXT);
|
||||
return factory == null ? null : (lookup, ctx, doc, consumer) -> factory
|
||||
.newFactory(name, script.get().getParams(), lookup)
|
||||
.newInstance(ctx)
|
||||
.runForDoc(doc, consumer);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Parameter<?>> getParameters() {
|
||||
return List.of(indexed, hasDocValues, stored, ignoreMalformed, nullValue, meta);
|
||||
return List.of(indexed, hasDocValues, stored, ignoreMalformed, nullValue, script, onScriptError, meta);
|
||||
}
|
||||
|
||||
@Override
|
||||
public IpFieldMapper build(ContentPath contentPath) {
|
||||
return new IpFieldMapper(name,
|
||||
new IpFieldType(buildFullName(contentPath), indexed.getValue(), stored.getValue(),
|
||||
hasDocValues.getValue(), parseNullValue(), meta.getValue()),
|
||||
hasDocValues.getValue(), parseNullValue(), scriptValues(), meta.getValue()),
|
||||
multiFieldsBuilder.build(this, contentPath), copyTo.build(), this);
|
||||
}
|
||||
|
||||
|
@ -115,21 +146,23 @@ public class IpFieldMapper extends FieldMapper {
|
|||
|
||||
public static final TypeParser PARSER = new TypeParser((n, c) -> {
|
||||
boolean ignoreMalformedByDefault = IGNORE_MALFORMED_SETTING.get(c.getSettings());
|
||||
return new Builder(n, ignoreMalformedByDefault, c.indexVersionCreated());
|
||||
return new Builder(n, c.scriptCompiler(), ignoreMalformedByDefault, c.indexVersionCreated());
|
||||
});
|
||||
|
||||
public static final class IpFieldType extends SimpleMappedFieldType {
|
||||
|
||||
private final InetAddress nullValue;
|
||||
private final FieldValues<InetAddress> scriptValues;
|
||||
|
||||
public IpFieldType(String name, boolean indexed, boolean stored, boolean hasDocValues,
|
||||
InetAddress nullValue, Map<String, String> meta) {
|
||||
InetAddress nullValue, FieldValues<InetAddress> scriptValues, Map<String, String> meta) {
|
||||
super(name, indexed, stored, hasDocValues, TextSearchInfo.SIMPLE_MATCH_WITHOUT_TERMS, meta);
|
||||
this.nullValue = nullValue;
|
||||
this.scriptValues = scriptValues;
|
||||
}
|
||||
|
||||
public IpFieldType(String name) {
|
||||
this(name, true, false, true, null, Collections.emptyMap());
|
||||
this(name, true, false, true, null, null, Collections.emptyMap());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -153,6 +186,9 @@ public class IpFieldMapper extends FieldMapper {
|
|||
if (format != null) {
|
||||
throw new IllegalArgumentException("Field [" + name() + "] of type [" + typeName() + "] doesn't support formats.");
|
||||
}
|
||||
if (scriptValues != null) {
|
||||
return FieldValues.valueFetcher(scriptValues, v -> InetAddresses.toAddrString((InetAddress)v), context);
|
||||
}
|
||||
return new SourceValueFetcher(name(), context, nullValue) {
|
||||
@Override
|
||||
protected Object parseSourceValue(Object value) {
|
||||
|
@ -348,13 +384,17 @@ public class IpFieldMapper extends FieldMapper {
|
|||
private final boolean ignoreMalformedByDefault;
|
||||
private final Version indexCreatedVersion;
|
||||
|
||||
private final Script script;
|
||||
private final FieldValues<InetAddress> scriptValues;
|
||||
private final ScriptCompiler scriptCompiler;
|
||||
|
||||
private IpFieldMapper(
|
||||
String simpleName,
|
||||
MappedFieldType mappedFieldType,
|
||||
MultiFields multiFields,
|
||||
CopyTo copyTo,
|
||||
Builder builder) {
|
||||
super(simpleName, mappedFieldType, multiFields, copyTo);
|
||||
super(simpleName, mappedFieldType, multiFields, copyTo, builder.script.get() != null, builder.onScriptError.get());
|
||||
this.ignoreMalformedByDefault = builder.ignoreMalformedByDefault;
|
||||
this.indexed = builder.indexed.getValue();
|
||||
this.hasDocValues = builder.hasDocValues.getValue();
|
||||
|
@ -363,6 +403,9 @@ public class IpFieldMapper extends FieldMapper {
|
|||
this.nullValue = builder.parseNullValue();
|
||||
this.nullValueAsString = builder.nullValue.getValue();
|
||||
this.indexCreatedVersion = builder.indexCreatedVersion;
|
||||
this.script = builder.script.get();
|
||||
this.scriptValues = builder.scriptValues();
|
||||
this.scriptCompiler = builder.scriptCompiler;
|
||||
}
|
||||
|
||||
boolean ignoreMalformed() {
|
||||
|
@ -413,6 +456,10 @@ public class IpFieldMapper extends FieldMapper {
|
|||
}
|
||||
}
|
||||
|
||||
indexValue(context, address);
|
||||
}
|
||||
|
||||
private void indexValue(ParseContext context, InetAddress address) {
|
||||
if (indexed) {
|
||||
context.doc().add(new InetAddressPoint(fieldType().name(), address));
|
||||
}
|
||||
|
@ -426,9 +473,14 @@ public class IpFieldMapper extends FieldMapper {
|
|||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void indexScriptValues(SearchLookup searchLookup, LeafReaderContext readerContext, int doc, ParseContext parseContext) {
|
||||
this.scriptValues.valuesForDoc(searchLookup, readerContext, doc, value -> indexValue(parseContext, value));
|
||||
}
|
||||
|
||||
@Override
|
||||
public FieldMapper.Builder getMergeBuilder() {
|
||||
return new Builder(simpleName(), ignoreMalformedByDefault, indexCreatedVersion).init(this);
|
||||
return new Builder(simpleName(), scriptCompiler, ignoreMalformedByDefault, indexCreatedVersion).init(this);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -21,6 +21,7 @@ import java.net.Inet6Address;
|
|||
import java.net.InetAddress;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Script producing IP addresses. Unlike the other {@linkplain AbstractFieldScript}s
|
||||
|
@ -66,6 +67,13 @@ public abstract class IpFieldScript extends AbstractFieldScript {
|
|||
execute();
|
||||
}
|
||||
|
||||
public final void runForDoc(int docId, Consumer<InetAddress> consumer) {
|
||||
runForDoc(docId);
|
||||
for (int i = 0; i < count; i++) {
|
||||
consumer.accept(InetAddressPoint.decode(values[i].bytes));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Values from the last time {@link #runForDoc(int)} was called. This array
|
||||
* is mutable and will change with the next call of {@link #runForDoc(int)}.
|
||||
|
@ -97,7 +105,7 @@ public abstract class IpFieldScript extends AbstractFieldScript {
|
|||
return count;
|
||||
}
|
||||
|
||||
protected final void emit(String v) {
|
||||
public final void emit(String v) {
|
||||
checkMaxSize(count);
|
||||
if (values.length < count + 1) {
|
||||
values = ArrayUtil.grow(values, count + 1);
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.elasticsearch.index.query.SearchExecutionContext;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
|
||||
/**
|
||||
* Represents values for a given document
|
||||
|
@ -37,6 +38,17 @@ public interface FieldValues<T> {
|
|||
* @return the value fetcher
|
||||
*/
|
||||
static ValueFetcher valueFetcher(FieldValues<?> fieldValues, SearchExecutionContext context) {
|
||||
return valueFetcher(fieldValues, v -> v, context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a {@link ValueFetcher} that fetches values from a {@link FieldValues} instance
|
||||
* @param fieldValues the source of the values
|
||||
* @param formatter a function to format the values
|
||||
* @param context the search execution context
|
||||
* @return the value fetcher
|
||||
*/
|
||||
static ValueFetcher valueFetcher(FieldValues<?> fieldValues, Function<Object, Object> formatter, SearchExecutionContext context) {
|
||||
return new ValueFetcher() {
|
||||
LeafReaderContext ctx;
|
||||
|
||||
|
@ -49,7 +61,7 @@ public interface FieldValues<T> {
|
|||
public List<Object> fetchValues(SourceLookup lookup) {
|
||||
List<Object> values = new ArrayList<>();
|
||||
try {
|
||||
fieldValues.valuesForDoc(context.lookup(), ctx, lookup.docId(), values::add);
|
||||
fieldValues.valuesForDoc(context.lookup(), ctx, lookup.docId(), v -> values.add(formatter.apply(v)));
|
||||
} catch (Exception e) {
|
||||
// ignore errors - if they exist here then they existed at index time
|
||||
// and so on_script_error must have been set to `ignore`
|
||||
|
|
|
@ -25,6 +25,7 @@ import java.io.IOException;
|
|||
import java.net.InetAddress;
|
||||
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.equalTo;
|
||||
|
||||
public class IpFieldMapperTests extends MapperTestCase {
|
||||
|
||||
|
@ -213,4 +214,25 @@ public class IpFieldMapperTests extends MapperTestCase {
|
|||
protected boolean dedupAfterFetch() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public void testScriptAndPrecludedParameters() {
|
||||
{
|
||||
Exception e = expectThrows(MapperParsingException.class, () -> createDocumentMapper(fieldMapping(b -> {
|
||||
b.field("type", "ip");
|
||||
b.field("script", "test");
|
||||
b.field("null_value", 7);
|
||||
})));
|
||||
assertThat(e.getMessage(),
|
||||
equalTo("Failed to parse mapping: Field [null_value] cannot be set in conjunction with field [script]"));
|
||||
}
|
||||
{
|
||||
Exception e = expectThrows(MapperParsingException.class, () -> createDocumentMapper(fieldMapping(b -> {
|
||||
b.field("type", "ip");
|
||||
b.field("script", "test");
|
||||
b.field("ignore_malformed", "true");
|
||||
})));
|
||||
assertThat(e.getMessage(),
|
||||
equalTo("Failed to parse mapping: Field [ignore_malformed] cannot be set in conjunction with field [script]"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,7 @@ import org.apache.lucene.search.MatchNoDocsQuery;
|
|||
import org.apache.lucene.util.BytesRef;
|
||||
import org.elasticsearch.Version;
|
||||
import org.elasticsearch.common.network.InetAddresses;
|
||||
import org.elasticsearch.script.ScriptCompiler;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetAddress;
|
||||
|
@ -63,7 +64,7 @@ public class IpFieldTypeTests extends FieldTypeTestCase {
|
|||
prefix = ip + "/16";
|
||||
assertEquals(InetAddressPoint.newPrefixQuery("field", InetAddresses.forString(ip), 16), ft.termQuery(prefix, null));
|
||||
|
||||
MappedFieldType unsearchable = new IpFieldMapper.IpFieldType("field", false, false, true, null, Collections.emptyMap());
|
||||
MappedFieldType unsearchable = new IpFieldMapper.IpFieldType("field", false, false, true, null, null, Collections.emptyMap());
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> unsearchable.termQuery("::1", null));
|
||||
assertEquals("Cannot search on field [field] since it is not indexed.", e.getMessage());
|
||||
|
@ -166,7 +167,7 @@ public class IpFieldTypeTests extends FieldTypeTestCase {
|
|||
InetAddresses.forString("2001:db8::")),
|
||||
ft.rangeQuery("::ffff:c0a8:107", "2001:db8::", true, true, null, null, null, null));
|
||||
|
||||
MappedFieldType unsearchable = new IpFieldMapper.IpFieldType("field", false, false, true, null, Collections.emptyMap());
|
||||
MappedFieldType unsearchable = new IpFieldMapper.IpFieldType("field", false, false, true, null, null, Collections.emptyMap());
|
||||
IllegalArgumentException e = expectThrows(IllegalArgumentException.class,
|
||||
() -> unsearchable.rangeQuery("::1", "2001::", true, true, null, null, null, null));
|
||||
assertEquals("Cannot search on field [field] since it is not indexed.", e.getMessage());
|
||||
|
@ -174,12 +175,12 @@ public class IpFieldTypeTests extends FieldTypeTestCase {
|
|||
|
||||
public void testFetchSourceValue() throws IOException {
|
||||
MappedFieldType mapper
|
||||
= new IpFieldMapper.Builder("field", true, Version.CURRENT).build(new ContentPath()).fieldType();
|
||||
= new IpFieldMapper.Builder("field", ScriptCompiler.NONE, true, Version.CURRENT).build(new ContentPath()).fieldType();
|
||||
assertEquals(List.of("2001:db8::2:1"), fetchSourceValue(mapper, "2001:db8::2:1"));
|
||||
assertEquals(List.of("2001:db8::2:1"), fetchSourceValue(mapper, "2001:db8:0:0:0:0:2:1"));
|
||||
assertEquals(List.of("::1"), fetchSourceValue(mapper, "0:0:0:0:0:0:0:1"));
|
||||
|
||||
MappedFieldType nullValueMapper = new IpFieldMapper.Builder("field", true, Version.CURRENT)
|
||||
MappedFieldType nullValueMapper = new IpFieldMapper.Builder("field", ScriptCompiler.NONE, true, Version.CURRENT)
|
||||
.nullValue("2001:db8:0:0:0:0:2:7")
|
||||
.build(new ContentPath())
|
||||
.fieldType();
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0 and the Server Side Public License, v 1; you may not use this file except
|
||||
* in compliance with, at your election, the Elastic License 2.0 or the Server
|
||||
* Side Public License, v 1.
|
||||
*/
|
||||
|
||||
package org.elasticsearch.index.mapper;
|
||||
|
||||
import org.apache.lucene.index.IndexableField;
|
||||
import org.apache.lucene.index.LeafReaderContext;
|
||||
import org.elasticsearch.script.IpFieldScript;
|
||||
import org.elasticsearch.search.lookup.SearchLookup;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
public class IpScriptMapperTests extends MapperScriptTestCase<IpFieldScript.Factory> {
|
||||
|
||||
private static IpFieldScript.Factory factory(Consumer<IpFieldScript> executor) {
|
||||
return new IpFieldScript.Factory() {
|
||||
@Override
|
||||
public IpFieldScript.LeafFactory newFactory(String fieldName, Map<String, Object> params, SearchLookup searchLookup) {
|
||||
return new IpFieldScript.LeafFactory() {
|
||||
@Override
|
||||
public IpFieldScript newInstance(LeafReaderContext ctx) {
|
||||
return new IpFieldScript(fieldName, params, searchLookup, ctx) {
|
||||
@Override
|
||||
public void execute() {
|
||||
executor.accept(this);
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String type() {
|
||||
return "ip";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IpFieldScript.Factory serializableScript() {
|
||||
return factory(s -> {});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IpFieldScript.Factory errorThrowingScript() {
|
||||
return factory(s -> {
|
||||
throw new UnsupportedOperationException("Oops");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IpFieldScript.Factory singleValueScript() {
|
||||
return factory(s -> s.emit("::1"));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected IpFieldScript.Factory multipleValuesScript() {
|
||||
return factory(s -> {
|
||||
s.emit("::1");
|
||||
s.emit("::2");
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assertMultipleValues(IndexableField[] fields) {
|
||||
assertEquals(4, fields.length);
|
||||
assertEquals("InetAddressPoint <field:[0:0:0:0:0:0:0:1]>", fields[0].toString());
|
||||
assertEquals("docValuesType=SORTED_SET<field:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]>", fields[1].toString());
|
||||
assertEquals("InetAddressPoint <field:[0:0:0:0:0:0:0:2]>", fields[2].toString());
|
||||
assertEquals("docValuesType=SORTED_SET<field:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 2]>", fields[3].toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assertDocValuesDisabled(IndexableField[] fields) {
|
||||
assertEquals(1, fields.length);
|
||||
assertEquals("InetAddressPoint <field:[0:0:0:0:0:0:0:1]>", fields[0].toString());
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void assertIndexDisabled(IndexableField[] fields) {
|
||||
assertEquals(1, fields.length);
|
||||
assertEquals("docValuesType=SORTED_SET<field:[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 1]>", fields[0].toString());
|
||||
}
|
||||
}
|
|
@ -1080,7 +1080,8 @@ public class TermsAggregatorTests extends AggregatorTestCase {
|
|||
}
|
||||
|
||||
public void testIpField() throws Exception {
|
||||
MappedFieldType fieldType = new IpFieldMapper.IpFieldType("field", randomBoolean(), false, true, null, Collections.emptyMap());
|
||||
MappedFieldType fieldType
|
||||
= new IpFieldMapper.IpFieldType("field", randomBoolean(), false, true, null, null, Collections.emptyMap());
|
||||
testCase(new TermsAggregationBuilder("_name").field("field"), new MatchAllDocsQuery(), iw -> {
|
||||
Document document = new Document();
|
||||
InetAddress point = InetAddresses.forString("192.168.100.42");
|
||||
|
|
Loading…
Reference in New Issue