mirror of https://github.com/apache/lucene.git
SOLR-44 - simple facet support for StandardRequestHandler and DisMaxRequestHandler
git-svn-id: https://svn.apache.org/repos/asf/incubator/solr/trunk@441175 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
74f787bede
commit
447ddf06ea
|
@ -46,6 +46,9 @@ New Features
|
|||
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 @@ Changes in runtime behavior
|
|||
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
|
||||
|
|
|
@ -22,6 +22,8 @@ import org.apache.solr.core.SolrException;
|
|||
|
||||
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 @@ import java.net.URL;
|
|||
* <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 @@ import java.net.URL;
|
|||
* </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 @@ public class DisMaxRequestHandler
|
|||
|
||||
/* * * 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 @@ public class DisMaxRequestHandler
|
|||
|
||||
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 @@ public class DisMaxRequestHandler
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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();
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,6 +37,8 @@ public abstract class SolrParams {
|
|||
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 @@ public abstract class SolrParams {
|
|||
/** 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 */
|
||||
|
|
|
@ -18,6 +18,7 @@ package org.apache.solr.request;
|
|||
|
||||
import org.apache.lucene.search.*;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.net.URL;
|
||||
|
||||
|
@ -107,20 +108,46 @@ public class StandardRequestHandler implements SolrRequestHandler, SolrInfoMBean
|
|||
}
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
|
@ -136,6 +163,25 @@ public class StandardRequestHandler implements SolrRequestHandler, SolrInfoMBean
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 //////////////////////
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -60,7 +60,9 @@ import java.io.IOException;
|
|||
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";
|
||||
|
|
|
@ -719,6 +719,30 @@ public class SolrPluginUtils {
|
|||
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
|
||||
|
|
|
@ -167,7 +167,7 @@ public class BasicFunctionalityTest extends AbstractSolrTestCase {
|
|||
|
||||
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 class BasicFunctionalityTest extends AbstractSolrTestCase {
|
|||
|
||||
}
|
||||
|
||||
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++) {
|
||||
|
|
|
@ -39,31 +39,39 @@ public class DisMaxRequestHandlerTest extends AbstractSolrTestCase {
|
|||
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 @@ public class DisMaxRequestHandlerTest extends AbstractSolrTestCase {
|
|||
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 @@ public class DisMaxRequestHandlerTest extends AbstractSolrTestCase {
|
|||
,"//*[@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();
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue