diff --git a/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/ExpressionTests.java b/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/ExpressionTests.java index 591cc9ce47737..33e6239002eb1 100644 --- a/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/ExpressionTests.java +++ b/modules/lang-expression/src/test/java/org/elasticsearch/script/expression/ExpressionTests.java @@ -20,27 +20,52 @@ package org.elasticsearch.script.expression; import org.elasticsearch.common.settings.Settings; -import org.elasticsearch.index.IndexService; -import org.elasticsearch.index.query.QueryShardContext; +import org.elasticsearch.index.fielddata.AtomicNumericFieldData; +import org.elasticsearch.index.fielddata.IndexNumericFieldData; +import org.elasticsearch.index.fielddata.SortedNumericDoubleValues; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.index.mapper.NumberFieldMapper.NumberFieldType; +import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType; import org.elasticsearch.script.ScriptException; import org.elasticsearch.script.SearchScript; import org.elasticsearch.search.lookup.SearchLookup; -import org.elasticsearch.test.ESSingleNodeTestCase; +import org.elasticsearch.test.ESTestCase; +import java.io.IOException; import java.text.ParseException; import java.util.Collections; -public class ExpressionTests extends ESSingleNodeTestCase { - ExpressionScriptEngine service; - SearchLookup lookup; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class ExpressionTests extends ESTestCase { + private ExpressionScriptEngine service; + private SearchLookup lookup; @Override public void setUp() throws Exception { super.setUp(); - IndexService index = createIndex("test", Settings.EMPTY, "type", "d", "type=double"); + + NumberFieldType fieldType = new NumberFieldType(NumberType.DOUBLE); + MapperService mapperService = mock(MapperService.class); + when(mapperService.fullName("field")).thenReturn(fieldType); + when(mapperService.fullName("alias")).thenReturn(fieldType); + + SortedNumericDoubleValues doubleValues = mock(SortedNumericDoubleValues.class); + when(doubleValues.advanceExact(anyInt())).thenReturn(true); + when(doubleValues.nextValue()).thenReturn(2.718); + + AtomicNumericFieldData atomicFieldData = mock(AtomicNumericFieldData.class); + when(atomicFieldData.getDoubleValues()).thenReturn(doubleValues); + + IndexNumericFieldData fieldData = mock(IndexNumericFieldData.class); + when(fieldData.getFieldName()).thenReturn("field"); + when(fieldData.load(anyObject())).thenReturn(atomicFieldData); + service = new ExpressionScriptEngine(Settings.EMPTY); - QueryShardContext shardContext = index.newQueryShardContext(0, null, () -> 0, null); - lookup = new SearchLookup(index.mapperService(), shardContext::getForField, null); + lookup = new SearchLookup(mapperService, ignored -> fieldData, null); } private SearchScript.LeafFactory compile(String expression) { @@ -50,22 +75,38 @@ private SearchScript.LeafFactory compile(String expression) { public void testNeedsScores() { assertFalse(compile("1.2").needs_score()); - assertFalse(compile("doc['d'].value").needs_score()); + assertFalse(compile("doc['field'].value").needs_score()); assertTrue(compile("1/_score").needs_score()); - assertTrue(compile("doc['d'].value * _score").needs_score()); + assertTrue(compile("doc['field'].value * _score").needs_score()); } public void testCompileError() { ScriptException e = expectThrows(ScriptException.class, () -> { - compile("doc['d'].value * *@#)(@$*@#$ + 4"); + compile("doc['field'].value * *@#)(@$*@#$ + 4"); }); assertTrue(e.getCause() instanceof ParseException); } public void testLinkError() { ScriptException e = expectThrows(ScriptException.class, () -> { - compile("doc['e'].value * 5"); + compile("doc['nonexistent'].value * 5"); }); assertTrue(e.getCause() instanceof ParseException); } + + public void testFieldAccess() throws IOException { + SearchScript script = compile("doc['field'].value").newInstance(null); + script.setDocument(1); + + double result = script.runAsDouble(); + assertEquals(2.718, result, 0.0); + } + + public void testFieldAccessWithFieldAlias() throws IOException { + SearchScript script = compile("doc['alias'].value").newInstance(null); + script.setDocument(1); + + double result = script.runAsDouble(); + assertEquals(2.718, result, 0.0); + } } diff --git a/server/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java b/server/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java index 2e04443f9e526..9ec20001adc8a 100644 --- a/server/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java +++ b/server/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java @@ -148,7 +148,7 @@ private FieldLookup loadFieldData(String name) { reader.document(docId, fieldVisitor); fieldVisitor.postProcess(mapperService); List storedFields = fieldVisitor.fields().get(data.fieldType().name()); - data.fields(singletonMap(name, storedFields)); + data.fields(singletonMap(fieldName, storedFields)); } catch (IOException e) { throw new ElasticsearchParseException("failed to load field [{}]", e, name); } diff --git a/server/src/test/java/org/elasticsearch/search/lookup/LeafDocLookupTests.java b/server/src/test/java/org/elasticsearch/search/lookup/LeafDocLookupTests.java new file mode 100644 index 0000000000000..dfdbef1c3d539 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/search/lookup/LeafDocLookupTests.java @@ -0,0 +1,75 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.search.lookup; + +import org.elasticsearch.index.fielddata.AtomicFieldData; +import org.elasticsearch.index.fielddata.IndexFieldData; +import org.elasticsearch.index.fielddata.ScriptDocValues; +import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.test.ESTestCase; +import org.junit.Before; + +import static org.mockito.AdditionalAnswers.returnsFirstArg; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.doReturn; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class LeafDocLookupTests extends ESTestCase { + private ScriptDocValues docValues; + private LeafDocLookup docLookup; + + @Before + public void setUp() throws Exception { + super.setUp(); + + MappedFieldType fieldType = mock(MappedFieldType.class); + when(fieldType.name()).thenReturn("field"); + when(fieldType.valueForDisplay(anyObject())).then(returnsFirstArg()); + + MapperService mapperService = mock(MapperService.class); + when(mapperService.fullName("field")).thenReturn(fieldType); + when(mapperService.fullName("alias")).thenReturn(fieldType); + + docValues = mock(ScriptDocValues.class); + + AtomicFieldData atomicFieldData = mock(AtomicFieldData.class); + doReturn(docValues).when(atomicFieldData).getScriptValues(); + + IndexFieldData fieldData = mock(IndexFieldData.class); + when(fieldData.getFieldName()).thenReturn("field"); + doReturn(atomicFieldData).when(fieldData).load(anyObject()); + + docLookup = new LeafDocLookup(mapperService, + ignored -> fieldData, + new String[] { "type" }, + null); + } + + public void testBasicLookup() { + ScriptDocValues fetchedDocValues = docLookup.get("field"); + assertEquals(docValues, fetchedDocValues); + } + + public void testLookupWithFieldAlias() { + ScriptDocValues fetchedDocValues = docLookup.get("alias"); + assertEquals(docValues, fetchedDocValues); + } +} diff --git a/server/src/test/java/org/elasticsearch/search/lookup/LeafFieldsLookupTests.java b/server/src/test/java/org/elasticsearch/search/lookup/LeafFieldsLookupTests.java new file mode 100644 index 0000000000000..1195893a28af0 --- /dev/null +++ b/server/src/test/java/org/elasticsearch/search/lookup/LeafFieldsLookupTests.java @@ -0,0 +1,92 @@ +/* + * Licensed to Elasticsearch under one or more contributor + * license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright + * ownership. Elasticsearch licenses this file to you under + * the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.elasticsearch.search.lookup; + +import org.apache.lucene.index.DocValuesType; +import org.apache.lucene.index.FieldInfo; +import org.apache.lucene.index.IndexOptions; +import org.apache.lucene.index.LeafReader; +import org.apache.lucene.index.StoredFieldVisitor; +import org.elasticsearch.index.mapper.MappedFieldType; +import org.elasticsearch.index.mapper.MapperService; +import org.elasticsearch.test.ESTestCase; +import org.junit.Before; + +import java.util.Collections; +import java.util.List; + +import static org.mockito.AdditionalAnswers.returnsFirstArg; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.anyInt; +import static org.mockito.Matchers.anyObject; +import static org.mockito.Mockito.doAnswer; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +public class LeafFieldsLookupTests extends ESTestCase { + private LeafFieldsLookup fieldsLookup; + + @Before + public void setUp() throws Exception { + super.setUp(); + + MappedFieldType fieldType = mock(MappedFieldType.class); + when(fieldType.name()).thenReturn("field"); + when(fieldType.valueForDisplay(anyObject())).then(returnsFirstArg()); + + MapperService mapperService = mock(MapperService.class); + when(mapperService.fullName("field")).thenReturn(fieldType); + when(mapperService.fullName("alias")).thenReturn(fieldType); + + FieldInfo mockFieldInfo = new FieldInfo("field", 1, false, false, true, + IndexOptions.NONE, DocValuesType.NONE, -1, Collections.emptyMap(), 0, 0, false); + + LeafReader leafReader = mock(LeafReader.class); + doAnswer(invocation -> { + Object[] args = invocation.getArguments(); + StoredFieldVisitor visitor = (StoredFieldVisitor) args[1]; + visitor.doubleField(mockFieldInfo, 2.718); + return null; + }).when(leafReader).document(anyInt(), any(StoredFieldVisitor.class)); + + fieldsLookup = new LeafFieldsLookup(mapperService, + new String[] { "type" }, + leafReader); + } + + public void testBasicLookup() { + FieldLookup fieldLookup = (FieldLookup) fieldsLookup.get("field"); + assertEquals("field", fieldLookup.fieldType().name()); + + List values = fieldLookup.getValues(); + assertNotNull(values); + assertEquals(1, values.size()); + assertEquals(2.718, values.get(0)); + } + + public void testLookupWithFieldAlias() { + FieldLookup fieldLookup = (FieldLookup) fieldsLookup.get("alias"); + assertEquals("field", fieldLookup.fieldType().name()); + + List values = fieldLookup.getValues(); + assertNotNull(values); + assertEquals(1, values.size()); + assertEquals(2.718, values.get(0)); + } +}