Quellcode durchsuchen

Make wildcard accessible from the scripting field API (#82763)

This adds the mapped typed wildcard to the scripting fields API.
Jack Conradson vor 3 Jahren
Ursprung
Commit
0a51d3239f

+ 5 - 0
docs/changelog/82763.yaml

@@ -0,0 +1,5 @@
+pr: 82763
+summary: Make wildcard accessible from the scripting field API
+area: Infra/Scripting
+type: enhancement
+issues: []

+ 9 - 4
server/src/main/java/org/elasticsearch/index/fielddata/plain/StringBinaryDVLeafFieldData.java

@@ -9,17 +9,22 @@
 package org.elasticsearch.index.fielddata.plain;
 
 import org.apache.lucene.index.BinaryDocValues;
-import org.elasticsearch.index.fielddata.ScriptDocValues;
-import org.elasticsearch.script.field.DelegateDocValuesField;
+import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.script.field.DocValuesField;
+import org.elasticsearch.script.field.ToScriptField;
 
 final class StringBinaryDVLeafFieldData extends AbstractBinaryDVLeafFieldData {
-    StringBinaryDVLeafFieldData(BinaryDocValues values) {
+
+    protected final ToScriptField<SortedBinaryDocValues> toScriptField;
+
+    StringBinaryDVLeafFieldData(BinaryDocValues values, ToScriptField<SortedBinaryDocValues> toScriptField) {
         super(values);
+
+        this.toScriptField = toScriptField;
     }
 
     @Override
     public DocValuesField<?> getScriptField(String name) {
-        return new DelegateDocValuesField(new ScriptDocValues.Strings(new ScriptDocValues.StringsSupplier(getBytesValues())), name);
+        return toScriptField.getScriptField(getBytesValues(), name);
     }
 }

+ 10 - 2
server/src/main/java/org/elasticsearch/index/fielddata/plain/StringBinaryIndexFieldData.java

@@ -14,7 +14,9 @@ import org.apache.lucene.search.SortField;
 import org.elasticsearch.common.util.BigArrays;
 import org.elasticsearch.index.fielddata.IndexFieldData;
 import org.elasticsearch.index.fielddata.IndexFieldData.XFieldComparatorSource.Nested;
+import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
 import org.elasticsearch.index.fielddata.fieldcomparator.BytesRefFieldComparatorSource;
+import org.elasticsearch.script.field.ToScriptField;
 import org.elasticsearch.search.DocValueFormat;
 import org.elasticsearch.search.MultiValueMode;
 import org.elasticsearch.search.aggregations.support.ValuesSourceType;
@@ -27,10 +29,16 @@ public class StringBinaryIndexFieldData implements IndexFieldData<StringBinaryDV
 
     protected final String fieldName;
     protected final ValuesSourceType valuesSourceType;
+    protected final ToScriptField<SortedBinaryDocValues> toScriptField;
 
-    public StringBinaryIndexFieldData(String fieldName, ValuesSourceType valuesSourceType) {
+    public StringBinaryIndexFieldData(
+        String fieldName,
+        ValuesSourceType valuesSourceType,
+        ToScriptField<SortedBinaryDocValues> toScriptField
+    ) {
         this.fieldName = fieldName;
         this.valuesSourceType = valuesSourceType;
+        this.toScriptField = toScriptField;
     }
 
     @Override
@@ -52,7 +60,7 @@ public class StringBinaryIndexFieldData implements IndexFieldData<StringBinaryDV
     @Override
     public StringBinaryDVLeafFieldData load(LeafReaderContext context) {
         try {
-            return new StringBinaryDVLeafFieldData(DocValues.getBinary(context.reader(), fieldName));
+            return new StringBinaryDVLeafFieldData(DocValues.getBinary(context.reader(), fieldName), toScriptField);
         } catch (IOException e) {
             throw new IllegalStateException("Cannot load doc values", e);
         }

+ 3 - 1
x-pack/plugin/wildcard/build.gradle

@@ -1,16 +1,18 @@
 import org.elasticsearch.gradle.internal.info.BuildParams
 
 apply plugin: 'elasticsearch.internal-es-plugin'
+apply plugin: 'elasticsearch.internal-yaml-rest-test'
 
 esplugin {
   name 'wildcard'
   description 'A plugin for a keyword field type with efficient wildcard search'
   classname 'org.elasticsearch.xpack.wildcard.Wildcard'
-  extendedPlugins = ['x-pack-core']
+  extendedPlugins = ['x-pack-core', 'lang-painless']
 }
 archivesBaseName = 'x-pack-wildcard'
 
 dependencies {
+  compileOnly project(':modules:lang-painless:spi')
   compileOnly project(path: xpackModule('core'))
   testImplementation(testArtifact(project(xpackModule('core'))))
 }

+ 18 - 0
x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/WildcardDocValuesField.java

@@ -0,0 +1,18 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.wildcard;
+
+import org.elasticsearch.index.fielddata.SortedBinaryDocValues;
+import org.elasticsearch.script.field.AbstractKeywordDocValuesField;
+
+public class WildcardDocValuesField extends AbstractKeywordDocValuesField {
+
+    public WildcardDocValuesField(SortedBinaryDocValues input, String name) {
+        super(input, name);
+    }
+}

+ 37 - 0
x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/WildcardPainlessExtension.java

@@ -0,0 +1,37 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.wildcard;
+
+import org.elasticsearch.painless.spi.PainlessExtension;
+import org.elasticsearch.painless.spi.Whitelist;
+import org.elasticsearch.painless.spi.WhitelistLoader;
+import org.elasticsearch.script.ScriptContext;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import static java.util.Collections.singletonList;
+import static org.elasticsearch.script.ScriptModule.CORE_CONTEXTS;
+
+public class WildcardPainlessExtension implements PainlessExtension {
+    private static final Whitelist WHITELIST = WhitelistLoader.loadFromResourceFiles(
+        WildcardPainlessExtension.class,
+        "org.elasticsearch.xpack.wildcard.txt"
+    );
+
+    @Override
+    public Map<ScriptContext<?>, List<Whitelist>> getContextWhitelists() {
+        List<Whitelist> whitelist = singletonList(WHITELIST);
+        Map<ScriptContext<?>, List<Whitelist>> contextWhitelists = new HashMap<>(CORE_CONTEXTS.size());
+        for (ScriptContext<?> scriptContext : CORE_CONTEXTS.values()) {
+            contextWhitelists.put(scriptContext, whitelist);
+        }
+        return contextWhitelists;
+    }
+}

+ 6 - 1
x-pack/plugin/wildcard/src/main/java/org/elasticsearch/xpack/wildcard/mapper/WildcardFieldMapper.java

@@ -67,6 +67,7 @@ import org.elasticsearch.index.query.SearchExecutionContext;
 import org.elasticsearch.search.aggregations.support.CoreValuesSourceType;
 import org.elasticsearch.search.lookup.SearchLookup;
 import org.elasticsearch.xcontent.XContentParser;
+import org.elasticsearch.xpack.wildcard.WildcardDocValuesField;
 
 import java.io.IOException;
 import java.nio.charset.StandardCharsets;
@@ -834,7 +835,11 @@ public class WildcardFieldMapper extends FieldMapper {
         @Override
         public IndexFieldData.Builder fielddataBuilder(String fullyQualifiedIndexName, Supplier<SearchLookup> searchLookup) {
             failIfNoDocValues();
-            return (cache, breakerService) -> new StringBinaryIndexFieldData(name(), CoreValuesSourceType.KEYWORD);
+            return (cache, breakerService) -> new StringBinaryIndexFieldData(
+                name(),
+                CoreValuesSourceType.KEYWORD,
+                WildcardDocValuesField::new
+            );
         }
 
         @Override

+ 1 - 0
x-pack/plugin/wildcard/src/main/resources/META-INF/services/org.elasticsearch.painless.spi.PainlessExtension

@@ -0,0 +1 @@
+org.elasticsearch.xpack.wildcard.WildcardPainlessExtension

+ 10 - 0
x-pack/plugin/wildcard/src/main/resources/org/elasticsearch/xpack/wildcard/org.elasticsearch.xpack.wildcard.txt

@@ -0,0 +1,10 @@
+#
+# 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.
+#
+
+class org.elasticsearch.xpack.wildcard.WildcardDocValuesField @dynamic_type {
+}

+ 27 - 0
x-pack/plugin/wildcard/src/yamlRestTest/java/org/elasticsearch/xpack/wildcard/WildcardClientYamlTestSuiteIT.java

@@ -0,0 +1,27 @@
+/*
+ * 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; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+package org.elasticsearch.xpack.wildcard;
+
+import com.carrotsearch.randomizedtesting.annotations.Name;
+import com.carrotsearch.randomizedtesting.annotations.ParametersFactory;
+
+import org.elasticsearch.test.rest.yaml.ClientYamlTestCandidate;
+import org.elasticsearch.test.rest.yaml.ESClientYamlSuiteTestCase;
+
+/** Runs yaml rest tests */
+public class WildcardClientYamlTestSuiteIT extends ESClientYamlSuiteTestCase {
+
+    public WildcardClientYamlTestSuiteIT(@Name("yaml") ClientYamlTestCandidate testCandidate) {
+        super(testCandidate);
+    }
+
+    @ParametersFactory
+    public static Iterable<Object[]> parameters() throws Exception {
+        return ESClientYamlSuiteTestCase.createParameters();
+    }
+}

+ 49 - 0
x-pack/plugin/wildcard/src/yamlRestTest/resources/rest-api-spec/test/10_script_values.yml

@@ -0,0 +1,49 @@
+setup:
+  - do:
+      indices.create:
+        index:  test
+        body:
+          settings:
+            number_of_shards: 1
+          mappings:
+            properties:
+              test_wc:
+                type: wildcard
+
+  - do:
+      bulk:
+        index: test
+        refresh: true
+        body: |
+          { "index": {"_id" : "1"} }
+          { "test_wc": "abc" }
+          { "index": {"_id" : "2"} }
+          { "test_wc": ["adc", "aec"] }
+
+---
+"Wildcard Fields API":
+  - do:
+      search:
+        index: test
+        body:
+          query:
+            wildcard:
+              test_wc:
+                value: "a*c"
+          script_fields:
+            constOne:
+              script:
+                source: "/* avoid stash */ $('test_wc', 'dne')"
+            constTwo:
+              script:
+                source: "field('test_wc').get(0, 'dne') + field('test_wc').get(1, 'xyz')"
+            constThree:
+              script:
+                source: "String s = ''; for (String z : field('test_wc')) s += z; return s"
+
+  - match: { hits.hits.0.fields.constOne.0: "abc" }
+  - match: { hits.hits.0.fields.constTwo.0: "abcxyz" }
+  - match: { hits.hits.0.fields.constThree.0: "abc" }
+  - match: { hits.hits.1.fields.constOne.0: "adc" }
+  - match: { hits.hits.1.fields.constTwo.0: "adcaec" }
+  - match: { hits.hits.1.fields.constThree.0: "adcaec" }