SOLR-1432: weight nested queries in function queries

git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@816202 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Yonik Seeley 2009-09-17 14:08:13 +00:00
parent a726c23c8b
commit 1eb4697977
38 changed files with 502 additions and 95 deletions

View File

@ -33,10 +33,7 @@ import org.apache.solr.util.DateMathParser;
import java.io.IOException;
import java.text.*;
import java.util.Date;
import java.util.Locale;
import java.util.Map;
import java.util.TimeZone;
import java.util.*;
// TODO: make a FlexibleDateField that can accept dates in multiple
// formats, better for human entered dates.
@ -425,7 +422,7 @@ class DateFieldSource extends FieldCacheSource {
return "date(" + field + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
return new StringIndexDocValues(this, reader, field) {
protected String toTerm(String readableValue) {
// needed for frange queries to work properly

View File

@ -160,7 +160,7 @@ public class RandomSortField extends FieldType {
}
@Override
public DocValues getValues(final IndexReader reader) throws IOException {
public DocValues getValues(Map context, final IndexReader reader) throws IOException {
return new DocValues() {
private final int seed = getSeed(field, reader);
@Override

View File

@ -18,7 +18,6 @@
package org.apache.solr.schema;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.FieldCache;
import org.apache.solr.search.function.ValueSource;
import org.apache.solr.search.function.FieldCacheSource;
import org.apache.solr.search.function.DocValues;
@ -93,7 +92,7 @@ class SortableDoubleFieldSource extends FieldCacheSource {
return "sdouble(" + field + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final double def = defVal;
return new StringIndexDocValues(this, reader, field) {

View File

@ -18,7 +18,6 @@
package org.apache.solr.schema;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.FieldCache;
import org.apache.solr.search.function.ValueSource;
import org.apache.solr.search.function.FieldCacheSource;
import org.apache.solr.search.function.DocValues;
@ -93,7 +92,7 @@ class SortableFloatFieldSource extends FieldCacheSource {
return "sfloat(" + field + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final float def = defVal;
return new StringIndexDocValues(this, reader, field) {

View File

@ -18,7 +18,6 @@
package org.apache.solr.schema;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.FieldCache;
import org.apache.solr.search.function.ValueSource;
import org.apache.solr.search.function.FieldCacheSource;
import org.apache.solr.search.function.DocValues;
@ -97,7 +96,7 @@ class SortableIntFieldSource extends FieldCacheSource {
return "sint(" + field + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final int def = defVal;
return new StringIndexDocValues(this, reader, field) {

View File

@ -18,7 +18,6 @@
package org.apache.solr.schema;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.FieldCache;
import org.apache.solr.search.function.ValueSource;
import org.apache.solr.search.function.FieldCacheSource;
import org.apache.solr.search.function.DocValues;
@ -94,7 +93,7 @@ class SortableLongFieldSource extends FieldCacheSource {
return "slong(" + field + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final long def = defVal;
return new StringIndexDocValues(this, reader, field) {

View File

@ -27,7 +27,6 @@ import org.apache.solr.search.function.FieldCacheSource;
import org.apache.solr.search.function.DocValues;
import org.apache.solr.search.function.StringIndexDocValues;
import org.apache.solr.search.QParser;
import org.apache.solr.util.NumberUtils;
import java.util.Map;
import java.io.IOException;
@ -67,7 +66,7 @@ class StrFieldSource extends FieldCacheSource {
return "str(" + field + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
return new StringIndexDocValues(this, reader, field) {
protected String toTerm(String readableValue) {
return readableValue;

View File

@ -19,6 +19,7 @@ package org.apache.solr.search;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Filter;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.request.SolrQueryRequest;
@ -60,10 +61,19 @@ public class FunctionRangeQParserPlugin extends QParserPlugin {
// TODO: add a score=val option to allow score to be the value
ValueSourceRangeFilter rf = new ValueSourceRangeFilter(vs, l, u, includeLower, includeUpper);
ConstantScoreQuery csq = new ConstantScoreQuery(rf);
SolrConstantScoreQuery csq = new SolrConstantScoreQuery(rf);
return csq;
}
};
}
}
class FunctionConstantScoreQuery extends ConstantScoreQuery {
public FunctionConstantScoreQuery(Filter filter) {
super(filter);
}
}

View File

@ -17,7 +17,6 @@
package org.apache.solr.search;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
@ -436,7 +435,7 @@ public class QueryParsing {
} else if (query instanceof FuzzyQuery) {
out.append(query.toString());
writeBoost=false;
} else if (query instanceof ConstantScoreQuery) {
} else if (query instanceof SolrConstantScoreQuery) {
out.append(query.toString());
writeBoost=false;
} else {

View File

@ -0,0 +1,201 @@
package org.apache.solr.search;
import org.apache.lucene.search.*;
import org.apache.lucene.index.IndexReader;
import org.apache.solr.search.function.ValueSource;
import org.apache.solr.common.SolrException;
import java.io.IOException;
import java.util.Set;
import java.util.Map;
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* A query that wraps a filter and simply returns a constant score equal to the
* query boost for every document in the filter. This Solr extension also supports
* weighting of a SolrFilter.
*
* Experimental and subject to change.
*/
public class SolrConstantScoreQuery extends ConstantScoreQuery {
public SolrConstantScoreQuery(Filter filter) {
super(filter);
}
/** Returns the encapsulated filter */
public Filter getFilter() {
return filter;
}
public Query rewrite(IndexReader reader) throws IOException {
return this;
}
public void extractTerms(Set terms) {
// OK to not add any terms when used for MultiSearcher,
// but may not be OK for highlighting
}
protected class ConstantWeight extends Weight {
private Similarity similarity;
private float queryNorm;
private float queryWeight;
private Map context;
public ConstantWeight(Searcher searcher) throws IOException {
this.similarity = getSimilarity(searcher);
this.context = ValueSource.newContext();
if (filter instanceof SolrFilter)
((SolrFilter)filter).createWeight(context, searcher);
}
public Query getQuery() {
return SolrConstantScoreQuery.this;
}
public float getValue() {
return queryWeight;
}
public float sumOfSquaredWeights() throws IOException {
queryWeight = getBoost();
return queryWeight * queryWeight;
}
public void normalize(float norm) {
this.queryNorm = norm;
queryWeight *= this.queryNorm;
}
public Scorer scorer(IndexReader reader, boolean scoreDocsInOrder, boolean topScorer) throws IOException {
return new ConstantScorer(similarity, reader, this);
}
public Explanation explain(IndexReader reader, int doc) throws IOException {
ConstantScorer cs = new ConstantScorer(similarity, reader, this);
boolean exists = cs.docIdSetIterator.advance(doc) == doc;
ComplexExplanation result = new ComplexExplanation();
if (exists) {
result.setDescription("ConstantScoreQuery(" + filter
+ "), product of:");
result.setValue(queryWeight);
result.setMatch(Boolean.TRUE);
result.addDetail(new Explanation(getBoost(), "boost"));
result.addDetail(new Explanation(queryNorm,"queryNorm"));
} else {
result.setDescription("ConstantScoreQuery(" + filter
+ ") doesn't match id " + doc);
result.setValue(0);
result.setMatch(Boolean.FALSE);
}
return result;
}
}
protected class ConstantScorer extends Scorer {
final DocIdSetIterator docIdSetIterator;
final float theScore;
int doc = -1;
public ConstantScorer(Similarity similarity, IndexReader reader, ConstantWeight w) throws IOException {
super(similarity);
theScore = w.getValue();
DocIdSet docIdSet = filter instanceof SolrFilter ? ((SolrFilter)filter).getDocIdSet(w.context, reader) : filter.getDocIdSet(reader);
if (docIdSet == null) {
docIdSetIterator = DocIdSet.EMPTY_DOCIDSET.iterator();
} else {
DocIdSetIterator iter = docIdSet.iterator();
if (iter == null) {
docIdSetIterator = DocIdSet.EMPTY_DOCIDSET.iterator();
} else {
docIdSetIterator = iter;
}
}
}
/** @deprecated use {@link #nextDoc()} instead. */
public boolean next() throws IOException {
return docIdSetIterator.nextDoc() != NO_MORE_DOCS;
}
public int nextDoc() throws IOException {
return docIdSetIterator.nextDoc();
}
/** @deprecated use {@link #docID()} instead. */
public int doc() {
return docIdSetIterator.doc();
}
public int docID() {
return docIdSetIterator.docID();
}
public float score() throws IOException {
return theScore;
}
/** @deprecated use {@link #advance(int)} instead. */
public boolean skipTo(int target) throws IOException {
return docIdSetIterator.advance(target) != NO_MORE_DOCS;
}
public int advance(int target) throws IOException {
return docIdSetIterator.advance(target);
}
public Explanation explain(int doc) throws IOException {
throw new UnsupportedOperationException();
}
}
public Weight createWeight(Searcher searcher) {
try {
return new SolrConstantScoreQuery.ConstantWeight(searcher);
} catch (IOException e) {
// TODO: remove this if ConstantScoreQuery.createWeight adds IOException
throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, e);
}
}
/** Prints a user-readable version of this query. */
public String toString(String field) {
return "ConstantScore(" + filter.toString()
+ (getBoost()==1.0 ? ")" : "^" + getBoost());
}
/** Returns true if <code>o</code> is equal to this. */
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SolrConstantScoreQuery)) return false;
SolrConstantScoreQuery other = (SolrConstantScoreQuery)o;
return this.getBoost()==other.getBoost() && filter.equals(other.filter);
}
/** Returns a hash code value for this object. */
public int hashCode() {
// Simple add is OK since no existing filter hashcode has a float component.
return filter.hashCode() + Float.floatToIntBits(getBoost());
}
}

View File

@ -0,0 +1,46 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.search;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.index.IndexReader;
import java.util.Map;
import java.io.IOException;
/** A SolrFilter extends the Lucene Filter and adds extra semantics such as passing on
* weight context info for function queries.
*
* Experimental and subject to change.
*/
public abstract class SolrFilter extends Filter {
/** Implementations should propagate createWeight to sub-ValueSources which can store weight info in the context.
* The context object will be passed to getDocIdSet() where this info can be retrieved. */
public abstract void createWeight(Map context, Searcher searcher) throws IOException;
public abstract DocIdSet getDocIdSet(Map context, IndexReader reader) throws IOException;
@Override
public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
return getDocIdSet(null, reader);
}
}

View File

@ -16,10 +16,7 @@
*/
package org.apache.solr.search;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Date;
import java.util.*;
import java.io.IOException;
import org.apache.lucene.queryParser.ParseException;
@ -398,7 +395,7 @@ class LongConstValueSource extends ValueSource {
return "const(" + constant + ")";
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
return new DocValues() {
public float floatVal(int doc) {
return constant;

View File

@ -24,6 +24,7 @@ import org.apache.solr.search.SolrIndexReader;
import java.io.IOException;
import java.util.Set;
import java.util.Map;
/**
* Query that is boosted by a ValueSource
@ -59,10 +60,13 @@ public class BoostedQuery extends Query {
private class BoostedWeight extends Weight {
Searcher searcher;
Weight qWeight;
Map context;
public BoostedWeight(Searcher searcher) throws IOException {
this.searcher = searcher;
this.qWeight = q.weight(searcher);
this.context = boostVal.newContext();
boostVal.createWeight(context,searcher);
}
public Query getQuery() {
@ -105,7 +109,7 @@ public class BoostedQuery extends Query {
return subQueryExpl;
}
DocValues vals = boostVal.getValues(subReaders[readerPos]);
DocValues vals = boostVal.getValues(context, subReaders[readerPos]);
float sc = subQueryExpl.getValue() * vals.floatVal(doc-readerBase);
Explanation res = new ComplexExplanation(
true, sc, BoostedQuery.this.toString() + ", product of:");
@ -132,7 +136,7 @@ public class BoostedQuery extends Query {
this.scorer = scorer;
this.reader = reader;
this.searcher = searcher; // for explain
this.vals = vs.getValues(reader);
this.vals = vs.getValues(weight.context, reader);
}
@Override

View File

@ -20,6 +20,7 @@ import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.FieldCache;
import java.io.IOException;
import java.util.Map;
/**
* Obtains int field values from the {@link org.apache.lucene.search.FieldCache}
@ -45,7 +46,7 @@ public class ByteFieldSource extends FieldCacheSource {
return "byte(" + field + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final byte[] arr = (parser == null) ?
cache.getBytes(reader, field) :
cache.getBytes(reader, field, parser);

View File

@ -20,6 +20,7 @@ package org.apache.solr.search.function;
import org.apache.lucene.index.IndexReader;
import java.io.IOException;
import java.util.Map;
/**
* <code>ConstValueSource</code> returns a constant for all documents
@ -35,7 +36,7 @@ public class ConstValueSource extends ValueSource {
return "const(" + constant + ")";
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
return new DocValues() {
public float floatVal(int doc) {
return constant;

View File

@ -21,6 +21,7 @@ import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.FieldCache;
import java.io.IOException;
import java.util.Map;
/**
* Obtains float field values from the {@link org.apache.lucene.search.FieldCache}
@ -46,7 +47,7 @@ public class DoubleFieldSource extends FieldCacheSource {
return "double(" + field + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final double[] arr = (parser == null) ?
((FieldCache) cache).getDoubles(reader, field) :
((FieldCache) cache).getDoubles(reader, field, parser);

View File

@ -18,8 +18,10 @@
package org.apache.solr.search.function;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Searcher;
import java.io.IOException;
import java.util.Map;
public abstract class DualFloatFunction extends ValueSource {
protected final ValueSource a;
@ -41,9 +43,9 @@ public abstract class DualFloatFunction extends ValueSource {
return name() + "(" + a.description() + "," + b.description() + ")";
}
public DocValues getValues(IndexReader reader) throws IOException {
final DocValues aVals = a.getValues(reader);
final DocValues bVals = b.getValues(reader);
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final DocValues aVals = a.getValues(context, reader);
final DocValues bVals = b.getValues(context, reader);
return new DocValues() {
public float floatVal(int doc) {
return func(doc, aVals, bVals);
@ -66,6 +68,12 @@ public abstract class DualFloatFunction extends ValueSource {
};
}
@Override
public void createWeight(Map context, Searcher searcher) throws IOException {
a.createWeight(context,searcher);
b.createWeight(context,searcher);
}
public int hashCode() {
int h = a.hashCode();
h ^= (h << 13) | (h >>> 20);

View File

@ -54,7 +54,7 @@ public class FileFloatSource extends ValueSource {
return "float(" + field + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
int offset = 0;
if (reader instanceof SolrIndexReader) {
SolrIndexReader r = (SolrIndexReader)reader;

View File

@ -22,6 +22,7 @@ import org.apache.solr.search.function.DocValues;
import org.apache.lucene.search.FieldCache;
import java.io.IOException;
import java.util.Map;
/**
* Obtains float field values from the {@link org.apache.lucene.search.FieldCache}
@ -47,7 +48,7 @@ public class FloatFieldSource extends FieldCacheSource {
return "float(" + field + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final float[] arr = (parser==null) ?
cache.getFloats(reader, field) :
cache.getFloats(reader, field, parser);

View File

@ -23,6 +23,8 @@ import org.apache.solr.search.SolrIndexReader;
import java.io.IOException;
import java.util.Set;
import java.util.IdentityHashMap;
import java.util.Map;
/**
@ -58,9 +60,12 @@ public class FunctionQuery extends Query {
protected Searcher searcher;
protected float queryNorm;
protected float queryWeight;
protected Map context;
public FunctionWeight(Searcher searcher) {
public FunctionWeight(Searcher searcher) throws IOException {
this.searcher = searcher;
this.context = func.newContext();
func.createWeight(context, searcher);
}
public Query getQuery() {
@ -115,7 +120,7 @@ public class FunctionQuery extends Query {
this.reader = reader;
this.maxDoc = reader.maxDoc();
this.hasDeletions = reader.hasDeletions();
vals = func.getValues(reader);
vals = func.getValues(weight.context, reader);
}
@Override
@ -197,7 +202,7 @@ public class FunctionQuery extends Query {
}
public Weight createWeight(Searcher searcher) {
public Weight createWeight(Searcher searcher) throws IOException {
return new FunctionQuery.FunctionWeight(searcher);
}

View File

@ -22,6 +22,7 @@ import org.apache.solr.search.function.DocValues;
import org.apache.lucene.search.FieldCache;
import java.io.IOException;
import java.util.Map;
/**
* Obtains int field values from the {@link org.apache.lucene.search.FieldCache}
@ -47,7 +48,7 @@ public class IntFieldSource extends FieldCacheSource {
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final int[] arr = (parser==null) ?
cache.getInts(reader, field) :
cache.getInts(reader, field, parser);

View File

@ -18,8 +18,10 @@
package org.apache.solr.search.function;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Searcher;
import java.io.IOException;
import java.util.Map;
/**
* <code>LinearFloatFunction</code> implements a linear function over
@ -44,8 +46,8 @@ public class LinearFloatFunction extends ValueSource {
return slope + "*float(" + source.description() + ")+" + intercept;
}
public DocValues getValues(IndexReader reader) throws IOException {
final DocValues vals = source.getValues(reader);
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final DocValues vals = source.getValues(context, reader);
return new DocValues() {
public float floatVal(int doc) {
return vals.floatVal(doc) * slope + intercept;
@ -68,6 +70,11 @@ public class LinearFloatFunction extends ValueSource {
};
}
@Override
public void createWeight(Map context, Searcher searcher) throws IOException {
source.createWeight(context, searcher);
}
public int hashCode() {
int h = Float.floatToIntBits(slope);
h = (h >>> 2) | (h << 30);

View File

@ -22,6 +22,7 @@ import org.apache.lucene.search.FieldCache;
import java.io.IOException;
import java.util.Map;
/**
* Obtains float field values from the {@link org.apache.lucene.search.FieldCache}
@ -52,7 +53,7 @@ public class LongFieldSource extends FieldCacheSource {
return Long.parseLong(extVal);
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final long[] arr = (parser == null) ?
((FieldCache) cache).getLongs(reader, field) :
((FieldCache) cache).getLongs(reader, field, parser);

View File

@ -18,8 +18,10 @@
package org.apache.solr.search.function;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Searcher;
import java.io.IOException;
import java.util.Map;
/**
* Returns the max of a ValueSource and a float
@ -43,8 +45,8 @@ public class MaxFloatFunction extends ValueSource {
return "max(" + source.description() + "," + fval + ")";
}
public DocValues getValues(IndexReader reader) throws IOException {
final DocValues vals = source.getValues(reader);
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final DocValues vals = source.getValues(context, reader);
return new DocValues() {
public float floatVal(int doc) {
float v = vals.floatVal(doc);
@ -68,6 +70,11 @@ public class MaxFloatFunction extends ValueSource {
};
}
@Override
public void createWeight(Map context, Searcher searcher) throws IOException {
source.createWeight(context, searcher);
}
public int hashCode() {
int h = Float.floatToIntBits(fval);
h = (h >>> 2) | (h << 30);

View File

@ -20,9 +20,9 @@ package org.apache.solr.search.function;
import org.apache.lucene.index.IndexReader;
import org.apache.solr.search.function.DocValues;
import org.apache.solr.search.function.ValueSource;
import org.apache.lucene.search.FieldCache;
import java.io.IOException;
import java.util.Map;
/**
* Obtains the ordinal of the field value from the default Lucene {@link org.apache.lucene.search.FieldCache} using getStringIndex().
@ -51,7 +51,7 @@ public class OrdFieldSource extends ValueSource {
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
return new StringIndexDocValues(this, reader, field) {
protected String toTerm(String readableValue) {
return readableValue;

View File

@ -18,13 +18,11 @@
package org.apache.solr.search.function;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Weight;
import org.apache.lucene.search.Scorer;
import org.apache.lucene.search.*;
import org.apache.solr.common.SolrException;
import java.io.IOException;
import java.util.Map;
/**
* <code>QueryValueSource</code> returns the relevance score of the query
@ -45,8 +43,9 @@ public class QueryValueSource extends ValueSource {
return "query(" + q + ",def=" + defVal + ")";
}
public DocValues getValues(IndexReader reader) throws IOException {
return new QueryDocValues(reader, q, defVal);
@Override
public DocValues getValues(Map context, IndexReader reader) throws IOException {
return new QueryDocValues(reader, q, defVal, context==null ? null : (Weight)context.get(this));
}
public int hashCode() {
@ -58,13 +57,18 @@ public class QueryValueSource extends ValueSource {
QueryValueSource other = (QueryValueSource)o;
return this.q.equals(other.q) && this.defVal==other.defVal;
}
@Override
public void createWeight(Map context, Searcher searcher) throws IOException {
Weight w = q.weight(searcher);
context.put(this, w);
}
}
class QueryDocValues extends DocValues {
final Query q;
final IndexReader reader;
final IndexSearcher searcher;
final Weight weight;
final float defVal;
@ -75,12 +79,11 @@ class QueryDocValues extends DocValues {
// to trigger a scorer reset on first access.
int lastDocRequested=Integer.MAX_VALUE;
public QueryDocValues(IndexReader reader, Query q, float defVal) throws IOException {
public QueryDocValues(IndexReader reader, Query q, float defVal, Weight w) throws IOException {
this.reader = reader;
this.q = q;
this.defVal = defVal;
searcher = new IndexSearcher(reader);
weight = q.weight(searcher);
weight = w!=null ? w : q.weight(new IndexSearcher(reader));
}
public float floatVal(int doc) {
@ -88,12 +91,12 @@ class QueryDocValues extends DocValues {
if (doc < lastDocRequested) {
// out-of-order access.... reset scorer.
scorer = weight.scorer(reader, true, false);
scorerDoc = scorer.nextDoc();
scorerDoc = -1;
}
lastDocRequested = doc;
if (scorerDoc < doc) {
scorerDoc = scorer.nextDoc();
scorerDoc = scorer.advance(doc);
}
if (scorerDoc > doc) {

View File

@ -18,8 +18,10 @@
package org.apache.solr.search.function;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Searcher;
import java.io.IOException;
import java.util.Map;
/**
* <code>LinearFloatFunction</code> implements a linear function over
@ -48,8 +50,8 @@ public class RangeMapFloatFunction extends ValueSource {
return "map(" + source.description() + "," + min + "," + max + "," + target + ")";
}
public DocValues getValues(IndexReader reader) throws IOException {
final DocValues vals = source.getValues(reader);
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final DocValues vals = source.getValues(context, reader);
return new DocValues() {
public float floatVal(int doc) {
float val = vals.floatVal(doc);
@ -73,6 +75,11 @@ public class RangeMapFloatFunction extends ValueSource {
};
}
@Override
public void createWeight(Map context, Searcher searcher) throws IOException {
source.createWeight(context, searcher);
}
public int hashCode() {
int h = source.hashCode();
h ^= (h << 10) | (h >>> 23);

View File

@ -18,8 +18,10 @@
package org.apache.solr.search.function;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Searcher;
import java.io.IOException;
import java.util.Map;
/**
* <code>ReciprocalFloatFunction</code> implements a reciprocal function f(x) = a/(mx+b), based on
@ -52,8 +54,8 @@ public class ReciprocalFloatFunction extends ValueSource {
this.b=b;
}
public DocValues getValues(IndexReader reader) throws IOException {
final DocValues vals = source.getValues(reader);
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final DocValues vals = source.getValues(context, reader);
return new DocValues() {
public float floatVal(int doc) {
return a/(m*vals.floatVal(doc) + b);
@ -78,6 +80,11 @@ public class ReciprocalFloatFunction extends ValueSource {
};
}
@Override
public void createWeight(Map context, Searcher searcher) throws IOException {
source.createWeight(context, searcher);
}
public String description() {
return Float.toString(a) + "/("
+ m + "*float(" + source.description() + ")"

View File

@ -23,6 +23,7 @@ import org.apache.solr.search.function.ValueSource;
import org.apache.lucene.search.FieldCache;
import java.io.IOException;
import java.util.Map;
/**
* Obtains the ordinal of the field value from the default Lucene {@link org.apache.lucene.search.FieldCache} using getStringIndex()
@ -51,7 +52,7 @@ public class ReverseOrdFieldSource extends ValueSource {
return "rord("+field+')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final FieldCache.StringIndex sindex = FieldCache.DEFAULT.getStringIndex(reader, field);
final int arr[] = sindex.order;

View File

@ -18,8 +18,10 @@
package org.apache.solr.search.function;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Searcher;
import java.io.IOException;
import java.util.Map;
/**
* Scales values to be between min and max.
@ -47,8 +49,8 @@ public class ScaleFloatFunction extends ValueSource {
return "scale(" + source.description() + "," + min + "," + max + ")";
}
public DocValues getValues(IndexReader reader) throws IOException {
final DocValues vals = source.getValues(reader);
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final DocValues vals = source.getValues(context, reader);
int maxDoc = reader.maxDoc();
// this doesn't take into account deleted docs!
@ -107,6 +109,11 @@ public class ScaleFloatFunction extends ValueSource {
};
}
@Override
public void createWeight(Map context, Searcher searcher) throws IOException {
source.createWeight(context, searcher);
}
public int hashCode() {
int h = Float.floatToIntBits(min);
h = h*29;

View File

@ -20,6 +20,7 @@ import org.apache.lucene.search.FieldCache;
import org.apache.lucene.index.IndexReader;
import java.io.IOException;
import java.util.Map;
/**
@ -42,7 +43,7 @@ public class ShortFieldSource extends FieldCacheSource{
return "short(" + field + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final short[] arr = (parser == null) ?
cache.getShorts(reader, field) :
cache.getShorts(reader, field, parser);

View File

@ -20,25 +20,20 @@ package org.apache.solr.search.function;
import org.apache.lucene.index.IndexReader;
import java.io.IOException;
import java.util.Map;
/** A simple float function with a single argument
*/
public abstract class SimpleFloatFunction extends ValueSource {
protected final ValueSource source;
public abstract class SimpleFloatFunction extends SingleFunction {
public SimpleFloatFunction(ValueSource source) {
this.source = source;
super(source);
}
protected abstract String name();
protected abstract float func(int doc, DocValues vals);
public String description() {
return name() + '(' + source.description() + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
final DocValues vals = source.getValues(reader);
@Override
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final DocValues vals = source.getValues(context, reader);
return new DocValues() {
public float floatVal(int doc) {
return func(doc, vals);
@ -60,15 +55,4 @@ import java.io.IOException;
}
};
}
public int hashCode() {
return source.hashCode() + name().hashCode();
}
public boolean equals(Object o) {
if (this.getClass() != o.getClass()) return false;
SimpleFloatFunction other = (SimpleFloatFunction)o;
return this.name().equals(other.name())
&& this.source.equals(other.source);
}
}

View File

@ -0,0 +1,55 @@
/**
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.solr.search.function;
import org.apache.lucene.search.Searcher;
import java.io.IOException;
import java.util.Map;
/** A function with a single argument
*/
public abstract class SingleFunction extends ValueSource {
protected final ValueSource source;
public SingleFunction(ValueSource source) {
this.source = source;
}
protected abstract String name();
public String description() {
return name() + '(' + source.description() + ')';
}
public int hashCode() {
return source.hashCode() + name().hashCode();
}
public boolean equals(Object o) {
if (this.getClass() != o.getClass()) return false;
SingleFunction other = (SingleFunction)o;
return this.name().equals(other.name())
&& this.source.equals(other.source);
}
@Override
public void createWeight(Map context, Searcher searcher) throws IOException {
source.createWeight(context, searcher);
}
}

View File

@ -18,9 +18,11 @@
package org.apache.solr.search.function;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Searcher;
import java.io.IOException;
import java.util.Arrays;
import java.util.Map;
/**
* <code>SumFloatFunction</code> returns the sum of it's components.
@ -71,10 +73,10 @@ abstract class MultiFloatFunction extends ValueSource {
return sb.toString();
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
final DocValues[] valsArr = new DocValues[sources.length];
for (int i=0; i<sources.length; i++) {
valsArr[i] = sources[i].getValues(reader);
valsArr[i] = sources[i].getValues(context, reader);
}
return new DocValues() {
@ -111,6 +113,12 @@ abstract class MultiFloatFunction extends ValueSource {
};
}
@Override
public void createWeight(Map context, Searcher searcher) throws IOException {
for (ValueSource source : sources)
source.createWeight(context, searcher);
}
public int hashCode() {
return Arrays.hashCode(sources) + name().hashCode();
}

View File

@ -20,6 +20,7 @@ import org.apache.solr.search.SolrIndexReader;
import org.apache.lucene.index.IndexReader;
import java.io.IOException;
import java.util.Map;
/**
* A value source that wraps another and ensures that the top level reader
@ -41,7 +42,7 @@ public class TopValueSource extends ValueSource {
return "top(" + vs.description() + ')';
}
public DocValues getValues(IndexReader reader) throws IOException {
public DocValues getValues(Map context, IndexReader reader) throws IOException {
int offset = 0;
IndexReader topReader = reader;
if (topReader instanceof SolrIndexReader) {
@ -53,7 +54,7 @@ public class TopValueSource extends ValueSource {
topReader = r;
}
final int off = offset;
final DocValues vals = vs.getValues(topReader);
final DocValues vals = vs.getValues(context, topReader);
if (topReader == reader) return vals;
return new DocValues() {

View File

@ -23,6 +23,8 @@ import org.apache.solr.search.function.DocValues;
import java.io.IOException;
import java.io.Serializable;
import java.util.IdentityHashMap;
import java.util.Map;
/**
* Instantiates {@link org.apache.solr.search.function.DocValues} for a particular reader.
@ -33,7 +35,16 @@ import java.io.Serializable;
*/
public abstract class ValueSource implements Serializable {
public abstract DocValues getValues(IndexReader reader) throws IOException;
public DocValues getValues(IndexReader reader) throws IOException {
return getValues(null, reader);
}
/** Gets the values for this reader and the context that was previously
* passed to createWeight()
*/
public DocValues getValues(Map context, IndexReader reader) throws IOException {
return null;
}
public abstract boolean equals(Object o);
@ -46,6 +57,16 @@ public abstract class ValueSource implements Serializable {
return description();
}
/** Implementations should propagate createWeight to sub-ValueSources which can optionally store
* weight info in the context. The context object will be passed to getValues()
* where this info can be retrieved. */
public void createWeight(Map context, Searcher searcher) throws IOException {
}
/** Returns a new non-threadsafe context map. */
public static Map newContext() {
return new IdentityHashMap();
}
}

View File

@ -20,15 +20,18 @@ package org.apache.solr.search.function;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.DocIdSetIterator;
import org.apache.lucene.search.Searcher;
import org.apache.lucene.index.IndexReader;
import org.apache.solr.search.SolrFilter;
import java.io.IOException;
import java.util.Map;
/**
* RangeFilter over a ValueSource.
*/
public class ValueSourceRangeFilter extends Filter {
public class ValueSourceRangeFilter extends SolrFilter {
private final ValueSource valueSource;
private final String lowerVal;
private final String upperVal;
@ -47,14 +50,19 @@ public class ValueSourceRangeFilter extends Filter {
this.includeUpper = upperVal != null && includeUpper;
}
public DocIdSet getDocIdSet(final IndexReader reader) throws IOException {
public DocIdSet getDocIdSet(final Map context, final IndexReader reader) throws IOException {
return new DocIdSet() {
public DocIdSetIterator iterator() throws IOException {
return valueSource.getValues(reader).getRangeScorer(reader, lowerVal, upperVal, includeLower, includeUpper);
return valueSource.getValues(context, reader).getRangeScorer(reader, lowerVal, upperVal, includeLower, includeUpper);
}
};
}
@Override
public void createWeight(Map context, Searcher searcher) throws IOException {
valueSource.createWeight(context, searcher);
}
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("frange(");

View File

@ -201,6 +201,7 @@ public class TestFunctionQuery extends AbstractSolrTestCase {
public void testFunctions() {
doTest("foo_pf"); // a plain float field
doTest("foo_f"); // a sortable float field
doTest("foo_tf"); // a trie float field
}
public void testExternalField() {
@ -304,5 +305,26 @@ public class TestFunctionQuery extends AbstractSolrTestCase {
assertQ(req("fl","*,score","q", "{!func}ms(2009-08-31T12:10:10.125Z,b_tdt)", "fq","id:1"), "//float[@name='score']='1.0'");
assertQ(req("fl","*,score","q", "{!func}ms(2009-08-31T12:10:10.125Z/SECOND,2009-08-31T12:10:10.124Z/SECOND)", "fq","id:1"), "//float[@name='score']='0.0'");
for (int i=100; i<112; i++) {
assertU(adoc("id",""+i, "text","batman"));
}
assertU(commit());
assertU(adoc("id","120", "text","batman superman")); // in a segment by itself
assertU(commit());
// batman and superman have the same idf in single-doc segment, but very different in the complete index.
String q ="{!func}query($qq)";
String fq="id:120";
assertQ(req("fl","*,score","q", q, "qq","text:batman", "fq",fq), "//float[@name='score']<'1.0'");
assertQ(req("fl","*,score","q", q, "qq","text:superman", "fq",fq), "//float[@name='score']>'1.0'");
// test weighting through a function range query
assertQ(req("fl","*,score", "q", "{!frange l=1 u=10}query($qq)", "qq","text:superman"), "//*[@numFound='1']");
// test weighting through a complex function
q ="{!func}sub(div(sum(0.0,product(1,query($qq))),1),0)";
assertQ(req("fl","*,score","q", q, "qq","text:batman", "fq",fq), "//float[@name='score']<'1.0'");
assertQ(req("fl","*,score","q", q, "qq","text:superman", "fq",fq), "//float[@name='score']>'1.0'");
}
}