mirror of https://github.com/apache/lucene.git
facet.mincount,facet.offset,facet.sort params: SOLR-106
git-svn-id: https://svn.apache.org/repos/asf/incubator/solr/trunk@496811 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
ebfafc6d63
commit
b9e804e032
|
@ -37,17 +37,24 @@ Detailed Change List
|
||||||
New Features
|
New Features
|
||||||
1. SOLR-82: Default field values can be specified in the schema.xml.
|
1. SOLR-82: Default field values can be specified in the schema.xml.
|
||||||
(Ryan McKinley via hossman)
|
(Ryan McKinley via hossman)
|
||||||
|
|
||||||
2. SOLR-89: Two new TokenFilters with corresponding Factories...
|
2. SOLR-89: Two new TokenFilters with corresponding Factories...
|
||||||
* TrimFilter - Trims leading and trailing whitespace from Tokens
|
* TrimFilter - Trims leading and trailing whitespace from Tokens
|
||||||
* PatternReplaceFilter - applies a Pattern to each token in the
|
* PatternReplaceFilter - applies a Pattern to each token in the
|
||||||
stream, replacing match occurances with a specified replacement.
|
stream, replacing match occurances with a specified replacement.
|
||||||
(hossman)
|
(hossman)
|
||||||
|
|
||||||
3. SOLR-91: allow configuration of a limit of the number of searchers
|
3. SOLR-91: allow configuration of a limit of the number of searchers
|
||||||
that can be warming in the background. This can be used to avoid
|
that can be warming in the background. This can be used to avoid
|
||||||
out-of-memory errors, or contention caused by more and more searchers
|
out-of-memory errors, or contention caused by more and more searchers
|
||||||
warming in the background. An error is thrown if the limit specified
|
warming in the background. An error is thrown if the limit specified
|
||||||
by maxWarmingSearchers in solrconfig.xml is exceeded. (yonik)
|
by maxWarmingSearchers in solrconfig.xml is exceeded. (yonik)
|
||||||
|
|
||||||
|
4. SOLR-106: New faceting parameters that allow specification of a
|
||||||
|
minimum count for returned facets (facet.mincount), paging through facets
|
||||||
|
(facet.offset, facet.limit), and explicit sorting (facet.sort).
|
||||||
|
facet.zeros is now deprecated. (yonik)
|
||||||
|
|
||||||
Changes in runtime behavior
|
Changes in runtime behavior
|
||||||
1. Highlighting using DisMax will only pick up terms from the main
|
1. Highlighting using DisMax will only pick up terms from the main
|
||||||
user query, not boost or filter queries (klaas).
|
user query, not boost or filter queries (klaas).
|
||||||
|
@ -58,9 +65,11 @@ Optimizations
|
||||||
Bug Fixes
|
Bug Fixes
|
||||||
1. SOLR-87: Parsing of synonym files did not correctly handle escaped
|
1. SOLR-87: Parsing of synonym files did not correctly handle escaped
|
||||||
whitespace such as \r\n\t\b\f. (yonik)
|
whitespace such as \r\n\t\b\f. (yonik)
|
||||||
|
|
||||||
2. SOLR-92: DOMUtils.getText (used when parsing config files) did not
|
2. SOLR-92: DOMUtils.getText (used when parsing config files) did not
|
||||||
work properly with many DOM implementations when dealing with
|
work properly with many DOM implementations when dealing with
|
||||||
"Attributes". (Ryan McKinley via hossman)
|
"Attributes". (Ryan McKinley via hossman)
|
||||||
|
|
||||||
3. SOLR-9,SOLR-99: Tighten up sort specification error checking, throw
|
3. SOLR-9,SOLR-99: Tighten up sort specification error checking, throw
|
||||||
exceptions for missing sort specifications or a sort on a non-indexed
|
exceptions for missing sort specifications or a sort on a non-indexed
|
||||||
field. (Ryan McKinley via yonik)
|
field. (Ryan McKinley via yonik)
|
||||||
|
|
|
@ -20,10 +20,12 @@ package org.apache.solr.request;
|
||||||
import org.apache.lucene.index.Term;
|
import org.apache.lucene.index.Term;
|
||||||
import org.apache.lucene.index.TermEnum;
|
import org.apache.lucene.index.TermEnum;
|
||||||
import org.apache.lucene.index.IndexReader;
|
import org.apache.lucene.index.IndexReader;
|
||||||
|
import org.apache.lucene.index.TermDocs;
|
||||||
import org.apache.lucene.queryParser.ParseException;
|
import org.apache.lucene.queryParser.ParseException;
|
||||||
import org.apache.lucene.search.*;
|
import org.apache.lucene.search.*;
|
||||||
import org.apache.solr.core.SolrCore;
|
import org.apache.solr.core.SolrCore;
|
||||||
import org.apache.solr.core.SolrException;
|
import org.apache.solr.core.SolrException;
|
||||||
|
import org.apache.solr.core.SolrConfig;
|
||||||
import org.apache.solr.request.SolrParams;
|
import org.apache.solr.request.SolrParams;
|
||||||
import org.apache.solr.schema.IndexSchema;
|
import org.apache.solr.schema.IndexSchema;
|
||||||
import org.apache.solr.schema.FieldType;
|
import org.apache.solr.schema.FieldType;
|
||||||
|
@ -118,20 +120,29 @@ public class SimpleFacets {
|
||||||
|
|
||||||
|
|
||||||
public NamedList getTermCounts(String field) throws IOException {
|
public NamedList getTermCounts(String field) throws IOException {
|
||||||
|
int offset = params.getFieldInt(field, params.FACET_OFFSET, 0);
|
||||||
int limit = params.getFieldInt(field, params.FACET_LIMIT, 100);
|
int limit = params.getFieldInt(field, params.FACET_LIMIT, 100);
|
||||||
boolean zeros = params.getFieldBool(field, params.FACET_ZEROS, true);
|
Integer mincount = params.getFieldInt(field, params.FACET_MINCOUNT);
|
||||||
|
if (mincount==null) {
|
||||||
|
Boolean zeros = params.getFieldBool(field, params.FACET_ZEROS);
|
||||||
|
// mincount = (zeros!=null && zeros) ? 0 : 1;
|
||||||
|
mincount = (zeros!=null && !zeros) ? 1 : 0;
|
||||||
|
// current default is to include zeros.
|
||||||
|
}
|
||||||
boolean missing = params.getFieldBool(field, params.FACET_MISSING, false);
|
boolean missing = params.getFieldBool(field, params.FACET_MISSING, false);
|
||||||
|
// default to sorting if there is a limit.
|
||||||
|
boolean sort = params.getFieldBool(field, params.FACET_SORT, limit>0);
|
||||||
|
|
||||||
NamedList counts;
|
NamedList counts;
|
||||||
SchemaField sf = searcher.getSchema().getField(field);
|
SchemaField sf = searcher.getSchema().getField(field);
|
||||||
FieldType ft = sf.getType();
|
FieldType ft = sf.getType();
|
||||||
if (sf.multiValued() || ft.isTokenized() || ft instanceof BoolField) {
|
if (sf.multiValued() || ft.isTokenized() || ft instanceof BoolField) {
|
||||||
// Always use filters for booleans... we know the number of values is very small.
|
// Always use filters for booleans... we know the number of values is very small.
|
||||||
counts = getFacetTermEnumCounts(searcher,docs,field,limit,zeros,missing);
|
counts = getFacetTermEnumCounts(searcher, docs, field, offset, limit, mincount,missing,sort);
|
||||||
} else {
|
} else {
|
||||||
// TODO: future logic could use filters instead of the fieldcache if
|
// TODO: future logic could use filters instead of the fieldcache if
|
||||||
// the number of terms in the field is small enough.
|
// the number of terms in the field is small enough.
|
||||||
counts = getFieldCacheCounts(searcher, docs, field, limit, zeros, missing);
|
counts = getFieldCacheCounts(searcher, docs, field, offset,limit, mincount, missing, sort);
|
||||||
}
|
}
|
||||||
|
|
||||||
return counts;
|
return counts;
|
||||||
|
@ -177,7 +188,7 @@ public class SimpleFacets {
|
||||||
* Use the Lucene FieldCache to get counts for each unique field value in <code>docs</code>.
|
* Use the Lucene FieldCache to get counts for each unique field value in <code>docs</code>.
|
||||||
* The field must have at most one indexed token per document.
|
* The field must have at most one indexed token per document.
|
||||||
*/
|
*/
|
||||||
public static NamedList getFieldCacheCounts(SolrIndexSearcher searcher, DocSet docs, String fieldName, int limit, boolean zeros, boolean missing) throws IOException {
|
public static NamedList getFieldCacheCounts(SolrIndexSearcher searcher, DocSet docs, String fieldName, int offset, int limit, int mincount, boolean missing, boolean sort) throws IOException {
|
||||||
// TODO: If the number of terms is high compared to docs.size(), and zeros==false,
|
// TODO: If the number of terms is high compared to docs.size(), and zeros==false,
|
||||||
// we should use an alternate strategy to avoid
|
// we should use an alternate strategy to avoid
|
||||||
// 1) creating another huge int[] for the counts
|
// 1) creating another huge int[] for the counts
|
||||||
|
@ -188,7 +199,7 @@ public class SimpleFacets {
|
||||||
//
|
//
|
||||||
|
|
||||||
FieldCache.StringIndex si = FieldCache.DEFAULT.getStringIndex(searcher.getReader(), fieldName);
|
FieldCache.StringIndex si = FieldCache.DEFAULT.getStringIndex(searcher.getReader(), fieldName);
|
||||||
int[] count = new int[si.lookup.length];
|
final int[] count = new int[si.lookup.length];
|
||||||
DocIterator iter = docs.iterator();
|
DocIterator iter = docs.iterator();
|
||||||
while (iter.hasNext()) {
|
while (iter.hasNext()) {
|
||||||
count[si.order[iter.nextDoc()]]++;
|
count[si.order[iter.nextDoc()]]++;
|
||||||
|
@ -200,42 +211,51 @@ public class SimpleFacets {
|
||||||
// IDEA: we could also maintain a count of "other"... everything that fell outside
|
// IDEA: we could also maintain a count of "other"... everything that fell outside
|
||||||
// of the top 'N'
|
// of the top 'N'
|
||||||
|
|
||||||
BoundedTreeSet<CountPair<String,Integer>> queue=null;
|
int off=offset;
|
||||||
|
int lim=limit>=0 ? limit : Integer.MAX_VALUE;
|
||||||
|
|
||||||
if (limit>=0) {
|
if (sort) {
|
||||||
// TODO: compare performance of BoundedTreeSet compare against priority queue?
|
// TODO: compare performance of BoundedTreeSet compare against priority queue?
|
||||||
queue = new BoundedTreeSet<CountPair<String,Integer>>(limit);
|
int maxsize = limit>0 ? offset+limit : Integer.MAX_VALUE-1;
|
||||||
}
|
final BoundedTreeSet<CountPair<String,Integer>> queue = new BoundedTreeSet<CountPair<String,Integer>>(maxsize);
|
||||||
|
int min=mincount-1; // the smallest value in the top 'N' values
|
||||||
int min=-1; // the smallest value in the top 'N' values
|
for (int i=1; i<count.length; i++) {
|
||||||
for (int i=1; i<count.length; i++) {
|
int c = count[i];
|
||||||
int c = count[i];
|
if (c>min) {
|
||||||
if (c==0 && !zeros) continue;
|
// NOTE: we use c>min rather than c>=min as an optimization because we are going in
|
||||||
if (limit<0) {
|
// index order, so we already know that the keys are ordered. This can be very
|
||||||
res.add(ft.indexedToReadable(si.lookup[i]), c);
|
// important if a lot of the counts are repeated (like zero counts would be).
|
||||||
} else if (c>min) {
|
queue.add(new CountPair<String,Integer>(ft.indexedToReadable(si.lookup[i]), c));
|
||||||
// NOTE: we use c>min rather than c>=min as an optimization because we are going in
|
if (queue.size()>=maxsize) min=queue.last().val;
|
||||||
// index order, so we already know that the keys are ordered. This can be very
|
}
|
||||||
// important if a lot of the counts are repeated (like zero counts would be).
|
|
||||||
queue.add(new CountPair<String,Integer>(ft.indexedToReadable(si.lookup[i]), c));
|
|
||||||
if (queue.size()>=limit) min=queue.last().val;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (limit>=0) {
|
|
||||||
for (CountPair<String,Integer> p : queue) {
|
for (CountPair<String,Integer> p : queue) {
|
||||||
|
if (--off>=0) continue;
|
||||||
|
if (--lim<0) break;
|
||||||
res.add(p.key, p.val);
|
res.add(p.key, p.val);
|
||||||
}
|
}
|
||||||
|
} else if (mincount<=0) {
|
||||||
|
// This is an optimization... if mincount<=0 and we aren't sorting then
|
||||||
|
// we know exactly where to start and end in the fieldcache.
|
||||||
|
for (int i=offset+1; i<offset+1+limit; i++) {
|
||||||
|
res.add(ft.indexedToReadable(si.lookup[i]),count[i]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (int i=1; i<count.length; i++) {
|
||||||
|
int c = count[i];
|
||||||
|
if (c<mincount || --off>=0) continue;
|
||||||
|
if (--lim<0) break;
|
||||||
|
res.add(ft.indexedToReadable(si.lookup[i]), c);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (missing) res.add(null, count[0]);
|
if (missing) res.add(null, count[0]);
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of terms in the specified field along with the
|
* Returns a list of terms in the specified field along with the
|
||||||
* corrisponding count of documents in the set that match that constraint.
|
* corresponding count of documents in the set that match that constraint.
|
||||||
* This method uses the FilterCache to get the intersection count between <code>docs</code>
|
* This method uses the FilterCache to get the intersection count between <code>docs</code>
|
||||||
* and the DocSet for each term in the filter.
|
* and the DocSet for each term in the filter.
|
||||||
*
|
*
|
||||||
|
@ -243,7 +263,7 @@ public class SimpleFacets {
|
||||||
* @see SolrParams#FACET_ZEROS
|
* @see SolrParams#FACET_ZEROS
|
||||||
* @see SolrParams#FACET_MISSING
|
* @see SolrParams#FACET_MISSING
|
||||||
*/
|
*/
|
||||||
public NamedList getFacetTermEnumCounts(SolrIndexSearcher searcher, DocSet docs, String field, int limit, boolean zeros, boolean missing)
|
public NamedList getFacetTermEnumCounts(SolrIndexSearcher searcher, DocSet docs, String field, int offset, int limit, int mincount, boolean missing, boolean sort)
|
||||||
throws IOException {
|
throws IOException {
|
||||||
|
|
||||||
/* :TODO: potential optimization...
|
/* :TODO: potential optimization...
|
||||||
|
@ -255,13 +275,13 @@ public class SimpleFacets {
|
||||||
IndexReader r = searcher.getReader();
|
IndexReader r = searcher.getReader();
|
||||||
FieldType ft = schema.getFieldType(field);
|
FieldType ft = schema.getFieldType(field);
|
||||||
|
|
||||||
Set<CountPair<String,Integer>> counts
|
final int maxsize = limit>=0 ? offset+limit : Integer.MAX_VALUE-1;
|
||||||
= new HashSet<CountPair<String,Integer>>();
|
final BoundedTreeSet<CountPair<String,Integer>> queue = sort ? new BoundedTreeSet<CountPair<String,Integer>>(maxsize) : null;
|
||||||
|
final NamedList res = new NamedList();
|
||||||
if (0 <= limit) {
|
|
||||||
counts = new BoundedTreeSet<CountPair<String,Integer>>(limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
int min=mincount-1; // the smallest value in the top 'N' values
|
||||||
|
int off=offset;
|
||||||
|
int lim=limit>=0 ? limit : Integer.MAX_VALUE;
|
||||||
TermEnum te = r.terms(new Term(field,""));
|
TermEnum te = r.terms(new Term(field,""));
|
||||||
do {
|
do {
|
||||||
Term t = te.term();
|
Term t = te.term();
|
||||||
|
@ -269,26 +289,37 @@ public class SimpleFacets {
|
||||||
if (null == t || ! t.field().equals(field))
|
if (null == t || ! t.field().equals(field))
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (0 < te.docFreq()) { /* all docs may be deleted */
|
int df = te.docFreq();
|
||||||
int count = searcher.numDocs(new TermQuery(t),
|
|
||||||
docs);
|
|
||||||
|
|
||||||
if (zeros || 0 < count)
|
if (df>0) { /* check df since all docs may be deleted */
|
||||||
counts.add(new CountPair<String,Integer>
|
int c = searcher.numDocs(new TermQuery(t), docs);
|
||||||
(t.text(), count));
|
|
||||||
|
|
||||||
|
if (sort) {
|
||||||
|
if (c>min) {
|
||||||
|
queue.add(new CountPair<String,Integer>(t.text(), c));
|
||||||
|
if (queue.size()>=maxsize) min=queue.last().val;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (c >= mincount && --off<0) {
|
||||||
|
if (--lim<0) break;
|
||||||
|
res.add(ft.indexedToReadable(t.text()), c);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} while (te.next());
|
} while (te.next());
|
||||||
|
|
||||||
NamedList res = new NamedList();
|
if (sort) {
|
||||||
for (CountPair<String,Integer> p : counts) {
|
for (CountPair<String,Integer> p : queue) {
|
||||||
res.add(ft.indexedToReadable(p.key), p.val);
|
if (--off>=0) continue;
|
||||||
|
if (--lim<0) break;
|
||||||
|
res.add(ft.indexedToReadable(p.key), p.val);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (missing) {
|
if (missing) {
|
||||||
res.add(null, getFieldMissingCount(searcher,docs,field));
|
res.add(null, getFieldMissingCount(searcher,docs,field));
|
||||||
}
|
}
|
||||||
|
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -83,17 +83,32 @@ public abstract class SolrParams {
|
||||||
* Facet Contraint Counts (multi-value)
|
* Facet Contraint Counts (multi-value)
|
||||||
*/
|
*/
|
||||||
public static final String FACET_FIELD = "facet.field";
|
public static final String FACET_FIELD = "facet.field";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The offset into the list of facets.
|
||||||
|
* Can be overriden on a per field basis.
|
||||||
|
*/
|
||||||
|
public static final String FACET_OFFSET = "facet.offset";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Numeric option indicating the maximum number of facet field counts
|
* Numeric option indicating the maximum number of facet field counts
|
||||||
* be included in the response for each field - in descending order of count.
|
* be included in the response for each field - in descending order of count.
|
||||||
* Can be overriden on a per field basis.
|
* Can be overriden on a per field basis.
|
||||||
*/
|
*/
|
||||||
public static final String FACET_LIMIT = "facet.limit";
|
public static final String FACET_LIMIT = "facet.limit";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Numeric option indicating the minimum number of hits before a facet should
|
||||||
|
* be included in the response. Can be overriden on a per field basis.
|
||||||
|
*/
|
||||||
|
public static final String FACET_MINCOUNT = "facet.mincount";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Boolean option indicating whether facet field counts of "0" should
|
* Boolean option indicating whether facet field counts of "0" should
|
||||||
* be included in the response. Can be overriden on a per field basis.
|
* be included in the response. Can be overriden on a per field basis.
|
||||||
*/
|
*/
|
||||||
public static final String FACET_ZEROS = "facet.zeros";
|
public static final String FACET_ZEROS = "facet.zeros";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Boolean option indicating whether the response should include a
|
* Boolean option indicating whether the response should include a
|
||||||
* facet field count for all records which have no value for the
|
* facet field count for all records which have no value for the
|
||||||
|
@ -101,6 +116,11 @@ public abstract class SolrParams {
|
||||||
*/
|
*/
|
||||||
public static final String FACET_MISSING = "facet.missing";
|
public static final String FACET_MISSING = "facet.missing";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Boolean option: true causes facets to be sorted
|
||||||
|
* by the count, false results in natural index order.
|
||||||
|
*/
|
||||||
|
public static final String FACET_SORT = "facet.sort";
|
||||||
|
|
||||||
/** returns the String value of a param, or null if not set */
|
/** returns the String value of a param, or null if not set */
|
||||||
public abstract String get(String param);
|
public abstract String get(String param);
|
||||||
|
@ -166,6 +186,13 @@ public abstract class SolrParams {
|
||||||
String val = get(param);
|
String val = get(param);
|
||||||
return val==null ? def : Integer.parseInt(val);
|
return val==null ? def : Integer.parseInt(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the int value of the field param,
|
||||||
|
or the value for param, or def if neither is set. */
|
||||||
|
public Integer getFieldInt(String field, String param) {
|
||||||
|
String val = getFieldParam(field, param);
|
||||||
|
return val==null ? null : Integer.parseInt(val);
|
||||||
|
}
|
||||||
|
|
||||||
/** Returns the int value of the field param,
|
/** Returns the int value of the field param,
|
||||||
or the value for param, or def if neither is set. */
|
or the value for param, or def if neither is set. */
|
||||||
|
@ -174,6 +201,7 @@ public abstract class SolrParams {
|
||||||
return val==null ? def : Integer.parseInt(val);
|
return val==null ? def : Integer.parseInt(val);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Returns the Float value of the param, or null if not set */
|
/** Returns the Float value of the param, or null if not set */
|
||||||
public Float getFloat(String param) {
|
public Float getFloat(String param) {
|
||||||
String val = get(param);
|
String val = get(param);
|
||||||
|
|
|
@ -240,7 +240,7 @@ public class BasicFunctionalityTest extends AbstractSolrTestCase {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @see TestRemoveDuplicatesTokenFilter */
|
/** @see org.apache.solr.analysis.TestRemoveDuplicatesTokenFilter */
|
||||||
public void testRemoveDuplicatesTokenFilter() {
|
public void testRemoveDuplicatesTokenFilter() {
|
||||||
Query q = QueryParsing.parseQuery("TV", "dedup",
|
Query q = QueryParsing.parseQuery("TV", "dedup",
|
||||||
h.getCore().getSchema());
|
h.getCore().getSchema());
|
||||||
|
@ -508,76 +508,240 @@ public class BasicFunctionalityTest extends AbstractSolrTestCase {
|
||||||
,"//lst[@name='trait_s']/int[@name='Chauvinist'][.='1']"
|
,"//lst[@name='trait_s']/int[@name='Chauvinist'][.='1']"
|
||||||
,"//lst[@name='trait_s']/int[not(@name)][.='1']"
|
,"//lst[@name='trait_s']/int[not(@name)][.='1']"
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assertQ("check counts with facet.mincount=1&facet.missing=true using fq",
|
||||||
|
req("q", "id:[42 TO 47]"
|
||||||
|
,"facet", "true"
|
||||||
|
,"facet.mincount", "1"
|
||||||
|
,"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']"
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("check counts with facet.mincount=2&facet.missing=true using fq",
|
||||||
|
req("q", "id:[42 TO 47]"
|
||||||
|
,"facet", "true"
|
||||||
|
,"facet.mincount", "2"
|
||||||
|
,"f.trait_s.facet.missing", "true"
|
||||||
|
,"fq", "id:[42 TO 45]"
|
||||||
|
,"facet.field", "trait_s"
|
||||||
|
)
|
||||||
|
,"*[count(//doc)=4]"
|
||||||
|
,"*[count(//lst[@name='trait_s']/int)=2]"
|
||||||
|
,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
|
||||||
|
,"//lst[@name='trait_s']/int[not(@name)][.='1']"
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("check sorted paging",
|
||||||
|
req("q", "id:[42 TO 47]"
|
||||||
|
,"facet", "true"
|
||||||
|
,"fq", "id:[42 TO 45]"
|
||||||
|
,"facet.field", "trait_s"
|
||||||
|
,"facet.mincount","0"
|
||||||
|
,"facet.offset","0"
|
||||||
|
,"facet.limit","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[@name='Pig'][.='0']"
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("check sorted paging",
|
||||||
|
req("q", "id:[42 TO 47]"
|
||||||
|
,"facet", "true"
|
||||||
|
,"fq", "id:[42 TO 45]"
|
||||||
|
,"facet.field", "trait_s"
|
||||||
|
,"facet.mincount","0"
|
||||||
|
,"facet.offset","0"
|
||||||
|
,"facet.limit","3"
|
||||||
|
,"sort","true"
|
||||||
|
)
|
||||||
|
,"*[count(//lst[@name='trait_s']/int)=3]"
|
||||||
|
,"//lst[@name='trait_s']/int[@name='Tool'][.='2']"
|
||||||
|
,"//lst[@name='trait_s']/int[@name='Obnoxious'][.='1']"
|
||||||
|
,"//lst[@name='trait_s']/int[@name='Chauvinist'][.='1']"
|
||||||
|
);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void testSimpleFacetCountsWithLimits() {
|
|
||||||
assertU(adoc("id", "1", "t_s", "A"));
|
public void testFacetMultiValued() {
|
||||||
assertU(adoc("id", "2", "t_s", "B"));
|
doSimpleFacetCountsWithLimits("t_s");
|
||||||
assertU(adoc("id", "3", "t_s", "C"));
|
}
|
||||||
assertU(adoc("id", "4", "t_s", "C"));
|
|
||||||
assertU(adoc("id", "5", "t_s", "D"));
|
public void testFacetSingleValued() {
|
||||||
assertU(adoc("id", "6", "t_s", "E"));
|
doSimpleFacetCountsWithLimits("t_s1");
|
||||||
assertU(adoc("id", "7", "t_s", "E"));
|
}
|
||||||
assertU(adoc("id", "8", "t_s", "E"));
|
|
||||||
assertU(adoc("id", "9", "t_s", "F"));
|
public void doSimpleFacetCountsWithLimits(String f) {
|
||||||
assertU(adoc("id", "10", "t_s", "G"));
|
String pre = "//lst[@name='"+f+"']";
|
||||||
assertU(adoc("id", "11", "t_s", "G"));
|
String notc = "id:[* TO *] -"+f+":C";
|
||||||
assertU(adoc("id", "12", "t_s", "G"));
|
|
||||||
assertU(adoc("id", "13", "t_s", "G"));
|
assertU(adoc("id", "1", f, "A"));
|
||||||
assertU(adoc("id", "14", "t_s", "G"));
|
assertU(adoc("id", "2", f, "B"));
|
||||||
|
assertU(adoc("id", "3", f, "C"));
|
||||||
|
assertU(adoc("id", "4", f, "C"));
|
||||||
|
assertU(adoc("id", "5", f, "D"));
|
||||||
|
assertU(adoc("id", "6", f, "E"));
|
||||||
|
assertU(adoc("id", "7", f, "E"));
|
||||||
|
assertU(adoc("id", "8", f, "E"));
|
||||||
|
assertU(adoc("id", "9", f, "F"));
|
||||||
|
assertU(adoc("id", "10", f, "G"));
|
||||||
|
assertU(adoc("id", "11", f, "G"));
|
||||||
|
assertU(adoc("id", "12", f, "G"));
|
||||||
|
assertU(adoc("id", "13", f, "G"));
|
||||||
|
assertU(adoc("id", "14", f, "G"));
|
||||||
assertU(commit());
|
assertU(commit());
|
||||||
|
|
||||||
assertQ("check counts for unlimited facet",
|
assertQ("check counts for unlimited facet",
|
||||||
req("q", "id:[* TO *]"
|
req("q", "id:[* TO *]"
|
||||||
,"facet", "true"
|
,"facet", "true"
|
||||||
,"facet.field", "t_s"
|
,"facet.field", f
|
||||||
)
|
)
|
||||||
,"*[count(//lst[@name='facet_fields']/lst[@name='t_s']/int)=7]"
|
,"*[count(//lst[@name='facet_fields']/lst/int)=7]"
|
||||||
|
|
||||||
,"//lst[@name='t_s']/int[@name='G'][.='5']"
|
,pre+"/int[@name='G'][.='5']"
|
||||||
,"//lst[@name='t_s']/int[@name='E'][.='3']"
|
,pre+"/int[@name='E'][.='3']"
|
||||||
,"//lst[@name='t_s']/int[@name='C'][.='2']"
|
,pre+"/int[@name='C'][.='2']"
|
||||||
|
|
||||||
,"//lst[@name='t_s']/int[@name='A'][.='1']"
|
,pre+"/int[@name='A'][.='1']"
|
||||||
,"//lst[@name='t_s']/int[@name='B'][.='1']"
|
,pre+"/int[@name='B'][.='1']"
|
||||||
,"//lst[@name='t_s']/int[@name='D'][.='1']"
|
,pre+"/int[@name='D'][.='1']"
|
||||||
,"//lst[@name='t_s']/int[@name='F'][.='1']"
|
,pre+"/int[@name='F'][.='1']"
|
||||||
);
|
);
|
||||||
|
|
||||||
assertQ("check counts for facet with generous limit",
|
assertQ("check counts for facet with generous limit",
|
||||||
req("q", "id:[* TO *]"
|
req("q", "id:[* TO *]"
|
||||||
,"facet", "true"
|
,"facet", "true"
|
||||||
,"facet.limit", "100"
|
,"facet.limit", "100"
|
||||||
,"facet.field", "t_s"
|
,"facet.field", f
|
||||||
)
|
)
|
||||||
,"*[count(//lst[@name='facet_fields']/lst[@name='t_s']/int)=7]"
|
,"*[count(//lst[@name='facet_fields']/lst/int)=7]"
|
||||||
|
|
||||||
,"//lst[@name='t_s']/int[1][@name='G'][.='5']"
|
,pre+"/int[1][@name='G'][.='5']"
|
||||||
,"//lst[@name='t_s']/int[2][@name='E'][.='3']"
|
,pre+"/int[2][@name='E'][.='3']"
|
||||||
,"//lst[@name='t_s']/int[3][@name='C'][.='2']"
|
,pre+"/int[3][@name='C'][.='2']"
|
||||||
|
|
||||||
,"//lst[@name='t_s']/int[@name='A'][.='1']"
|
,pre+"/int[@name='A'][.='1']"
|
||||||
,"//lst[@name='t_s']/int[@name='B'][.='1']"
|
,pre+"/int[@name='B'][.='1']"
|
||||||
,"//lst[@name='t_s']/int[@name='D'][.='1']"
|
,pre+"/int[@name='D'][.='1']"
|
||||||
,"//lst[@name='t_s']/int[@name='F'][.='1']"
|
,pre+"/int[@name='F'][.='1']"
|
||||||
);
|
);
|
||||||
|
|
||||||
assertQ("check counts for limited facet",
|
assertQ("check counts for limited facet",
|
||||||
req("q", "id:[* TO *]"
|
req("q", "id:[* TO *]"
|
||||||
,"facet", "true"
|
,"facet", "true"
|
||||||
,"facet.limit", "2"
|
,"facet.limit", "2"
|
||||||
,"facet.field", "t_s"
|
,"facet.field", f
|
||||||
)
|
)
|
||||||
,"*[count(//lst[@name='facet_fields']/lst[@name='t_s']/int)=2]"
|
,"*[count(//lst[@name='facet_fields']/lst/int)=2]"
|
||||||
|
|
||||||
,"//lst[@name='t_s']/int[1][@name='G'][.='5']"
|
|
||||||
,"//lst[@name='t_s']/int[2][@name='E'][.='3']"
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
,pre+"/int[1][@name='G'][.='5']"
|
||||||
|
,pre+"/int[2][@name='E'][.='3']"
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("check offset",
|
||||||
|
req("q", "id:[* TO *]"
|
||||||
|
,"facet", "true"
|
||||||
|
,"facet.offset", "1"
|
||||||
|
,"facet.limit", "1"
|
||||||
|
,"facet.field", f
|
||||||
|
)
|
||||||
|
,"*[count(//lst[@name='facet_fields']/lst/int)=1]"
|
||||||
|
|
||||||
|
,pre+"/int[1][@name='E'][.='3']"
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("test sorted facet paging with zero (don't count in limit)",
|
||||||
|
req("q", "id:[* TO *]"
|
||||||
|
,"fq",notc
|
||||||
|
,"facet", "true"
|
||||||
|
,"facet.field", f
|
||||||
|
,"facet.mincount","1"
|
||||||
|
,"facet.offset","0"
|
||||||
|
,"facet.limit","6"
|
||||||
|
)
|
||||||
|
,"*[count(//lst[@name='facet_fields']/lst/int)=6]"
|
||||||
|
,pre+"/int[1][@name='G'][.='5']"
|
||||||
|
,pre+"/int[2][@name='E'][.='3']"
|
||||||
|
,pre+"/int[3][@name='A'][.='1']"
|
||||||
|
,pre+"/int[4][@name='B'][.='1']"
|
||||||
|
,pre+"/int[5][@name='D'][.='1']"
|
||||||
|
,pre+"/int[6][@name='F'][.='1']"
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("test sorted facet paging with zero (test offset correctness)",
|
||||||
|
req("q", "id:[* TO *]"
|
||||||
|
,"fq",notc
|
||||||
|
,"facet", "true"
|
||||||
|
,"facet.field", f
|
||||||
|
,"facet.mincount","1"
|
||||||
|
,"facet.offset","3"
|
||||||
|
,"facet.limit","2"
|
||||||
|
,"facet.sort","true"
|
||||||
|
)
|
||||||
|
,"*[count(//lst[@name='facet_fields']/lst/int)=2]"
|
||||||
|
,pre+"/int[1][@name='B'][.='1']"
|
||||||
|
,pre+"/int[2][@name='D'][.='1']"
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("test facet unsorted paging",
|
||||||
|
req("q", "id:[* TO *]"
|
||||||
|
,"fq",notc
|
||||||
|
,"facet", "true"
|
||||||
|
,"facet.field", f
|
||||||
|
,"facet.mincount","1"
|
||||||
|
,"facet.offset","0"
|
||||||
|
,"facet.limit","6"
|
||||||
|
,"facet.sort","false"
|
||||||
|
)
|
||||||
|
,"*[count(//lst[@name='facet_fields']/lst/int)=6]"
|
||||||
|
,pre+"/int[1][@name='A'][.='1']"
|
||||||
|
,pre+"/int[2][@name='B'][.='1']"
|
||||||
|
,pre+"/int[3][@name='D'][.='1']"
|
||||||
|
,pre+"/int[4][@name='E'][.='3']"
|
||||||
|
,pre+"/int[5][@name='F'][.='1']"
|
||||||
|
,pre+"/int[6][@name='G'][.='5']"
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("test facet unsorted paging",
|
||||||
|
req("q", "id:[* TO *]"
|
||||||
|
,"fq",notc
|
||||||
|
,"facet", "true"
|
||||||
|
,"facet.field", f
|
||||||
|
,"facet.mincount","1"
|
||||||
|
,"facet.offset","3"
|
||||||
|
,"facet.limit","2"
|
||||||
|
,"facet.sort","false"
|
||||||
|
)
|
||||||
|
,"*[count(//lst[@name='facet_fields']/lst/int)=2]"
|
||||||
|
,pre+"/int[1][@name='E'][.='3']"
|
||||||
|
,pre+"/int[2][@name='F'][.='1']"
|
||||||
|
);
|
||||||
|
|
||||||
|
assertQ("test facet unsorted paging, mincount=2",
|
||||||
|
req("q", "id:[* TO *]"
|
||||||
|
,"fq",notc
|
||||||
|
,"facet", "true"
|
||||||
|
,"facet.field", f
|
||||||
|
,"facet.mincount","2"
|
||||||
|
,"facet.offset","1"
|
||||||
|
,"facet.limit","2"
|
||||||
|
,"facet.sort","false"
|
||||||
|
)
|
||||||
|
,"*[count(//lst[@name='facet_fields']/lst/int)=1]"
|
||||||
|
,pre+"/int[1][@name='G'][.='5']"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
private String mkstr(int len) {
|
private String mkstr(int len) {
|
||||||
StringBuilder sb = new StringBuilder(len);
|
StringBuilder sb = new StringBuilder(len);
|
||||||
|
|
|
@ -386,6 +386,7 @@
|
||||||
-->
|
-->
|
||||||
<dynamicField name="*_i" type="sint" indexed="true" stored="true"/>
|
<dynamicField name="*_i" type="sint" indexed="true" stored="true"/>
|
||||||
<dynamicField name="*_s" type="string" indexed="true" stored="true"/>
|
<dynamicField name="*_s" type="string" indexed="true" stored="true"/>
|
||||||
|
<dynamicField name="*_s1" type="string" indexed="true" stored="true" multiValued="false"/>
|
||||||
<dynamicField name="*_l" type="slong" indexed="true" stored="true"/>
|
<dynamicField name="*_l" type="slong" indexed="true" stored="true"/>
|
||||||
<dynamicField name="*_t" type="text" indexed="true" stored="true"/>
|
<dynamicField name="*_t" type="text" indexed="true" stored="true"/>
|
||||||
<dynamicField name="*_b" type="boolean" indexed="true" stored="true"/>
|
<dynamicField name="*_b" type="boolean" indexed="true" stored="true"/>
|
||||||
|
|
Loading…
Reference in New Issue