forked from elastic/elasticsearch
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
SCRIPTING: Add Expr. Compile for TermSetQuery Ctx.
* Follow up to elastic#33602 adding the ability to compile TermsSetQuery scripts with the expressions engine in the same way we support SearchScript in Expressions * Duplicated the code here for now to make the change less complex, the only difference to SearchScript is that `_score` and `_value` are not handled for TermsSetQuery
- Loading branch information
1 parent
d9947c6
commit 20a66f9
Showing
4 changed files
with
308 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
77 changes: 77 additions & 0 deletions
77
...ssion/src/main/java/org/elasticsearch/script/expression/ExpressionTermSetQueryScript.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
/* | ||
* 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.script.expression; | ||
|
||
import java.io.IOException; | ||
import java.util.Collections; | ||
import org.apache.lucene.expressions.Bindings; | ||
import org.apache.lucene.expressions.Expression; | ||
import org.apache.lucene.expressions.SimpleBindings; | ||
import org.apache.lucene.index.LeafReaderContext; | ||
import org.apache.lucene.search.DoubleValues; | ||
import org.apache.lucene.search.DoubleValuesSource; | ||
import org.elasticsearch.script.GeneralScriptException; | ||
import org.elasticsearch.script.TermsSetQueryScript; | ||
|
||
/** | ||
* A bridge to evaluate an {@link Expression} against {@link Bindings} in the context | ||
* of a {@link TermsSetQueryScript}. | ||
*/ | ||
class ExpressionTermSetQueryScript implements TermsSetQueryScript.LeafFactory { | ||
|
||
final Expression exprScript; | ||
final SimpleBindings bindings; | ||
final DoubleValuesSource source; | ||
final ReplaceableConstDoubleValueSource specialValue; // _value | ||
|
||
ExpressionTermSetQueryScript(Expression e, SimpleBindings b, ReplaceableConstDoubleValueSource v) { | ||
exprScript = e; | ||
bindings = b; | ||
source = exprScript.getDoubleValuesSource(bindings); | ||
specialValue = v; | ||
} | ||
|
||
@Override | ||
public TermsSetQueryScript newInstance(final LeafReaderContext leaf) throws IOException { | ||
return new TermsSetQueryScript(Collections.emptyMap(), null, null) { | ||
// Fake the scorer until setScorer is called. | ||
DoubleValues values = source.getValues(leaf, null); | ||
|
||
@Override | ||
public Number execute() { | ||
try { | ||
return values.doubleValue(); | ||
} catch (Exception exception) { | ||
throw new GeneralScriptException("Error evaluating " + exprScript, exception); | ||
} | ||
} | ||
|
||
@Override | ||
public void setDocument(int d) { | ||
try { | ||
values.advanceExact(d); | ||
} catch (IOException e) { | ||
throw new IllegalStateException("Can't advance to doc using " + exprScript, e); | ||
} | ||
} | ||
}; | ||
} | ||
|
||
} |
105 changes: 105 additions & 0 deletions
105
...ssion/src/test/java/org/elasticsearch/script/expression/ExpressionTermsSetQueryTests.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,105 @@ | ||
/* | ||
* 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.script.expression; | ||
|
||
import java.io.IOException; | ||
import java.text.ParseException; | ||
import java.util.Collections; | ||
import org.elasticsearch.common.settings.Settings; | ||
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.TermsSetQueryScript; | ||
import org.elasticsearch.search.lookup.SearchLookup; | ||
import org.elasticsearch.test.ESTestCase; | ||
|
||
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 ExpressionTermsSetQueryTests extends ESTestCase { | ||
private ExpressionScriptEngine service; | ||
private SearchLookup lookup; | ||
|
||
@Override | ||
public void setUp() throws Exception { | ||
super.setUp(); | ||
|
||
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); | ||
lookup = new SearchLookup(mapperService, ignored -> fieldData, null); | ||
} | ||
|
||
private TermsSetQueryScript.LeafFactory compile(String expression) { | ||
TermsSetQueryScript.Factory factory = | ||
service.compile(null, expression, TermsSetQueryScript.CONTEXT, Collections.emptyMap()); | ||
return factory.newFactory(Collections.emptyMap(), lookup); | ||
} | ||
|
||
public void testCompileError() { | ||
ScriptException e = expectThrows(ScriptException.class, () -> { | ||
compile("doc['field'].value * *@#)(@$*@#$ + 4"); | ||
}); | ||
assertTrue(e.getCause() instanceof ParseException); | ||
} | ||
|
||
public void testLinkError() { | ||
ScriptException e = expectThrows(ScriptException.class, () -> { | ||
compile("doc['nonexistent'].value * 5"); | ||
}); | ||
assertTrue(e.getCause() instanceof ParseException); | ||
} | ||
|
||
public void testFieldAccess() throws IOException { | ||
TermsSetQueryScript script = compile("doc['field'].value").newInstance(null); | ||
script.setDocument(1); | ||
|
||
double result = script.execute().doubleValue(); | ||
assertEquals(2.718, result, 0.0); | ||
} | ||
|
||
public void testFieldAccessWithFieldAlias() throws IOException { | ||
TermsSetQueryScript script = compile("doc['alias'].value").newInstance(null); | ||
script.setDocument(1); | ||
|
||
double result = script.execute().doubleValue(); | ||
assertEquals(2.718, result, 0.0); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters