You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by mk...@apache.org on 2020/06/30 21:58:52 UTC
[lucene-solr] branch branch_8_6 updated: SOLR-14539: Introducing
{!bool excludeTags=foo, bar}
This is an automated email from the ASF dual-hosted git repository.
mkhl pushed a commit to branch branch_8_6
in repository https://gitbox.apache.org/repos/asf/lucene-solr.git
The following commit(s) were added to refs/heads/branch_8_6 by this push:
new 6b6e510 SOLR-14539: Introducing {!bool excludeTags=foo,bar}
6b6e510 is described below
commit 6b6e510067b333e3a8d62a7a21d6525721be53dd
Author: Mikhail Khludnev <mk...@apache.org>
AuthorDate: Sun Jun 28 16:29:24 2020 +0300
SOLR-14539: Introducing {!bool excludeTags=foo,bar}
---
solr/CHANGES.txt | 2 +
.../org/apache/solr/search/BoolQParserPlugin.java | 53 ++++++++-----
.../apache/solr/search/join/FiltersQParser.java | 91 +++++++++-------------
.../facet/TestJsonFacetsWithNestedObjects.java | 67 ++++++++++++++++
4 files changed, 137 insertions(+), 76 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index 56783dd..1977fd9 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -93,6 +93,8 @@ Improvements
* SOLR-13286: Metrics will no longer write a (sometimes large) log message every minute. This can be re-enabled
via log4j2.xml if desired, or other HttpSolrCall log messages may be quieted on a per handler basis. (Gus Heck)
+* SOLR-14539: Introducing {!bool excludeTags=...} for Query DSL. (Mikhail Khludnev)
+
Optimizations
---------------------
* SOLR-8306: Do not collect expand documents when expand.rows=0 (Marshall Sanders, Amelia Henderson)
diff --git a/solr/core/src/java/org/apache/solr/search/BoolQParserPlugin.java b/solr/core/src/java/org/apache/solr/search/BoolQParserPlugin.java
index 04a9d5a..cd22dd7 100644
--- a/solr/core/src/java/org/apache/solr/search/BoolQParserPlugin.java
+++ b/solr/core/src/java/org/apache/solr/search/BoolQParserPlugin.java
@@ -18,11 +18,14 @@
package org.apache.solr.search;
import org.apache.lucene.search.BooleanClause;
-import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.query.FilterQuery;
import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.search.join.FiltersQParser;
+
+import java.util.IdentityHashMap;
+import java.util.Map;
/**
* Create a boolean query from sub queries.
@@ -35,33 +38,43 @@ public class BoolQParserPlugin extends QParserPlugin {
@Override
public QParser createParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
- return new QParser(qstr, localParams, params, req) {
+ return new FiltersQParser(qstr, localParams, params, req) {
@Override
public Query parse() throws SyntaxError {
- BooleanQuery.Builder builder = new BooleanQuery.Builder();
+ return parseImpl();
+ }
+
+ @Override
+ protected Query unwrapQuery(Query query, BooleanClause.Occur occur) {
+ if (occur== BooleanClause.Occur.FILTER) {
+ if (!(query instanceof ExtendedQuery) || (
+ ((ExtendedQuery) query).getCache())) {
+ return new FilterQuery(query);
+ }
+ } else {
+ if (query instanceof WrappedQuery) {
+ return ((WrappedQuery)query).getWrappedQuery();
+ }
+ }
+ return query;
+ }
+
+ @Override
+ protected Map<QParser, BooleanClause.Occur> clauses() throws SyntaxError {
+ Map<QParser, BooleanClause.Occur> clauses = new IdentityHashMap<>();
SolrParams solrParams = SolrParams.wrapDefaults(localParams, params);
- addQueries(builder, solrParams.getParams("must"), BooleanClause.Occur.MUST);
- addQueries(builder, solrParams.getParams("must_not"), BooleanClause.Occur.MUST_NOT);
- addQueries(builder, solrParams.getParams("filter"), BooleanClause.Occur.FILTER);
- addQueries(builder, solrParams.getParams("should"), BooleanClause.Occur.SHOULD);
- return builder.build();
+ addQueries(clauses, solrParams.getParams("must"), BooleanClause.Occur.MUST);
+ addQueries(clauses, solrParams.getParams("must_not"), BooleanClause.Occur.MUST_NOT);
+ addQueries(clauses, solrParams.getParams("filter"), BooleanClause.Occur.FILTER);
+ addQueries(clauses, solrParams.getParams("should"), BooleanClause.Occur.SHOULD);
+ return clauses;
}
- private void addQueries(BooleanQuery.Builder builder, String[] subQueries, BooleanClause.Occur occur) throws SyntaxError {
+ private void addQueries(Map<QParser, BooleanClause.Occur> clausesDest, String[] subQueries, BooleanClause.Occur occur) throws SyntaxError {
if (subQueries != null) {
for (String subQuery : subQueries) {
final QParser subParser = subQuery(subQuery, null);
- Query extQuery;
- if (BooleanClause.Occur.FILTER.equals(occur)) {
- extQuery = subParser.getQuery();
- if (!(extQuery instanceof ExtendedQuery) || (
- ((ExtendedQuery) extQuery).getCache())) {
- extQuery = new FilterQuery(extQuery);
- }
- } else {
- extQuery = subParser.parse();
- }
- builder.add(extQuery, occur);
+ clausesDest.put(subParser, occur);
}
}
}
diff --git a/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java b/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java
index a3557ff..851f48b 100644
--- a/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java
+++ b/solr/core/src/java/org/apache/solr/search/join/FiltersQParser.java
@@ -17,18 +17,13 @@
package org.apache.solr.search.join;
-import java.util.Collection;
-import java.util.HashSet;
-import java.util.IdentityHashMap;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import org.apache.lucene.search.BooleanClause;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.search.Query;
-import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.StrUtils;
import org.apache.solr.request.SolrQueryRequest;
@@ -42,22 +37,31 @@ public class FiltersQParser extends QParser {
return "param";
}
- FiltersQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
+ protected FiltersQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) {
super(qstr, localParams, params, req);
}
@Override
- public final Query parse() throws SyntaxError {
- Map<Query,Occur> clauses = clauses();
-
- exclude(clauses);
-
- int numClauses = 0;
+ public Query parse() throws SyntaxError {
+ BooleanQuery query = parseImpl();
+ return !query.clauses().isEmpty() ? wrapSubordinateClause(query) : noClausesQuery();
+ }
+
+ protected BooleanQuery parseImpl() throws SyntaxError {
+ Map<QParser, Occur> clauses = clauses();
+
+ exclude(clauses.keySet());
+
BooleanQuery.Builder builder = new BooleanQuery.Builder();
- numClauses += addQuery(builder, clauses);
- numClauses += addFilters(builder, clauses);
- // what about empty query?
- return numClauses > 0 ? wrapSubordinateClause(builder.build()) : noClausesQuery();
+ for (Map.Entry<QParser, Occur> clause: clauses.entrySet()) {
+ builder.add(unwrapQuery(clause.getKey().getQuery(), clause.getValue()), clause.getValue());
+ }
+ // what about empty query?
+ return builder.build();
+ }
+
+ protected Query unwrapQuery(Query query, BooleanClause.Occur occur) {
+ return query;
}
protected Query wrapSubordinateClause(Query subordinate) throws SyntaxError {
@@ -68,30 +72,7 @@ public class FiltersQParser extends QParser {
return new MatchAllDocsQuery();
}
- protected int addQuery(BooleanQuery.Builder builder, Map<Query,Occur> clauses) {
- int cnt=0;
- for (Map.Entry<Query, Occur> clause: clauses.entrySet()) {
- if (clause.getValue() == Occur.MUST) {
- builder.add(clause.getKey(), clause.getValue());
- cnt++;// shouldn't count more than once
- }
- }
- return cnt;
- }
-
- /** @return number of added clauses */
- protected int addFilters(BooleanQuery.Builder builder, Map<Query,Occur> clauses) throws SyntaxError {
- int count=0;
- for (Map.Entry<Query, Occur> clause: clauses.entrySet()) {
- if (clause.getValue() == Occur.FILTER) {
- builder.add( clause.getKey(), Occur.FILTER);
- count++;
- }
- }
- return count;
- }
-
- protected void exclude(Map<Query,Occur> clauses) {
+ protected void exclude(Collection<QParser> clauses) {
Set<String> tagsToExclude = new HashSet<>();
String excludeTags = localParams.get("excludeTags");
if (excludeTags != null) {
@@ -99,18 +80,22 @@ public class FiltersQParser extends QParser {
}
@SuppressWarnings("rawtypes")
Map tagMap = (Map) req.getContext().get("tags");
+ final Collection<QParser> excludeSet;
if (tagMap != null && !tagMap.isEmpty() && !tagsToExclude.isEmpty()) {
- clauses.keySet().removeAll(excludeSet(tagMap, tagsToExclude));
- } // else no filters were tagged
+ excludeSet = excludeSet(tagMap, tagsToExclude);
+ } else {
+ excludeSet = Collections.emptySet();
+ }
+ clauses.removeAll(excludeSet);
}
- protected Map<Query,Occur> clauses() throws SyntaxError {
+ protected Map<QParser,Occur> clauses() throws SyntaxError {
String[] params = localParams.getParams(getFiltersParamName());
if(params!=null && params.length == 0) { // never happens
throw new SyntaxError("Local parameter "+getFiltersParamName() +
" is not defined for "+stringIncludingLocalParams);
}
- Map<Query,Occur> clauses = new IdentityHashMap<>();
+ Map<QParser,Occur> clauses = new IdentityHashMap<>();
for (String filter : params==null ? new String[0] : params) {
if(filter==null || filter.length() == 0) {
@@ -119,21 +104,20 @@ public class FiltersQParser extends QParser {
}
// as a side effect, qparser is mapped by tags in req context
QParser parser = subQuery(filter, null);
- Query query = parser.getQuery();
- clauses.put(query, BooleanClause.Occur.FILTER);
+ clauses.put(parser, BooleanClause.Occur.FILTER);
}
String queryText = localParams.get(QueryParsing.V);
if (queryText != null && queryText.length() > 0) {
QParser parser = subQuery(queryText, null);
- clauses.put(parser.getQuery(), BooleanClause.Occur.MUST);
+ clauses.put(parser, BooleanClause.Occur.MUST);
}
return clauses;
}
- private Collection<?> excludeSet(@SuppressWarnings("rawtypes")
+ private Collection<QParser> excludeSet(@SuppressWarnings("rawtypes")
Map tagMap, Set<String> tagsToExclude) {
- IdentityHashMap<Query,Boolean> excludeSet = new IdentityHashMap<>();
+ IdentityHashMap<QParser,Boolean> excludeSet = new IdentityHashMap<>();
for (String excludeTag : tagsToExclude) {
Object olst = tagMap.get(excludeTag);
// tagMap has entries of List<String,List<QParser>>, but subject to change in the future
@@ -141,12 +125,7 @@ public class FiltersQParser extends QParser {
for (Object o : (Collection<?>)olst) {
if (!(o instanceof QParser)) continue;
QParser qp = (QParser)o;
- try {
- excludeSet.put(qp.getQuery(), Boolean.TRUE);
- } catch (SyntaxError syntaxError) {
- // This should not happen since we should only be retrieving a previously parsed query
- throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, syntaxError);
- }
+ excludeSet.put(qp, Boolean.TRUE);
}
}
return excludeSet.keySet();
diff --git a/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacetsWithNestedObjects.java b/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacetsWithNestedObjects.java
index 1e5b203..4cc943c 100644
--- a/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacetsWithNestedObjects.java
+++ b/solr/core/src/test/org/apache/solr/search/facet/TestJsonFacetsWithNestedObjects.java
@@ -16,9 +16,21 @@
*/
package org.apache.solr.search.facet;
+import java.io.IOException;
+import java.util.Map;
+
import org.apache.solr.SolrTestCaseHS;
+import org.apache.solr.client.solrj.SolrClient;
+import org.apache.solr.client.solrj.SolrServerException;
+import org.apache.solr.client.solrj.impl.InputStreamResponseParser;
+import org.apache.solr.client.solrj.impl.NoOpResponseParser;
+import org.apache.solr.client.solrj.request.json.DirectJsonQueryRequest;
+import org.apache.solr.client.solrj.request.json.JsonQueryRequest;
+import org.apache.solr.client.solrj.response.QueryResponse;
+import org.apache.solr.client.solrj.response.SolrResponseBase;
import org.apache.solr.common.SolrInputDocument;
import org.apache.solr.common.params.ModifiableSolrParams;
+import org.apache.solr.common.util.NamedList;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -32,6 +44,10 @@ public class TestJsonFacetsWithNestedObjects extends SolrTestCaseHS{
private static void indexBooksAndReviews() throws Exception {
final Client client = Client.localClient();
+ indexDocs(client);
+ }
+
+ private static void indexDocs(final Client client) throws IOException, SolrServerException, Exception {
client.deleteByQuery("*:*", null);
SolrInputDocument book1 = sdoc(
@@ -337,6 +353,57 @@ public class TestJsonFacetsWithNestedObjects extends SolrTestCaseHS{
);
}
+ public void testBoolExclusion() throws Exception {
+ final Client client = Client.localClient();
+ ModifiableSolrParams p = params("rows", "0");
+ client.testJQ(params(p,
+ "json.queries", "{'childquery':'comment_t:*'," +
+ "'parentquery':'type_s:book'," +
+ "'child.fq':[ {'#author':{'field':{'f':'author_s','query':'dan'}}}," +
+ "{'#stars':{'field':{'f':'stars_i','query':'4'}}}]," +
+ "'snowcrash':{'field':{'f':'title_t','query':'Snow Crash'}}" +
+ "}",
+ "json.query", "{'#top':{'parent':{'which':{'param':'parentquery'}," +
+ "'query':{'bool':{'filter':{'param':'child.fq'}," +
+ "'must':{'param':'childquery'}" +
+ "}}}}}",
+ "json.facet", "{" +
+ "'comments_for_author':{'domain':{" +
+ "'excludeTags':'top', " +
+ "'blockChildren':'{!v=$parentquery}'," +
+ "'filter':'{!bool filter=$child.fq filter=$childquery excludeTags=author}'" +
+ "}," +
+ "'type':'terms', 'field':'author_s'" +
+ "}" +
+ " ,comments_for_stars: {" +
+ " domain: { excludeTags:top, " +
+ "'blockChildren':'{!v=$parentquery}'," +
+ " filter:\"{!bool filter=$child.fq excludeTags=stars filter=$childquery}\" }," +
+ "type:terms, field:stars_i" +
+ "}" +
+ ",comments_for_stars_parent_filter: {" +
+ "domain: { " +
+ "excludeTags:top, " +
+ "'blockChildren':'{!v=$parentquery}'," +
+ "filter:[\"{!bool filter=$child.fq excludeTags=stars filter=$childquery}\","
+ + "\"{!child of=$parentquery}{!bool filter=$snowcrash}}\"] }," +
+ "type:terms, field:stars_i"+
+ " }" +
+ "}",
+ "json.fields", "'id,title_t'")
+ , "response=={numFound:0,start:0,'numFoundExact':true,docs:[]}"
+ , "facets=={ count:0," +
+ "comments_for_author:{" +
+ " buckets:[ {val:mary, count:1} ]}" +
+ "," +
+ "comments_for_stars:{" +
+ " buckets:[ {val:2, count:1}," +
+ " {val:3, count:1} ]}," +
+ "comments_for_stars_parent_filter:{" +
+ " buckets:[ {val:2, count:1} ]}" +
+ "}");
+ }
+
public void testUniqueBlock() throws Exception {
final Client client = Client.localClient();
ModifiableSolrParams p = params("rows","0");