Skip to content

Commit 7947fd3

Browse files
authored
Add tests around accessing field aliases in scripts. (#31417)
* Make sure aliases can be referenced through params._fields. * Make sure aliases can be accessed through 'doc'. * Convert ExpressionTests to a unit test.
1 parent b62c73a commit 7947fd3

File tree

4 files changed

+222
-14
lines changed

4 files changed

+222
-14
lines changed

modules/lang-expression/src/test/java/org/elasticsearch/script/expression/ExpressionTests.java

Lines changed: 54 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,27 +20,52 @@
2020
package org.elasticsearch.script.expression;
2121

2222
import org.elasticsearch.common.settings.Settings;
23-
import org.elasticsearch.index.IndexService;
24-
import org.elasticsearch.index.query.QueryShardContext;
23+
import org.elasticsearch.index.fielddata.AtomicNumericFieldData;
24+
import org.elasticsearch.index.fielddata.IndexNumericFieldData;
25+
import org.elasticsearch.index.fielddata.SortedNumericDoubleValues;
26+
import org.elasticsearch.index.mapper.MapperService;
27+
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberFieldType;
28+
import org.elasticsearch.index.mapper.NumberFieldMapper.NumberType;
2529
import org.elasticsearch.script.ScriptException;
2630
import org.elasticsearch.script.SearchScript;
2731
import org.elasticsearch.search.lookup.SearchLookup;
28-
import org.elasticsearch.test.ESSingleNodeTestCase;
32+
import org.elasticsearch.test.ESTestCase;
2933

34+
import java.io.IOException;
3035
import java.text.ParseException;
3136
import java.util.Collections;
3237

33-
public class ExpressionTests extends ESSingleNodeTestCase {
34-
ExpressionScriptEngine service;
35-
SearchLookup lookup;
38+
import static org.mockito.Matchers.anyInt;
39+
import static org.mockito.Matchers.anyObject;
40+
import static org.mockito.Mockito.mock;
41+
import static org.mockito.Mockito.when;
42+
43+
public class ExpressionTests extends ESTestCase {
44+
private ExpressionScriptEngine service;
45+
private SearchLookup lookup;
3646

3747
@Override
3848
public void setUp() throws Exception {
3949
super.setUp();
40-
IndexService index = createIndex("test", Settings.EMPTY, "type", "d", "type=double");
50+
51+
NumberFieldType fieldType = new NumberFieldType(NumberType.DOUBLE);
52+
MapperService mapperService = mock(MapperService.class);
53+
when(mapperService.fullName("field")).thenReturn(fieldType);
54+
when(mapperService.fullName("alias")).thenReturn(fieldType);
55+
56+
SortedNumericDoubleValues doubleValues = mock(SortedNumericDoubleValues.class);
57+
when(doubleValues.advanceExact(anyInt())).thenReturn(true);
58+
when(doubleValues.nextValue()).thenReturn(2.718);
59+
60+
AtomicNumericFieldData atomicFieldData = mock(AtomicNumericFieldData.class);
61+
when(atomicFieldData.getDoubleValues()).thenReturn(doubleValues);
62+
63+
IndexNumericFieldData fieldData = mock(IndexNumericFieldData.class);
64+
when(fieldData.getFieldName()).thenReturn("field");
65+
when(fieldData.load(anyObject())).thenReturn(atomicFieldData);
66+
4167
service = new ExpressionScriptEngine(Settings.EMPTY);
42-
QueryShardContext shardContext = index.newQueryShardContext(0, null, () -> 0, null);
43-
lookup = new SearchLookup(index.mapperService(), shardContext::getForField, null);
68+
lookup = new SearchLookup(mapperService, ignored -> fieldData, null);
4469
}
4570

4671
private SearchScript.LeafFactory compile(String expression) {
@@ -50,22 +75,38 @@ private SearchScript.LeafFactory compile(String expression) {
5075

5176
public void testNeedsScores() {
5277
assertFalse(compile("1.2").needs_score());
53-
assertFalse(compile("doc['d'].value").needs_score());
78+
assertFalse(compile("doc['field'].value").needs_score());
5479
assertTrue(compile("1/_score").needs_score());
55-
assertTrue(compile("doc['d'].value * _score").needs_score());
80+
assertTrue(compile("doc['field'].value * _score").needs_score());
5681
}
5782

5883
public void testCompileError() {
5984
ScriptException e = expectThrows(ScriptException.class, () -> {
60-
compile("doc['d'].value * *@#)(@$*@#$ + 4");
85+
compile("doc['field'].value * *@#)(@$*@#$ + 4");
6186
});
6287
assertTrue(e.getCause() instanceof ParseException);
6388
}
6489

6590
public void testLinkError() {
6691
ScriptException e = expectThrows(ScriptException.class, () -> {
67-
compile("doc['e'].value * 5");
92+
compile("doc['nonexistent'].value * 5");
6893
});
6994
assertTrue(e.getCause() instanceof ParseException);
7095
}
96+
97+
public void testFieldAccess() throws IOException {
98+
SearchScript script = compile("doc['field'].value").newInstance(null);
99+
script.setDocument(1);
100+
101+
double result = script.runAsDouble();
102+
assertEquals(2.718, result, 0.0);
103+
}
104+
105+
public void testFieldAccessWithFieldAlias() throws IOException {
106+
SearchScript script = compile("doc['alias'].value").newInstance(null);
107+
script.setDocument(1);
108+
109+
double result = script.runAsDouble();
110+
assertEquals(2.718, result, 0.0);
111+
}
71112
}

server/src/main/java/org/elasticsearch/search/lookup/LeafFieldsLookup.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ private FieldLookup loadFieldData(String name) {
148148
reader.document(docId, fieldVisitor);
149149
fieldVisitor.postProcess(mapperService);
150150
List<Object> storedFields = fieldVisitor.fields().get(data.fieldType().name());
151-
data.fields(singletonMap(name, storedFields));
151+
data.fields(singletonMap(fieldName, storedFields));
152152
} catch (IOException e) {
153153
throw new ElasticsearchParseException("failed to load field [{}]", e, name);
154154
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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.search.lookup;
20+
21+
import org.elasticsearch.index.fielddata.AtomicFieldData;
22+
import org.elasticsearch.index.fielddata.IndexFieldData;
23+
import org.elasticsearch.index.fielddata.ScriptDocValues;
24+
import org.elasticsearch.index.mapper.MappedFieldType;
25+
import org.elasticsearch.index.mapper.MapperService;
26+
import org.elasticsearch.test.ESTestCase;
27+
import org.junit.Before;
28+
29+
import static org.mockito.AdditionalAnswers.returnsFirstArg;
30+
import static org.mockito.Matchers.anyObject;
31+
import static org.mockito.Mockito.doReturn;
32+
import static org.mockito.Mockito.mock;
33+
import static org.mockito.Mockito.when;
34+
35+
public class LeafDocLookupTests extends ESTestCase {
36+
private ScriptDocValues<?> docValues;
37+
private LeafDocLookup docLookup;
38+
39+
@Before
40+
public void setUp() throws Exception {
41+
super.setUp();
42+
43+
MappedFieldType fieldType = mock(MappedFieldType.class);
44+
when(fieldType.name()).thenReturn("field");
45+
when(fieldType.valueForDisplay(anyObject())).then(returnsFirstArg());
46+
47+
MapperService mapperService = mock(MapperService.class);
48+
when(mapperService.fullName("field")).thenReturn(fieldType);
49+
when(mapperService.fullName("alias")).thenReturn(fieldType);
50+
51+
docValues = mock(ScriptDocValues.class);
52+
53+
AtomicFieldData atomicFieldData = mock(AtomicFieldData.class);
54+
doReturn(docValues).when(atomicFieldData).getScriptValues();
55+
56+
IndexFieldData<?> fieldData = mock(IndexFieldData.class);
57+
when(fieldData.getFieldName()).thenReturn("field");
58+
doReturn(atomicFieldData).when(fieldData).load(anyObject());
59+
60+
docLookup = new LeafDocLookup(mapperService,
61+
ignored -> fieldData,
62+
new String[] { "type" },
63+
null);
64+
}
65+
66+
public void testBasicLookup() {
67+
ScriptDocValues<?> fetchedDocValues = docLookup.get("field");
68+
assertEquals(docValues, fetchedDocValues);
69+
}
70+
71+
public void testLookupWithFieldAlias() {
72+
ScriptDocValues<?> fetchedDocValues = docLookup.get("alias");
73+
assertEquals(docValues, fetchedDocValues);
74+
}
75+
}
Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
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.search.lookup;
20+
21+
import org.apache.lucene.index.DocValuesType;
22+
import org.apache.lucene.index.FieldInfo;
23+
import org.apache.lucene.index.IndexOptions;
24+
import org.apache.lucene.index.LeafReader;
25+
import org.apache.lucene.index.StoredFieldVisitor;
26+
import org.elasticsearch.index.mapper.MappedFieldType;
27+
import org.elasticsearch.index.mapper.MapperService;
28+
import org.elasticsearch.test.ESTestCase;
29+
import org.junit.Before;
30+
31+
import java.util.Collections;
32+
import java.util.List;
33+
34+
import static org.mockito.AdditionalAnswers.returnsFirstArg;
35+
import static org.mockito.Matchers.any;
36+
import static org.mockito.Matchers.anyInt;
37+
import static org.mockito.Matchers.anyObject;
38+
import static org.mockito.Mockito.doAnswer;
39+
import static org.mockito.Mockito.mock;
40+
import static org.mockito.Mockito.when;
41+
42+
public class LeafFieldsLookupTests extends ESTestCase {
43+
private LeafFieldsLookup fieldsLookup;
44+
45+
@Before
46+
public void setUp() throws Exception {
47+
super.setUp();
48+
49+
MappedFieldType fieldType = mock(MappedFieldType.class);
50+
when(fieldType.name()).thenReturn("field");
51+
when(fieldType.valueForDisplay(anyObject())).then(returnsFirstArg());
52+
53+
MapperService mapperService = mock(MapperService.class);
54+
when(mapperService.fullName("field")).thenReturn(fieldType);
55+
when(mapperService.fullName("alias")).thenReturn(fieldType);
56+
57+
FieldInfo mockFieldInfo = new FieldInfo("field", 1, false, false, true,
58+
IndexOptions.NONE, DocValuesType.NONE, -1, Collections.emptyMap(), 0, 0, false);
59+
60+
LeafReader leafReader = mock(LeafReader.class);
61+
doAnswer(invocation -> {
62+
Object[] args = invocation.getArguments();
63+
StoredFieldVisitor visitor = (StoredFieldVisitor) args[1];
64+
visitor.doubleField(mockFieldInfo, 2.718);
65+
return null;
66+
}).when(leafReader).document(anyInt(), any(StoredFieldVisitor.class));
67+
68+
fieldsLookup = new LeafFieldsLookup(mapperService,
69+
new String[] { "type" },
70+
leafReader);
71+
}
72+
73+
public void testBasicLookup() {
74+
FieldLookup fieldLookup = (FieldLookup) fieldsLookup.get("field");
75+
assertEquals("field", fieldLookup.fieldType().name());
76+
77+
List<Object> values = fieldLookup.getValues();
78+
assertNotNull(values);
79+
assertEquals(1, values.size());
80+
assertEquals(2.718, values.get(0));
81+
}
82+
83+
public void testLookupWithFieldAlias() {
84+
FieldLookup fieldLookup = (FieldLookup) fieldsLookup.get("alias");
85+
assertEquals("field", fieldLookup.fieldType().name());
86+
87+
List<Object> values = fieldLookup.getValues();
88+
assertNotNull(values);
89+
assertEquals(1, values.size());
90+
assertEquals(2.718, values.get(0));
91+
}
92+
}

0 commit comments

Comments
 (0)