You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ds...@apache.org on 2016/07/29 02:53:28 UTC
lucene-solr:branch_6x: SOLR-9279: new function queries: gt, gte, lt,
lte,
eq Lucene Queries module: new ComparisonBoolFunction base class (cherry picked
from commit d12b93e)
Repository: lucene-solr
Updated Branches:
refs/heads/branch_6x 46e183dee -> c434eff82
SOLR-9279: new function queries: gt, gte, lt, lte, eq
Lucene Queries module: new ComparisonBoolFunction base class
(cherry picked from commit d12b93e)
Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/c434eff8
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/c434eff8
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/c434eff8
Branch: refs/heads/branch_6x
Commit: c434eff828702f32251c3a225623b65ae869ea82
Parents: 46e183d
Author: David Smiley <ds...@apache.org>
Authored: Thu Jul 28 22:45:43 2016 -0400
Committer: David Smiley <ds...@apache.org>
Committed: Thu Jul 28 22:49:18 2016 -0400
----------------------------------------------------------------------
lucene/CHANGES.txt | 3 +
.../valuesource/ComparisonBoolFunction.java | 105 +++++++++++++++++++
solr/CHANGES.txt | 3 +
.../apache/solr/search/ValueSourceParser.java | 52 +++++++++
.../function/SolrComparisonBoolFunction.java | 58 ++++++++++
.../apache/solr/search/QueryEqualityTest.java | 27 ++++-
.../solr/search/function/TestFunctionQuery.java | 69 +++++++++++-
7 files changed, 311 insertions(+), 6 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c434eff8/lucene/CHANGES.txt
----------------------------------------------------------------------
diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt
index 831a8b4..dbf39ed 100644
--- a/lucene/CHANGES.txt
+++ b/lucene/CHANGES.txt
@@ -44,6 +44,9 @@ New Features
for calculating query norm and coordination factor in Lucene 6.x.
Lucene 7 will no longer have those factors. (Uwe Schindler, Sascha Markus)
+* SOLR-9279: Queries module: new ComparisonBoolFunction base class
+ (Doug Turnbull via David Smiley)
+
Bug Fixes
* LUCENE-6662: Fixed potential resource leaks. (Rishabh Patel via Adrien Grand)
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c434eff8/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/ComparisonBoolFunction.java
----------------------------------------------------------------------
diff --git a/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/ComparisonBoolFunction.java b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/ComparisonBoolFunction.java
new file mode 100644
index 0000000..82d723a
--- /dev/null
+++ b/lucene/queries/src/java/org/apache/lucene/queries/function/valuesource/ComparisonBoolFunction.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.lucene.queries.function.valuesource;
+
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.queries.function.docvalues.BoolDocValues;
+import org.apache.lucene.search.IndexSearcher;
+
+/**
+ * Base class for comparison operators useful within an "if"/conditional.
+ */
+public abstract class ComparisonBoolFunction extends BoolFunction {
+
+ private final ValueSource lhs;
+ private final ValueSource rhs;
+ private final String name;
+
+ public ComparisonBoolFunction(ValueSource lhs, ValueSource rhs, String name) {
+ this.lhs = lhs;
+ this.rhs = rhs;
+ this.name = name;
+ }
+
+ /** Perform the comparison, returning true or false */
+ public abstract boolean compare(int doc, FunctionValues lhs, FunctionValues rhs);
+
+ /** Uniquely identify the operation (ie "gt", "lt" "gte", etc) */
+ public String name() {
+ return this.name;
+ }
+
+ @Override
+ public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+ final FunctionValues lhsVal = this.lhs.getValues(context, readerContext);
+ final FunctionValues rhsVal = this.rhs.getValues(context, readerContext);
+ final String compLabel = this.name();
+
+ return new BoolDocValues(this) {
+ @Override
+ public boolean boolVal(int doc) {
+ return compare(doc, lhsVal, rhsVal);
+ }
+
+ @Override
+ public String toString(int doc) {
+ return compLabel + "(" + lhsVal.toString(doc) + "," + rhsVal.toString(doc) + ")";
+ }
+
+ @Override
+ public boolean exists(int doc) {
+ return lhsVal.exists(doc) && rhsVal.exists(doc);
+ }
+
+ };
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this.getClass() != o.getClass()) return false;
+ ComparisonBoolFunction other = (ComparisonBoolFunction)o;
+ return name().equals(other.name())
+ && lhs.equals(other.lhs)
+ && rhs.equals(other.rhs); }
+
+ @Override
+ public int hashCode() {
+ int h = this.getClass().hashCode();
+ h = h * 31 + this.name().hashCode();
+ h = h * 31 + lhs.hashCode();
+ h = h * 31 + rhs.hashCode();
+ return h;
+ }
+
+ @Override
+ public String description() {
+ return name() + "(" + lhs.description() + "," + rhs.description() + ")";
+ }
+
+ @Override
+ public void createWeight(Map context, IndexSearcher searcher) throws IOException {
+ lhs.createWeight(context, searcher);
+ rhs.createWeight(context, searcher);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c434eff8/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 56d10e8..306acb1 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -71,6 +71,9 @@ New Features
doing a core backup, and in replication. Snapshot metadata is stored in a new snapshot_metadata/ dir.
(Hrishikesh Gadre via David Smiley)
+* SOLR-9279: New boolean comparison function queries comparing numeric arguments: gt, gte, lt, lte, eq
+ (Doug Turnbull, David Smiley)
+
Bug Fixes
----------------------
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c434eff8/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java b/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java
index 3d3803a..637c31b 100644
--- a/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java
+++ b/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java
@@ -64,6 +64,7 @@ import org.apache.solr.search.facet.UniqueAgg;
import org.apache.solr.search.function.CollapseScoreFunction;
import org.apache.solr.search.function.OrdFieldSource;
import org.apache.solr.search.function.ReverseOrdFieldSource;
+import org.apache.solr.search.function.SolrComparisonBoolFunction;
import org.apache.solr.search.function.distance.GeoDistValueSourceParser;
import org.apache.solr.search.function.distance.GeohashFunction;
import org.apache.solr.search.function.distance.GeohashHaversineFunction;
@@ -826,6 +827,57 @@ public abstract class ValueSourceParser implements NamedListInitializedPlugin {
}
});
+ addParser("gt", new ValueSourceParser() {
+ @Override
+ public ValueSource parse(FunctionQParser fp) throws SyntaxError {
+ ValueSource lhsValSource = fp.parseValueSource();
+ ValueSource rhsValSource = fp.parseValueSource();
+
+ return new SolrComparisonBoolFunction(lhsValSource, rhsValSource, "gt", (cmp) -> cmp > 0);
+ }
+ });
+
+ addParser("lt", new ValueSourceParser() {
+ @Override
+ public ValueSource parse(FunctionQParser fp) throws SyntaxError {
+ ValueSource lhsValSource = fp.parseValueSource();
+ ValueSource rhsValSource = fp.parseValueSource();
+
+ return new SolrComparisonBoolFunction(lhsValSource, rhsValSource, "lt", (cmp) -> cmp < 0);
+ }
+ });
+
+ addParser("gte", new ValueSourceParser() {
+ @Override
+ public ValueSource parse(FunctionQParser fp) throws SyntaxError {
+ ValueSource lhsValSource = fp.parseValueSource();
+ ValueSource rhsValSource = fp.parseValueSource();
+
+ return new SolrComparisonBoolFunction(lhsValSource, rhsValSource, "gte", (cmp) -> cmp >= 0);
+
+ }
+ });
+
+ addParser("lte", new ValueSourceParser() {
+ @Override
+ public ValueSource parse(FunctionQParser fp) throws SyntaxError {
+ ValueSource lhsValSource = fp.parseValueSource();
+ ValueSource rhsValSource = fp.parseValueSource();
+
+ return new SolrComparisonBoolFunction(lhsValSource, rhsValSource, "lte", (cmp) -> cmp <= 0);
+ }
+ });
+
+ addParser("eq", new ValueSourceParser() {
+ @Override
+ public ValueSource parse(FunctionQParser fp) throws SyntaxError {
+ ValueSource lhsValSource = fp.parseValueSource();
+ ValueSource rhsValSource = fp.parseValueSource();
+
+ return new SolrComparisonBoolFunction(lhsValSource, rhsValSource, "eq", (cmp) -> cmp == 0);
+ }
+ });
+
addParser("def", new ValueSourceParser() {
@Override
public ValueSource parse(FunctionQParser fp) throws SyntaxError {
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c434eff8/solr/core/src/java/org/apache/solr/search/function/SolrComparisonBoolFunction.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/search/function/SolrComparisonBoolFunction.java b/solr/core/src/java/org/apache/solr/search/function/SolrComparisonBoolFunction.java
new file mode 100644
index 0000000..c994fbb
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/search/function/SolrComparisonBoolFunction.java
@@ -0,0 +1,58 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF 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.apache.solr.search.function;
+
+import org.apache.lucene.queries.function.FunctionValues;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.queries.function.docvalues.IntDocValues;
+import org.apache.lucene.queries.function.docvalues.LongDocValues;
+import org.apache.lucene.queries.function.valuesource.ComparisonBoolFunction;
+
+/**
+ * Refines {@link ComparisonBoolFunction} to compare based on a 'long' or 'double' depending on if the
+ * any of the FunctionValues are {@link LongDocValues}.
+ */
+public class SolrComparisonBoolFunction extends ComparisonBoolFunction {
+
+ private final Compare cmp;
+
+ public interface Compare {
+ boolean compare(int integer);
+ }
+
+ public SolrComparisonBoolFunction(ValueSource lhs, ValueSource rhs, String name, Compare cmp) {
+ super(lhs, rhs, name);
+ this.cmp = cmp;
+ }
+
+ @Override
+ public boolean compare(int doc, FunctionValues lhs, FunctionValues rhs) {
+ // TODO consider a separate FunctionValues impl, one for Long, one for Double
+ // performs the safest possible numeric comparison, if both lhs and rhs are Longs, then
+ // we perform a Long comparison to avoid the issues with precision when casting to doubles
+ boolean lhsAnInt = (lhs instanceof LongDocValues || lhs instanceof IntDocValues);
+ boolean rhsAnInt = (rhs instanceof LongDocValues || rhs instanceof IntDocValues);
+ if (lhsAnInt && rhsAnInt) {
+ return cmp.compare(Long.compare(lhs.longVal(doc), rhs.longVal(doc)));
+ } else {
+ return cmp.compare(Double.compare(lhs.doubleVal(doc), rhs.doubleVal(doc)));
+ }
+ }
+
+ // note: don't override equals; the "name" will be unique and is already compared
+}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c434eff8/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java b/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java
index 2a89473..9c51844 100644
--- a/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java
+++ b/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java
@@ -15,6 +15,11 @@
* limitations under the License.
*/
package org.apache.solr.search;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryUtils;
import org.apache.solr.SolrTestCaseJ4;
@@ -24,10 +29,6 @@ import org.apache.solr.response.SolrQueryResponse;
import org.junit.AfterClass;
import org.junit.BeforeClass;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
-
/**
@@ -1075,4 +1076,22 @@ public class QueryEqualityTest extends SolrTestCaseJ4 {
// assertFuncEquals("agg_multistat(foo_i)", "agg_multistat(foo_i)");
}
+ public void testCompares() throws Exception {
+ assertFuncEquals("gt(foo_i,2)", "gt(foo_i, 2)");
+ assertFuncEquals("gt(foo_i,2)", "gt(foo_i,2)");
+ assertFuncEquals("lt(foo_i,2)", "lt(foo_i,2)");
+ assertFuncEquals("lte(foo_i,2)", "lte(foo_i,2)");
+ assertFuncEquals("gte(foo_i,2)", "gte(foo_i,2)");
+ assertFuncEquals("eq(foo_i,2)", "eq(foo_i,2)");
+
+ boolean equals = false;
+ try {
+ assertFuncEquals("eq(foo_i,2)", "lt(foo_i,2)");
+ equals = true;
+ } catch (AssertionError e) {
+ //expected
+ }
+ assertFalse(equals);
+ }
+
}
http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/c434eff8/solr/core/src/test/org/apache/solr/search/function/TestFunctionQuery.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/search/function/TestFunctionQuery.java b/solr/core/src/test/org/apache/solr/search/function/TestFunctionQuery.java
index f94a9ee..8c65b58 100644
--- a/solr/core/src/test/org/apache/solr/search/function/TestFunctionQuery.java
+++ b/solr/core/src/test/org/apache/solr/search/function/TestFunctionQuery.java
@@ -98,7 +98,7 @@ public class TestFunctionQuery extends SolrTestCaseJ4 {
return sb.toString();
}
- void singleTest(String field, String funcTemplate, List<String> args, float... results) {
+ protected void singleTest(String field, String funcTemplate, List<String> args, float... results) {
String parseableQuery = func(field, funcTemplate);
List<String> nargs = new ArrayList<>(Arrays.asList("q", parseableQuery
@@ -793,4 +793,69 @@ public class TestFunctionQuery extends SolrTestCaseJ4 {
}
}
-}
+ @Test
+ public void testNumericComparisons() throws Exception {
+ assertU(adoc("id", "1", "age_i", "35"));
+ assertU(adoc("id", "2", "age_i", "25"));
+ assertU(commit());
+
+ // test weighting of functions
+ assertJQ(req("q", "id:1", "fl", "a:gt(age_i,30),b:lt(age_i,30)")
+ , "/response/docs/[0]=={'a':true,'b':false}");
+
+ assertJQ(req("q", "id:1", "fl", "a:exists(gt(foo_i,30))")
+ , "/response/docs/[0]=={'a':false}");
+
+ singleTest("age_i", "if(gt(age_i,30),5,2)",
+ /*id*/1, /*score*/5,
+ /*id*/2, /*score*/2);
+
+ singleTest("age_i", "if(lt(age_i,30),5,2)",
+ /*id*/1, /*score*/2,
+ /*id*/2, /*score*/5);
+
+ singleTest("age_i", "if(lt(age_i,34.5),5,2)",
+ /*id*/1, /*score*/2,
+ /*id*/2, /*score*/5);
+
+ singleTest("age_i", "if(lte(age_i,35),5,2)",
+ /*id*/1, /*score*/5,
+ /*id*/2, /*score*/5);
+
+ singleTest("age_i", "if(gte(age_i,25),5,2)",
+ /*id*/1, /*score*/5,
+ /*id*/2, /*score*/5);
+
+ singleTest("age_i", "if(lte(age_i,25),5,2)",
+ /*id*/1, /*score*/2,
+ /*id*/2, /*score*/5);
+
+ singleTest("age_i", "if(gte(age_i,35),5,2)",
+ /*id*/1, /*score*/5,
+ /*id*/2, /*score*/2);
+
+
+ singleTest("age_i", "if(eq(age_i,30),5,2)",
+ /*id*/1, /*score*/2,
+ /*id*/2, /*score*/2);
+
+ singleTest("age_i", "if(eq(age_i,35),5,2)",
+ /*id*/1, /*score*/5,
+ /*id*/2, /*score*/2);
+ }
+
+ public void testLongComparisons() {
+ assertU(adoc("id", "1", "number_of_atoms_in_universe_l", Long.toString(Long.MAX_VALUE)));
+ assertU(adoc("id", "2", "number_of_atoms_in_universe_l", Long.toString(Long.MAX_VALUE - 1)));
+ assertU(commit());
+
+ singleTest("number_of_atoms_in_universe_l", "if(gt(number_of_atoms_in_universe_l," + Long.toString(Long.MAX_VALUE - 1) + "),5,2)",
+ /*id*/1, /*score*/5,
+ /*id*/2, /*score*/2);
+
+ singleTest("number_of_atoms_in_universe_l", "if(lt(number_of_atoms_in_universe_l," + Long.toString(Long.MAX_VALUE) + "),5,2)",
+ /*id*/2, /*score*/5,
+ /*id*/1, /*score*/2);
+ }
+
+ }