You are viewing a plain text version of this content. The canonical link for it is here.
Posted to solr-commits@lucene.apache.org by ho...@apache.org on 2006/09/07 20:55:15 UTC
svn commit: r441175 - in /incubator/solr/trunk: ./
src/java/org/apache/solr/request/ src/java/org/apache/solr/util/
src/test/org/apache/solr/
Author: hossman
Date: Thu Sep 7 11:55:14 2006
New Revision: 441175
URL: http://svn.apache.org/viewvc?view=rev&rev=441175
Log:
SOLR-44 - simple facet support for StandardRequestHandler and DisMaxRequestHandler
Added:
incubator/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java
incubator/solr/trunk/src/java/org/apache/solr/util/BoundedTreeSet.java
Modified:
incubator/solr/trunk/CHANGES.txt
incubator/solr/trunk/src/java/org/apache/solr/request/DisMaxRequestHandler.java
incubator/solr/trunk/src/java/org/apache/solr/request/SolrParams.java
incubator/solr/trunk/src/java/org/apache/solr/request/StandardRequestHandler.java
incubator/solr/trunk/src/java/org/apache/solr/util/DisMaxParams.java
incubator/solr/trunk/src/java/org/apache/solr/util/SolrPluginUtils.java
incubator/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java
incubator/solr/trunk/src/test/org/apache/solr/DisMaxRequestHandlerTest.java
Modified: incubator/solr/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/incubator/solr/trunk/CHANGES.txt?view=diff&rev=441175&r1=441174&r2=441175
==============================================================================
--- incubator/solr/trunk/CHANGES.txt (original)
+++ incubator/solr/trunk/CHANGES.txt Thu Sep 7 11:55:14 2006
@@ -46,6 +46,9 @@
be compressed using the compress=true setting. The field type also gains the
ability to specify a size threshold at which field data is compressed.
(klaas, SOLR-45)
+24. Simple faceted search support for fields (enumerating terms)
+ and arbitrary queries added to both StandardRequestHandler and
+ DisMaxRequestHandler. (hossman, SOLR-44)
Changes in runtime behavior
1. classes reorganized into different packages, package names changed to Apache
@@ -59,6 +62,8 @@
using a '<lst name="defaults">...</lst>' init param, for backwards
compatability all init prams will be used as defaults if an init param
with that name does not exist. (hossman, SOLR-43)
+ 6. The DisMaxRequestHandler now supports multiple occurances of the "fq"
+ param. (hossman, SOLR-44)
Optimizations
1. getDocListAndSet can now generate both a DocList and a DocSet from a
Modified: incubator/solr/trunk/src/java/org/apache/solr/request/DisMaxRequestHandler.java
URL: http://svn.apache.org/viewvc/incubator/solr/trunk/src/java/org/apache/solr/request/DisMaxRequestHandler.java?view=diff&rev=441175&r1=441174&r2=441175
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/DisMaxRequestHandler.java (original)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/DisMaxRequestHandler.java Thu Sep 7 11:55:14 2006
@@ -22,6 +22,8 @@
import org.apache.solr.search.SolrIndexSearcher;
import org.apache.solr.search.DocList;
+import org.apache.solr.search.DocSet;
+import org.apache.solr.search.DocListAndSet;
import org.apache.solr.search.SolrQueryParser;
import org.apache.solr.search.QueryParsing;
@@ -98,6 +100,8 @@
* <li> fq - (Filter Query) a raw lucene query that can be used
* to restrict the super set of products we are interested in - more
* efficient then using bq, but doesn't influence score.
+ * This param can be specified multiple times, and the filters
+ * are addative.
* </li>
* </ul>
*
@@ -113,7 +117,9 @@
* </ul>
*
* <pre>
- * :TODO: make bf,fq,pf,qf multival params now that SolrParams supports them
+ * :TODO: document facet param support
+ *
+ * :TODO: make bf,pf,qf multival params now that SolrParams supports them
* </pre>
*/
public class DisMaxRequestHandler
@@ -310,41 +316,47 @@
/* * * Restrict Results * * */
- List<Query> restrictions = new ArrayList<Query>(1);
-
- /* User Restriction */
- String filterQueryString = params.get(DMP.FQ);
- Query filterQuery = null;
- if (null != filterQueryString && !filterQueryString.equals("")) {
- filterQuery = p.parse(filterQueryString);
- restrictions.add(filterQuery);
- }
+ List<Query> restrictions = U.parseFilterQueries(req);
/* * * Generate Main Results * * */
flags |= U.setReturnFields(req,rsp);
- DocList results = s.getDocList(query, restrictions,
+
+ DocListAndSet results = new DocListAndSet();
+ NamedList facetInfo = null;
+ if (params.getBool(FACET,false)) {
+ results = s.getDocListAndSet(query, restrictions,
SolrPluginUtils.getSort(req),
req.getStart(), req.getLimit(),
flags);
- rsp.add("search-results",results);
+ facetInfo = getFacetInfo(req, rsp, results.docSet);
+ } else {
+ results.docList = s.getDocList(query, restrictions,
+ SolrPluginUtils.getSort(req),
+ req.getStart(), req.getLimit(),
+ flags);
+ }
+ rsp.add("search-results",results.docList);
+
+ if (null != facetInfo) rsp.add("facet_counts", facetInfo);
/* * * Debugging Info * * */
try {
- NamedList debug = U.doStandardDebug(req, userQuery, query, results);
+ NamedList debug = U.doStandardDebug(req, userQuery, query, results.docList);
if (null != debug) {
debug.add("boostquery", boostQuery);
debug.add("boostfunc", boostFunc);
-
- debug.add("filterquery", filterQueryString);
- if (null != filterQuery) {
- debug.add("parsedfilterquery",
- QueryParsing.toString(filterQuery, schema));
+ if (null != restrictions) {
+ debug.add("filter_queries", params.getParams(FQ));
+ List<String> fqs = new ArrayList<String>(restrictions.size());
+ for (Query fq : restrictions) {
+ fqs.add(QueryParsing.toString(fq, req.getSchema()));
+ }
+ debug.add("parsed_filter_queries",fqs);
}
-
rsp.add("debug", debug);
}
@@ -359,8 +371,10 @@
BooleanQuery highlightQuery = new BooleanQuery();
U.flattenBooleanQuery(highlightQuery, query);
- NamedList sumData = HighlightingUtils.doHighlighting(results, highlightQuery,
- req, queryFields.keySet().toArray(new String[0]));
+ String[] highFields = queryFields.keySet().toArray(new String[0]);
+ NamedList sumData =
+ HighlightingUtils.doHighlighting(results.docList, highlightQuery,
+ req, highFields);
if(sumData != null)
rsp.add("highlighting", sumData);
}
@@ -372,4 +386,22 @@
}
}
+ /**
+ * Fetches information about Facets for this request.
+ *
+ * Subclasses may with to override this method to provide more
+ * advanced faceting behavior.
+ * @see SimpleFacets#getFacetCounts
+ */
+ protected NamedList getFacetInfo(SolrQueryRequest req,
+ SolrQueryResponse rsp,
+ DocSet mainSet) {
+
+ SimpleFacets f = new SimpleFacets(req.getSearcher(),
+ mainSet,
+ req.getParams());
+ return f.getFacetCounts();
+ }
+
+
}
Added: incubator/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java
URL: http://svn.apache.org/viewvc/incubator/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java?view=auto&rev=441175
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/SimpleFacets.java Thu Sep 7 11:55:14 2006
@@ -0,0 +1,245 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.index.Term;
+import org.apache.lucene.index.TermEnum;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.queryParser.ParseException;
+import org.apache.lucene.queryParser.QueryParser;
+import org.apache.lucene.search.*;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.core.SolrException;
+import org.apache.solr.request.SolrParams;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.request.SolrQueryResponse;
+import org.apache.solr.request.DefaultSolrParams;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.schema.FieldType;
+import org.apache.solr.search.*;
+import org.apache.solr.util.NamedList;
+import org.apache.solr.util.BoundedTreeSet;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.logging.Level;
+
+/**
+ * A class that generates simple Facet information for a request.
+ *
+ * More advanced facet implementations may compose or subclass this class
+ * to leverage any of it's functionality.
+ */
+public class SimpleFacets {
+
+ /** The main set of documents all facet counts should be relative to */
+ protected DocSet docs;
+ /** Configuration params behavior should be driven by */
+ protected SolrParams params;
+ /** Searcher to use for all calculations */
+ protected SolrIndexSearcher searcher;
+
+ public SimpleFacets(SolrIndexSearcher searcher,
+ DocSet docs,
+ SolrParams params) {
+ this.searcher = searcher;
+ this.docs = docs;
+ this.params = params;
+ }
+
+ /**
+ * Looks at various Params to determing if any simple Facet Constraint count
+ * computations are desired.
+ *
+ * @see #getFacetQueryCounts
+ * @see #getFacetFieldCounts
+ * @see SolrParams#FACET
+ * @return a NamedList of Facet Count info or null
+ */
+ public NamedList getFacetCounts() {
+
+ // if someone called this method, benefit of the doubt: assume true
+ if (!params.getBool(params.FACET,true))
+ return null;
+
+ NamedList res = new NamedList();
+ try {
+
+ res.add("facet_queries", getFacetQueryCounts());
+
+ res.add("facet_fields", getFacetFieldCounts());
+
+ } catch (Exception e) {
+ SolrException.logOnce(SolrCore.log, "Exception during facet counts", e);
+ res.add("exception", SolrException.toStr(e));
+ }
+ return res;
+ }
+
+ /**
+ * Returns a list of facet counts for each of the facet queries
+ * specified in the params
+ *
+ * @see SolrParams#FACET_QUERY
+ */
+ public NamedList getFacetQueryCounts() throws IOException,ParseException {
+
+ NamedList res = new NamedList();
+
+ /* Ignore SolrParams.DF - could have init param facet.query assuming
+ * the schema default with query param DF intented to only affect Q.
+ * If user doesn't want schema default for facet.query, they should be
+ * explicit.
+ */
+ SolrQueryParser qp = new SolrQueryParser(searcher.getSchema(),null);
+
+ String[] facetQs = params.getParams(SolrParams.FACET_QUERY);
+ if (null != facetQs && 0 != facetQs.length) {
+ for (String q : facetQs) {
+ res.add(q, searcher.numDocs(qp.parse(q), docs));
+ }
+ }
+
+ return res;
+ }
+
+ /**
+ * Returns a list of value constraints and the associated facet counts
+ * for each facet field specified in the params.
+ *
+ * @see SolrParams#FACET_FIELD
+ * @see #getFacetFieldMissingCount
+ * @see #getFacetTermEnumCounts
+ */
+ public NamedList getFacetFieldCounts()
+ throws IOException {
+
+ NamedList res = new NamedList();
+ String[] facetFs = params.getParams(SolrParams.FACET_FIELD);
+ if (null != facetFs && 0 != facetFs.length) {
+
+ for (String f : facetFs) {
+
+ NamedList counts = getFacetTermEnumCounts(f);
+
+ if (params.getFieldBool(f, params.FACET_MISSING, false))
+ counts.add(null, getFacetFieldMissingCount(f));
+
+ res.add(f, counts);
+
+ }
+ }
+ return res;
+ }
+
+ /**
+ * Returns a count of the documents in the set which do not have any
+ * terms for for the specified field.
+ *
+ * @see SolrParams#FACET_MISSING
+ */
+ public int getFacetFieldMissingCount(String fieldName)
+ throws IOException {
+
+ DocSet hasVal = searcher.getDocSet
+ (new ConstantScoreRangeQuery(fieldName, null, null, false, false));
+ return docs.andNotSize(hasVal);
+ }
+
+ /**
+ * Returns a list of terms in the specified field along with the
+ * corrisponding count of documents in the set that match that constraint.
+ *
+ * @see SolrParams#FACET_LIMIT
+ * @see SolrParams#FACET_ZEROS
+ */
+ public NamedList getFacetTermEnumCounts(String fieldName)
+ throws IOException {
+
+ /* :TODO: potential optimization...
+ * cache the Terms with the highest docFreq and try them first
+ * don't enum if we get our max from them
+ */
+
+ IndexSchema schema = searcher.getSchema();
+ IndexReader r = searcher.getReader();
+ FieldType ft = schema.getFieldType(fieldName);
+
+ Set<CountPair<String,Integer>> counts
+ = new HashSet<CountPair<String,Integer>>();
+
+ String limit = params.getFieldParam(fieldName, params.FACET_LIMIT);
+ if (null != limit) {
+ counts = new BoundedTreeSet<CountPair<String,Integer>>
+ (Integer.parseInt(limit));
+ }
+
+ boolean zeros = params.getFieldBool(fieldName, params.FACET_ZEROS, true);
+
+ TermEnum te = r.terms(new Term(fieldName,""));
+ do {
+ Term t = te.term();
+
+ if (null == t || ! t.field().equals(fieldName))
+ break;
+
+ if (0 < te.docFreq()) { /* all docs may be deleted */
+ int count = searcher.numDocs(new TermQuery(t),
+ docs);
+
+ /* :TODO: is indexedToReadable correct? */
+ if (zeros || 0 < count)
+ counts.add(new CountPair<String,Integer>
+ (ft.indexedToReadable(t.text()), count));
+
+ }
+ } while (te.next());
+
+ NamedList res = new NamedList();
+ for (CountPair<String,Integer> p : counts) {
+ res.add(p.key, p.val);
+ }
+ return res;
+ }
+
+ /**
+ * A simple key=>val pair whose natural order is such that
+ * <b>higher</b> vals come before lower vals.
+ * In case of tie vals, then <b>lower</b> keys come before higher keys.
+ */
+ public static class CountPair<K extends Comparable<? super K>, V extends Comparable<? super V>>
+ implements Comparable<CountPair<K,V>> {
+
+ public CountPair(K k, V v) {
+ key = k; val = v;
+ }
+ public K key;
+ public V val;
+ public int hashCode() {
+ return key.hashCode() ^ val.hashCode();
+ }
+ public boolean equals(Object o) {
+ return (o instanceof CountPair)
+ && (0 == this.compareTo((CountPair<K,V>) o));
+ }
+ public int compareTo(CountPair<K,V> o) {
+ int vc = o.val.compareTo(val);
+ return (0 != vc ? vc : key.compareTo(o.key));
+ }
+ }
+}
+
Modified: incubator/solr/trunk/src/java/org/apache/solr/request/SolrParams.java
URL: http://svn.apache.org/viewvc/incubator/solr/trunk/src/java/org/apache/solr/request/SolrParams.java?view=diff&rev=441175&r1=441174&r2=441175
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/SolrParams.java (original)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/SolrParams.java Thu Sep 7 11:55:14 2006
@@ -37,6 +37,8 @@
public static final String WT ="wt";
/** query string */
public static final String Q ="q";
+ /** Lucene query string(s) for filtering the results without affecting scoring */
+ public static final String FQ ="fq";
/** zero based offset of matching documents to retrieve */
public static final String START ="start";
/** number of documents to return starting at "start" */
@@ -62,7 +64,38 @@
/** override default highlight Formatter class */
public static final String HIGHLIGHT_FORMATTER_CLASS = "highlightFormatterClass";
-
+ /**
+ * Should facet counts be calculated?
+ */
+ public static final String FACET = "facet";
+
+ /**
+ * Any lucene formated queries the user would like to use for
+ * Facet Contraint Counts (multi-value)
+ */
+ public static final String FACET_QUERY = "facet.query";
+ /**
+ * Any field whose terms the user wants to enumerate over for
+ * Facet Contraint Counts (multi-value)
+ */
+ public static final String FACET_FIELD = "facet.field";
+ /**
+ * Numeric option indicating the maximum number of facet field counts
+ * be included in the response for each field - in descending order of count.
+ * Can be overriden on a per field basis.
+ */
+ public static final String FACET_LIMIT = "facet.limit";
+ /**
+ * Boolean option indicating whether facet field counts of "0" should
+ * be included in the response. Can be overriden on a per field basis.
+ */
+ public static final String FACET_ZEROS = "facet.zeros";
+ /**
+ * Boolean option indicating whether the response should include a
+ * facet field count for all records which have no value for the
+ * facet field. Can be overriden on a per field basis.
+ */
+ public static final String FACET_MISSING = "facet.missing";
/** returns the String value of a param, or null if not set */
Modified: incubator/solr/trunk/src/java/org/apache/solr/request/StandardRequestHandler.java
URL: http://svn.apache.org/viewvc/incubator/solr/trunk/src/java/org/apache/solr/request/StandardRequestHandler.java?view=diff&rev=441175&r1=441174&r2=441175
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/request/StandardRequestHandler.java (original)
+++ incubator/solr/trunk/src/java/org/apache/solr/request/StandardRequestHandler.java Thu Sep 7 11:55:14 2006
@@ -18,6 +18,7 @@
import org.apache.lucene.search.*;
+import java.util.ArrayList;
import java.util.List;
import java.net.URL;
@@ -107,20 +108,46 @@
}
}
- DocList results = req.getSearcher().getDocList(query, null, sort, p.getInt(START,0), p.getInt(ROWS,10), flags);
- rsp.add(null,results);
+ DocListAndSet results = new DocListAndSet();
+ NamedList facetInfo = null;
+ List<Query> filters = U.parseFilterQueries(req);
+ SolrIndexSearcher s = req.getSearcher();
+
+ if (p.getBool(FACET,false)) {
+ results = s.getDocListAndSet(query, filters, sort,
+ p.getInt(START,0), p.getInt(ROWS,10),
+ flags);
+ facetInfo = getFacetInfo(req, rsp, results.docSet);
+ } else {
+ results.docList = s.getDocList(query, filters, sort,
+ p.getInt(START,0), p.getInt(ROWS,10),
+ flags);
+ }
+
+ rsp.add(null,results.docList);
+
+ if (null != facetInfo) rsp.add("facet_counts", facetInfo);
try {
- NamedList dbg = U.doStandardDebug(req, qs, query, results);
- if (null != dbg)
+ NamedList dbg = U.doStandardDebug(req, qs, query, results.docList);
+ if (null != dbg) {
+ if (null != filters) {
+ dbg.add("filter_queries",req.getParams().getParams(FQ));
+ List<String> fqs = new ArrayList<String>(filters.size());
+ for (Query fq : filters) {
+ fqs.add(QueryParsing.toString(fq, req.getSchema()));
+ }
+ dbg.add("parsed_filter_queries",fqs);
+ }
rsp.add("debug", dbg);
+ }
} catch (Exception e) {
SolrException.logOnce(SolrCore.log, "Exception durring debug", e);
rsp.add("exception_during_debug", SolrException.toStr(e));
}
NamedList sumData = HighlightingUtils.doHighlighting(
- results, query, req, new String[]{defaultField});
+ results.docList, query, req, new String[]{defaultField});
if(sumData != null)
rsp.add("highlighting", sumData);
@@ -135,6 +162,25 @@
return;
}
}
+
+ /**
+ * Fetches information about Facets for this request.
+ *
+ * Subclasses may with to override this method to provide more
+ * advanced faceting behavior.
+ * @see SimpleFacets#getFacetCounts
+ */
+ protected NamedList getFacetInfo(SolrQueryRequest req,
+ SolrQueryResponse rsp,
+ DocSet mainSet) {
+
+ SimpleFacets f = new SimpleFacets(req.getSearcher(),
+ mainSet,
+ req.getParams());
+ return f.getFacetCounts();
+ }
+
+
//////////////////////// SolrInfoMBeans methods //////////////////////
Added: incubator/solr/trunk/src/java/org/apache/solr/util/BoundedTreeSet.java
URL: http://svn.apache.org/viewvc/incubator/solr/trunk/src/java/org/apache/solr/util/BoundedTreeSet.java?view=auto&rev=441175
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/util/BoundedTreeSet.java (added)
+++ incubator/solr/trunk/src/java/org/apache/solr/util/BoundedTreeSet.java Thu Sep 7 11:55:14 2006
@@ -0,0 +1,67 @@
+/**
+ * Copyright 2006 The Apache Software Foundation
+ *
+ * Licensed 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.util;
+
+import java.util.*;
+
+/**
+ * A TreeSet that ensures it never grows beyond a max size.
+ * <code>last()</code> is removed if the <code>size()</code>
+ * get's bigger then <code>getMaxSize()</code>
+ */
+public class BoundedTreeSet<E> extends TreeSet<E> {
+ private int maxSize = Integer.MAX_VALUE;
+ public BoundedTreeSet(int maxSize) {
+ super();
+ this.setMaxSize(maxSize);
+ }
+ public BoundedTreeSet(int maxSize, Collection<? extends E> c) {
+ super(c);
+ this.setMaxSize(maxSize);
+ }
+ public BoundedTreeSet(int maxSize, Comparator<? super E> c) {
+ super(c);
+ this.setMaxSize(maxSize);
+ }
+ public BoundedTreeSet(int maxSize, SortedSet<E> s) {
+ super(s);
+ this.setMaxSize(maxSize);
+ }
+ public int getMaxSize() {
+ return maxSize;
+ }
+ public void setMaxSize(int max) {
+ maxSize = max;
+ adjust();
+ }
+ private void adjust() {
+ while (maxSize < size()) {
+ remove(last());
+ }
+ }
+ public boolean add(E item) {
+ boolean out = super.add(item);
+ adjust();
+ return out;
+ }
+ public boolean addAll(Collection<? extends E> c) {
+ boolean out = super.addAll(c);
+ adjust();
+ return out;
+ }
+}
Modified: incubator/solr/trunk/src/java/org/apache/solr/util/DisMaxParams.java
URL: http://svn.apache.org/viewvc/incubator/solr/trunk/src/java/org/apache/solr/util/DisMaxParams.java?view=diff&rev=441175&r1=441174&r2=441175
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/util/DisMaxParams.java (original)
+++ incubator/solr/trunk/src/java/org/apache/solr/util/DisMaxParams.java Thu Sep 7 11:55:14 2006
@@ -60,7 +60,9 @@
public static String BQ = "bq";
/** query and init param for boosting functions */
public static String BF = "bf";
- /** query and init param for filtering query */
+ /** query and init param for filtering query
+ * @deprecated use SolrParams.FQ or SolrPluginUtils.parseFilterQueries
+ */
public static String FQ = "fq";
/** query and init param for field list */
public static String GEN = "gen";
Modified: incubator/solr/trunk/src/java/org/apache/solr/util/SolrPluginUtils.java
URL: http://svn.apache.org/viewvc/incubator/solr/trunk/src/java/org/apache/solr/util/SolrPluginUtils.java?view=diff&rev=441175&r1=441174&r2=441175
==============================================================================
--- incubator/solr/trunk/src/java/org/apache/solr/util/SolrPluginUtils.java (original)
+++ incubator/solr/trunk/src/java/org/apache/solr/util/SolrPluginUtils.java Thu Sep 7 11:55:14 2006
@@ -719,6 +719,30 @@
return ss.getSort();
}
+ /**
+ * Builds a list of Query objects that should be used to filter results
+ * @see SolrParams#FQ
+ * @return null if no filter queries
+ */
+ public static List<Query> parseFilterQueries(SolrQueryRequest req) throws ParseException {
+ String[] in = req.getParams().getParams(SolrParams.FQ);
+
+ if (null == in || 0 == in.length) return null;
+
+ List<Query> out = new LinkedList<Query>();
+ SolrIndexSearcher s = req.getSearcher();
+ /* Ignore SolrParams.DF - could have init param FQs assuming the
+ * schema default with query param DF intented to only affect Q.
+ * If user doesn't want schema default, they should be explicit in the FQ.
+ */
+ SolrQueryParser qp = new SolrQueryParser(s.getSchema(), null);
+ for (String q : in) {
+ if (null != q && 0 != q.trim().length()) {
+ out.add(qp.parse(q));
+ }
+ }
+ return out;
+ }
/**
* A CacheRegenerator that can be used whenever the items in the cache
Modified: incubator/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java
URL: http://svn.apache.org/viewvc/incubator/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java?view=diff&rev=441175&r1=441174&r2=441175
==============================================================================
--- incubator/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java (original)
+++ incubator/solr/trunk/src/test/org/apache/solr/BasicFunctionalityTest.java Thu Sep 7 11:55:14 2006
@@ -167,7 +167,7 @@
DocumentBuilder builder = DocumentBuilderFactory.newInstance().newDocumentBuilder();
builder.parse(new ByteArrayInputStream
- (writer.toString().getBytes("UTF-8")));
+ (writer.toString().getBytes("UTF-8")));
}
public void testLocalSolrQueryRequestParams() {
@@ -319,7 +319,173 @@
}
+ public void testSimpleFacetCounts() {
+ assertU(adoc("id", "42", "trait_s", "Tool", "trait_s", "Obnoxious",
+ "name", "Zapp Brannigan"));
+ assertU(adoc("id", "43" ,
+ "title", "Democratic Order of Planets"));
+ assertU(adoc("id", "44", "trait_s", "Tool",
+ "name", "The Zapper"));
+ assertU(adoc("id", "45", "trait_s", "Chauvinist",
+ "title", "25 star General"));
+ assertU(adoc("id", "46", "trait_s", "Obnoxious",
+ "subject", "Defeated the pacifists of the Gandhi nebula"));
+ assertU(adoc("id", "47", "trait_s", "Pig",
+ "text", "line up and fly directly at the enemy death cannons, clogging them with wreckage!"));
+ assertU(commit());
+
+ assertQ("standard request handler returns all matches",
+ req("id:[42 TO 47]"),
+ "*[count(//doc)=6]"
+ );
+
+ assertQ("filter results using fq",
+ req("q","id:[42 TO 46]",
+ "fq", "id:[43 TO 47]"),
+ "*[count(//doc)=4]"
+ );
+
+ assertQ("don't filter results using blank fq",
+ req("q","id:[42 TO 46]",
+ "fq", " "),
+ "*[count(//doc)=5]"
+ );
+
+ assertQ("filter results using multiple fq params",
+ req("q","id:[42 TO 46]",
+ "fq", "trait_s:Obnoxious",
+ "fq", "id:[43 TO 47]"),
+ "*[count(//doc)=1]"
+ );
+
+ assertQ("check counts for facet queries",
+ req("q", "id:[42 TO 47]"
+ ,"facet", "true"
+ ,"facet.query", "trait_s:Obnoxious"
+ ,"facet.query", "id:[42 TO 45]"
+ ,"facet.query", "id:[43 TO 47]"
+ ,"facet.field", "trait_s"
+ )
+ ,"*[count(//doc)=6]"
+
+ ,"//lst[@name='facet_counts']/lst[@name='facet_queries']"
+ ,"//lst[@name='facet_queries']/int[@name='trait_s:Obnoxious'][.='2']"
+ ,"//lst[@name='facet_queries']/int[@name='id:[42 TO 45]'][.='4']"
+ ,"//lst[@name='facet_queries']/int[@name='id:[43 TO 47]'][.='5']"
+
+ ,"//lst[@name='facet_counts']/lst[@name='facet_fields']"
+ ,"//lst[@name='facet_fields']/lst[@name='trait_s']"
+ ,"*[count(//lst[@name='trait_s']/int)=4]"
+ ,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
+ ,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='2']"
+ ,"//lst[@name='trait_s']/int[@name='Pig'][.='1']"
+ );
+
+ assertQ("check counts for applied facet queries using filtering (fq)",
+ req("q", "id:[42 TO 47]"
+ ,"facet", "true"
+ ,"fq", "id:[42 TO 45]"
+ ,"facet.field", "trait_s"
+ ,"facet.query", "id:[42 TO 45]"
+ ,"facet.query", "id:[43 TO 47]"
+ )
+ ,"*[count(//doc)=4]"
+ ,"//lst[@name='facet_counts']/lst[@name='facet_queries']"
+ ,"//lst[@name='facet_queries']/int[@name='id:[42 TO 45]'][.='4']"
+ ,"//lst[@name='facet_queries']/int[@name='id:[43 TO 47]'][.='3']"
+ ,"*[count(//lst[@name='trait_s']/int)=4]"
+ ,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
+ ,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='1']"
+ ,"//lst[@name='trait_s']/int[@name='Chauvinist'][.='1']"
+ ,"//lst[@name='trait_s']/int[@name='Pig'][.='0']"
+ );
+
+ assertQ("check counts with facet.zero=false&facet.missing=true using fq",
+ req("q", "id:[42 TO 47]"
+ ,"facet", "true"
+ ,"facet.zeros", "false"
+ ,"f.trait_s.facet.missing", "true"
+ ,"fq", "id:[42 TO 45]"
+ ,"facet.field", "trait_s"
+ )
+ ,"*[count(//doc)=4]"
+ ,"*[count(//lst[@name='trait_s']/int)=4]"
+ ,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
+ ,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='1']"
+ ,"//lst[@name='trait_s']/int[@name='Chauvinist'][.='1']"
+ ,"//lst[@name='trait_s']/int[not(@name)][.='1']"
+ );
+
+ }
+
+ public void testSimpleFacetCountsWithLimits() {
+ assertU(adoc("id", "1", "t_s", "A"));
+ assertU(adoc("id", "2", "t_s", "B"));
+ assertU(adoc("id", "3", "t_s", "C"));
+ assertU(adoc("id", "4", "t_s", "C"));
+ assertU(adoc("id", "5", "t_s", "D"));
+ assertU(adoc("id", "6", "t_s", "E"));
+ assertU(adoc("id", "7", "t_s", "E"));
+ assertU(adoc("id", "8", "t_s", "E"));
+ assertU(adoc("id", "9", "t_s", "F"));
+ assertU(adoc("id", "10", "t_s", "G"));
+ assertU(adoc("id", "11", "t_s", "G"));
+ assertU(adoc("id", "12", "t_s", "G"));
+ assertU(adoc("id", "13", "t_s", "G"));
+ assertU(adoc("id", "14", "t_s", "G"));
+ assertU(commit());
+
+ assertQ("check counts for unlimited facet",
+ req("q", "id:[* TO *]"
+ ,"facet", "true"
+ ,"facet.field", "t_s"
+ )
+ ,"*[count(//lst[@name='facet_fields']/lst[@name='t_s']/int)=7]"
+
+ ,"//lst[@name='t_s']/int[@name='G'][.='5']"
+ ,"//lst[@name='t_s']/int[@name='E'][.='3']"
+ ,"//lst[@name='t_s']/int[@name='C'][.='2']"
+
+ ,"//lst[@name='t_s']/int[@name='A'][.='1']"
+ ,"//lst[@name='t_s']/int[@name='B'][.='1']"
+ ,"//lst[@name='t_s']/int[@name='D'][.='1']"
+ ,"//lst[@name='t_s']/int[@name='F'][.='1']"
+ );
+
+ assertQ("check counts for facet with generous limit",
+ req("q", "id:[* TO *]"
+ ,"facet", "true"
+ ,"facet.limit", "100"
+ ,"facet.field", "t_s"
+ )
+ ,"*[count(//lst[@name='facet_fields']/lst[@name='t_s']/int)=7]"
+
+ ,"//lst[@name='t_s']/int[1][@name='G'][.='5']"
+ ,"//lst[@name='t_s']/int[2][@name='E'][.='3']"
+ ,"//lst[@name='t_s']/int[3][@name='C'][.='2']"
+
+ ,"//lst[@name='t_s']/int[@name='A'][.='1']"
+ ,"//lst[@name='t_s']/int[@name='B'][.='1']"
+ ,"//lst[@name='t_s']/int[@name='D'][.='1']"
+ ,"//lst[@name='t_s']/int[@name='F'][.='1']"
+ );
+
+ assertQ("check counts for limited facet",
+ req("q", "id:[* TO *]"
+ ,"facet", "true"
+ ,"facet.limit", "2"
+ ,"facet.field", "t_s"
+ )
+ ,"*[count(//lst[@name='facet_fields']/lst[@name='t_s']/int)=2]"
+
+ ,"//lst[@name='t_s']/int[1][@name='G'][.='5']"
+ ,"//lst[@name='t_s']/int[2][@name='E'][.='3']"
+ );
+
+ }
+
+
private String mkstr(int len) {
StringBuilder sb = new StringBuilder(len);
for (int i = 0; i < len; i++) {
Modified: incubator/solr/trunk/src/test/org/apache/solr/DisMaxRequestHandlerTest.java
URL: http://svn.apache.org/viewvc/incubator/solr/trunk/src/test/org/apache/solr/DisMaxRequestHandlerTest.java?view=diff&rev=441175&r1=441174&r2=441175
==============================================================================
--- incubator/solr/trunk/src/test/org/apache/solr/DisMaxRequestHandlerTest.java (original)
+++ incubator/solr/trunk/src/test/org/apache/solr/DisMaxRequestHandlerTest.java Thu Sep 7 11:55:14 2006
@@ -39,31 +39,39 @@
public void setUp() throws Exception {
super.setUp();
lrf = h.getRequestFactory
- ("dismax",0,20,"version","2.0");
+ ("dismax", 0, 20,
+ "version","2.0",
+ "facet", "true",
+ "facet.field","t_s"
+ );
}
public void testSomeStuff() throws Exception {
assertU(adoc("id", "666",
"features_t", "cool and scary stuff",
"subject", "traveling in hell",
+ "t_s", "movie",
"title", "The Omen",
"weight", "87.9",
"iind", "666"));
assertU(adoc("id", "42",
"features_t", "cool stuff",
"subject", "traveling the galaxy",
+ "t_s", "movie", "t_s", "book",
"title", "Hitch Hiker's Guide to the Galaxy",
"weight", "99.45",
"iind", "42"));
assertU(adoc("id", "1",
"features_t", "nothing",
"subject", "garbage",
+ "t_s", "book",
"title", "Most Boring Guide Ever",
"weight", "77",
"iind", "4"));
assertU(adoc("id", "8675309",
"features_t", "Wikedly memorable chorus and stuff",
"subject", "One Cool Hot Chick",
+ "t_s", "song",
"title", "Jenny",
"weight", "97.3",
"iind", "8675309"));
@@ -72,6 +80,10 @@
assertQ("basic match",
req("guide")
,"//*[@numFound='2']"
+ ,"//lst[@name='facet_fields']/lst[@name='t_s']"
+ ,"*[count(//lst[@name='t_s']/int)=3]"
+ ,"//lst[@name='t_s']/int[@name='book'][.='2']"
+ ,"//lst[@name='t_s']/int[@name='movie'][.='1']"
);
assertQ("basic cross field matching, boost on same field matching",
@@ -94,12 +106,17 @@
,"//*[@numFound='3']"
);
+
}
public void testOldStyleDefaults() throws Exception {
lrf = h.getRequestFactory
- ("dismaxOldStyleDefaults",0,20,"version","2.0");
+ ("dismax", 0, 20,
+ "version","2.0",
+ "facet", "true",
+ "facet.field","t_s"
+ );
testSomeStuff();
}