You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ab...@apache.org on 2017/10/31 14:02:19 UTC

[37/50] [abbrv] lucene-solr:jira/solr-11285-sim: SOLR-10132: A new optional facet.matches parameter to return facet buckets only for terms that match a regular expression. (Gus Heck, Christine Poerschke)

SOLR-10132: A new optional facet.matches parameter to return facet buckets only for terms that match a regular expression. (Gus Heck, Christine Poerschke)


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/b8bcaf92
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/b8bcaf92
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/b8bcaf92

Branch: refs/heads/jira/solr-11285-sim
Commit: b8bcaf92465eed8477baf9932bea624b6b7830f8
Parents: b7332f6
Author: Christine Poerschke <cp...@apache.org>
Authored: Tue Oct 24 21:54:01 2017 +0100
Committer: Christine Poerschke <cp...@apache.org>
Committed: Mon Oct 30 18:03:25 2017 +0000

----------------------------------------------------------------------
 solr/CHANGES.txt                                |  3 ++
 .../solr/request/RegexBytesRefFilter.java       | 46 ++++++++++++++++++++
 .../org/apache/solr/request/SimpleFacets.java   | 44 +++++++++++++------
 .../solr/request/RegexBytesRefFilterTest.java   | 34 +++++++++++++++
 .../apache/solr/request/SimpleFacetsTest.java   | 30 +++++++++++++
 solr/solr-ref-guide/src/faceting.adoc           |  4 ++
 .../apache/solr/common/params/FacetParams.java  |  5 +++
 7 files changed, 153 insertions(+), 13 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b8bcaf92/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 22dfaf9..4c2c149 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -57,6 +57,9 @@ New Features
 
 * SOLR-11518: Implement Suggestions for freedisk violations (noble)
 
+* SOLR-10132: A new optional facet.matches parameter to return facet buckets only
+  for terms that match a regular expression. (Gus Heck, Christine Poerschke)
+
 Bug Fixes
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b8bcaf92/solr/core/src/java/org/apache/solr/request/RegexBytesRefFilter.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/request/RegexBytesRefFilter.java b/solr/core/src/java/org/apache/solr/request/RegexBytesRefFilter.java
new file mode 100644
index 0000000..5cf85df
--- /dev/null
+++ b/solr/core/src/java/org/apache/solr/request/RegexBytesRefFilter.java
@@ -0,0 +1,46 @@
+/*
+ * 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.request;
+
+import java.util.function.Predicate;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import org.apache.lucene.util.BytesRef;
+
+/**
+ * An implementation of {@link Predicate} which returns true if the BytesRef matches the supplied regular expression.
+ */
+public class RegexBytesRefFilter implements Predicate<BytesRef> {
+
+  final private Pattern compiled;
+
+  public RegexBytesRefFilter(String regex) {
+    this.compiled = Pattern.compile(regex);
+  }
+
+  protected boolean includeString(String term) {
+    Matcher m = compiled.matcher(term);
+    return m.matches();
+  }
+
+  @Override
+  public boolean test(BytesRef term) {
+    return includeString(term.utf8ToString());
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b8bcaf92/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
----------------------------------------------------------------------
diff --git a/solr/core/src/java/org/apache/solr/request/SimpleFacets.java b/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
index 4a911d2..cc5559d 100644
--- a/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
+++ b/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
@@ -348,6 +348,16 @@ public class SimpleFacets {
     ENUM, FC, FCS, UIF;
   }
 
+  /**
+   * Create a new bytes ref filter for excluding facet terms.
+   *
+   * This method by default uses the {@link FacetParams#FACET_EXCLUDETERMS} parameter
+   * but custom SimpleFacets classes could use a different implementation.
+   *
+   * @param field the field to check for facet term filters
+   * @param params the request parameter object
+   * @return A predicate for filtering terms or null if no filters are applicable.
+   */
   protected Predicate<BytesRef> newExcludeBytesRefFilter(String field, SolrParams params) {
     final String exclude = params.getFieldParam(field, FacetParams.FACET_EXCLUDETERMS);
     if (exclude == null) {
@@ -364,30 +374,37 @@ public class SimpleFacets {
     };
   }
 
+  /**
+   * Create a new bytes ref filter for filtering facet terms. If more than one filter is
+   * applicable the applicable filters will be returned as an {@link Predicate#and(Predicate)}
+   * of all such filters.
+   *
+   * @param field the field to check for facet term filters
+   * @param params the request parameter object
+   * @return A predicate for filtering terms or null if no filters are applicable.
+   */
   protected Predicate<BytesRef> newBytesRefFilter(String field, SolrParams params) {
     final String contains = params.getFieldParam(field, FacetParams.FACET_CONTAINS);
 
-    final Predicate<BytesRef> containsFilter;
+    Predicate<BytesRef> finalFilter = null;
+
     if (contains != null) {
       final boolean containsIgnoreCase = params.getFieldBool(field, FacetParams.FACET_CONTAINS_IGNORE_CASE, false);
-      containsFilter = new SubstringBytesRefFilter(contains, containsIgnoreCase);
-    } else {
-      containsFilter = null;
+      finalFilter = new SubstringBytesRefFilter(contains, containsIgnoreCase);
     }
 
-    final Predicate<BytesRef> excludeFilter = newExcludeBytesRefFilter(field, params);
-
-    if (containsFilter == null && excludeFilter == null) {
-      return null;
+    final String regex = params.getFieldParam(field, FacetParams.FACET_MATCHES);
+    if (regex != null) {
+      final RegexBytesRefFilter regexBytesRefFilter = new RegexBytesRefFilter(regex);
+      finalFilter = (finalFilter == null) ? regexBytesRefFilter : finalFilter.and(regexBytesRefFilter);
     }
 
-    if (containsFilter != null && excludeFilter == null) {
-      return containsFilter;
-    } else if (containsFilter == null && excludeFilter != null) {
-      return excludeFilter;
+    final Predicate<BytesRef> excludeFilter = newExcludeBytesRefFilter(field, params);
+    if (excludeFilter != null) {
+      finalFilter = (finalFilter == null) ? excludeFilter : finalFilter.and(excludeFilter);
     }
 
-    return containsFilter.and(excludeFilter);
+    return finalFilter;
   }
 
   /**
@@ -493,6 +510,7 @@ public class SimpleFacets {
             }
             if (termFilter != null) {
               throw new SolrException(ErrorCode.BAD_REQUEST, "BytesRef term filters ("
+                      + FacetParams.FACET_MATCHES + ", "
                       + FacetParams.FACET_CONTAINS + ", "
                       + FacetParams.FACET_EXCLUDETERMS + ") are not supported on numeric types");
             }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b8bcaf92/solr/core/src/test/org/apache/solr/request/RegexBytesRefFilterTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/request/RegexBytesRefFilterTest.java b/solr/core/src/test/org/apache/solr/request/RegexBytesRefFilterTest.java
new file mode 100644
index 0000000..22af631
--- /dev/null
+++ b/solr/core/src/test/org/apache/solr/request/RegexBytesRefFilterTest.java
@@ -0,0 +1,34 @@
+/*
+ * 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.request;
+
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.LuceneTestCase;
+import org.junit.Test;
+
+public class RegexBytesRefFilterTest extends LuceneTestCase {
+
+  @Test
+  public void testSubstringBytesRefFilter() {
+    final RegexBytesRefFilter filter = new RegexBytesRefFilter("^f[o]{2}.*");
+
+    assertTrue(filter.test(new BytesRef("foobar")));
+    assertFalse(filter.test(new BytesRef("qux")));
+    assertFalse(filter.test(new BytesRef("quxfoo")));
+  }
+
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b8bcaf92/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java
----------------------------------------------------------------------
diff --git a/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java b/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java
index ac9125e..1f87786 100644
--- a/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java
+++ b/solr/core/src/test/org/apache/solr/request/SimpleFacetsTest.java
@@ -568,6 +568,36 @@ public class SimpleFacetsTest extends SolrTestCaseJ4 {
 
 
   @Test
+  public void testFacetMatches() {
+    final String[][] uifSwitch = new String[][] {
+        new String[]{"f.trait_s.facet.method", "uif"},
+        new String[]{"facet.method", "uif"}
+    };
+    final String[] none = new String[]{};
+    for (String[] aSwitch : uifSwitch) {
+      for(String[] methodParam : new String[][]{ none, aSwitch}) {
+        assertQ("check facet.match filters facets returned",
+            req(methodParam
+                , "q", "id:[42 TO 47]"
+                , "facet", "true"
+                , "facet.field", "trait_s"
+                , "facet.matches", ".*o.*"
+            )
+            , "*[count(//doc)=6]"
+
+            , "//lst[@name='facet_counts']/lst[@name='facet_queries']"
+
+            , "//lst[@name='facet_counts']/lst[@name='facet_fields']"
+            , "//lst[@name='facet_fields']/lst[@name='trait_s']"
+            , "*[count(//lst[@name='trait_s']/int)=2]"
+            , "//lst[@name='trait_s']/int[@name='Tool'][.='2']"
+            , "//lst[@name='trait_s']/int[@name='Obnoxious'][.='2']"
+        );
+      }
+    }
+  }
+
+  @Test
   public void testSimpleFacetCounts() {
  
     assertQ("standard request handler returns all matches",

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b8bcaf92/solr/solr-ref-guide/src/faceting.adoc
----------------------------------------------------------------------
diff --git a/solr/solr-ref-guide/src/faceting.adoc b/solr/solr-ref-guide/src/faceting.adoc
index d8c0d0e..00a7ef1 100644
--- a/solr/solr-ref-guide/src/faceting.adoc
+++ b/solr/solr-ref-guide/src/faceting.adoc
@@ -65,6 +65,10 @@ The `facet.contains` parameter limits the terms on which to facet to those conta
 
 If `facet.contains` is used, the `facet.contains.ignoreCase` parameter causes case to be ignored when matching the given substring against candidate facet terms.
 
+`facet.matches`::
+
+If you want to only return facet buckets for the terms that match a regular expression.
+
 `facet.sort`::
 This parameter determines the ordering of the facet field constraints.
 +

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/b8bcaf92/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java
----------------------------------------------------------------------
diff --git a/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java b/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java
index e91b9af..52e5f4a 100644
--- a/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java
+++ b/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java
@@ -176,6 +176,11 @@ public interface FacetParams {
   public static final String FACET_CONTAINS = FACET + ".contains";
 
   /**
+   * Only return constraints of a facet field containing the given string.
+   */
+  public static final String FACET_MATCHES = FACET + ".matches";
+
+  /**
    * If using facet contains, ignore case when comparing values.
    */
   public static final String FACET_CONTAINS_IGNORE_CASE = FACET_CONTAINS + ".ignoreCase";