Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions changelog/unreleased/intervals-script-filter.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
title: >
Add script filter operator to intervals QParser, evaluated by Lucene Expressions
with interval.start, interval.end and interval.gap variables in scope.
type: added
authors:
- name: Mikhail Khludnev
nick: mkhl
- name: Nick Shanin
nick: nick-boss-tech
links:
- name: SOLR-18585
url: https://issues.apache.org/jira/browse/SOLR-18585
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,13 @@
package org.apache.solr.search;

import java.io.IOException;
import java.text.ParseException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.expressions.Expression;
import org.apache.lucene.expressions.js.JavascriptCompiler;
import org.apache.lucene.queries.intervals.IntervalQuery;
import org.apache.lucene.queries.intervals.Intervals;
import org.apache.lucene.queries.intervals.IntervalsSource;
Expand Down Expand Up @@ -434,8 +437,7 @@ private IntervalsSource applyFilter(
Map.Entry<String, Object> entry = filterMap.entrySet().iterator().next();
String op = entry.getKey();
if ("script".equals(op)) {
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST, "Filter operator 'script' is not supported");
return parseScriptFilter(source, entry.getValue());
}
IntervalsSource other =
parseRuleObject(
Expand All @@ -455,6 +457,38 @@ private IntervalsSource applyFilter(
};
}

/**
* Parses the {@code script} filter operator, which filters intervals by evaluating a Lucene
* Expressions script over the pseudo-variables {@code interval.start}, {@code interval.end}
* and {@code interval.gap}. A non-zero result accepts the interval.
*/
private IntervalsSource parseScriptFilter(IntervalsSource source, Object scriptObj) {
String script =
requireString(asStringObjectMap(scriptObj, "filter 'script'"), "source", "script");
Expression expression;
try {
expression = JavascriptCompiler.compile(script);
} catch (ParseException e) {
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST,
"Invalid script expression '" + script + "' in filter 'script'",
e);
}
ScriptFilterIntervalsSource.IntervalBindings bindings =
new ScriptFilterIntervalsSource.IntervalBindings();
for (String variable : expression.variables) {
try {
bindings.getDoubleValuesSource(variable);
} catch (IllegalArgumentException e) {
throw new SolrException(
SolrException.ErrorCode.BAD_REQUEST,
"Unknown variable '" + variable + "' in filter script '" + script + "'",
e);
}
}
return new ScriptFilterIntervalsSource(source, expression, bindings);
}

/**
* Resolves the field referenced by an optional {@code use_field} rule parameter, falling back
* to the query's default field when absent. Throws BAD_REQUEST if {@code useField} names a
Expand Down
Loading
Loading