You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ho...@apache.org on 2015/07/31 18:21:44 UTC
svn commit: r1693625 - in /lucene/dev/trunk:
lucene/core/src/java/org/apache/lucene/search/ solr/
solr/core/src/java/org/apache/solr/schema/
solr/core/src/java/org/apache/solr/search/
solr/core/src/test-files/solr/collection1/conf/ solr/core/src/test/o...
Author: hossman
Date: Fri Jul 31 16:21:44 2015
New Revision: 1693625
URL: http://svn.apache.org/r1693625
Log:
SOLR-2522: new two argument option for the existing field() function; picks the min/max value of a docValues field to use as a ValueSource: "field(field_name,min)" and "field(field_name,max)"
Added:
lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/function/TestMinMaxOnMultiValuedField.java (with props)
Modified:
lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/SortedSetSelector.java
lucene/dev/trunk/solr/CHANGES.txt
lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/FieldType.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieDoubleField.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieField.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieFloatField.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieIntField.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieLongField.java
lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java
lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema15.xml
lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java
Modified: lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/SortedSetSelector.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/SortedSetSelector.java?rev=1693625&r1=1693624&r2=1693625&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/SortedSetSelector.java (original)
+++ lucene/dev/trunk/lucene/core/src/java/org/apache/lucene/search/SortedSetSelector.java Fri Jul 31 16:21:44 2015
@@ -77,7 +77,7 @@ public class SortedSetSelector {
return new MinValue(sortedSet);
} else {
if (sortedSet instanceof RandomAccessOrds == false) {
- throw new UnsupportedOperationException("codec does not support random access ordinals, cannot use selector: " + selector);
+ throw new UnsupportedOperationException("codec does not support random access ordinals, cannot use selector: " + selector + " docValsImpl: " + sortedSet.toString());
}
RandomAccessOrds randomOrds = (RandomAccessOrds) sortedSet;
switch(selector) {
Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1693625&r1=1693624&r2=1693625&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Fri Jul 31 16:21:44 2015
@@ -166,6 +166,9 @@ New Features
* SOLR-7742: Support for Immutable ConfigSets (Gregory Chanan)
+* SOLR-2522: new two argument option for the existing field() function; picks the min/max value of a
+ docValues field to use as a ValueSource: "field(field_name,min)" and "field(field_name,max)" (hossman)
+
Bug Fixes
----------------------
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/FieldType.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/FieldType.java?rev=1693625&r1=1693624&r2=1693625&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/FieldType.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/FieldType.java Fri Jul 31 16:21:44 2015
@@ -23,6 +23,7 @@ import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Locale;
import java.util.Map;
import java.util.Set;
@@ -44,6 +45,8 @@ import org.apache.lucene.search.MultiTer
import org.apache.lucene.search.PrefixQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.SortedSetSelector;
+import org.apache.lucene.search.SortedNumericSelector;
import org.apache.lucene.search.TermQuery;
import org.apache.lucene.search.TermRangeQuery;
import org.apache.lucene.search.similarities.Similarity;
@@ -670,6 +673,27 @@ public abstract class FieldType extends
}
/**
+ * Method for dynamically building a ValueSource based on a single value of a multivalued field.
+ *
+ * The default implementation throws an error except in the trivial case where this method is used on
+ * a {@link SchemaField} that is in fact not-multivalued, in which case it delegates to
+ * {@link #getValueSource}
+ *
+ * @see MultiValueSelector
+ */
+ public ValueSource getSingleValueSource(MultiValueSelector choice, SchemaField field, QParser parser) {
+ // trivial base case
+ if (!field.multiValued()) {
+ // single value matches any selector
+ return getValueSource(field, parser);
+ }
+
+ throw new SolrException(ErrorCode.BAD_REQUEST, "Selecting a single value from a multivalued field is not supported for this field: " + field.getName() + " (type: " + this.getTypeName() + ")");
+ }
+
+
+
+ /**
* Returns a Query instance for doing range searches on this field type. {@link org.apache.solr.search.SolrQueryParser}
* currently passes part1 and part2 as null if they are '*' respectively. minInclusive and maxInclusive are both true
* currently by SolrQueryParser but that may change in the future. Also, other QueryParser implementations may have
@@ -1028,4 +1052,65 @@ public abstract class FieldType extends
final byte[] bytes = Base64.base64ToByteArray(val);
return new BytesRef(bytes);
}
+
+ /**
+ * An enumeration representing various options that may exist for selecting a single value from a
+ * multivalued field. This class is designed to be an abstract representation, agnostic of some of
+ * the underlying specifics. Not all enum value are garunteeded work in all contexts -- null checks
+ * must be dont by the caller for the specific methods needed.
+ *
+ * @see FieldType#getSingleValueSource
+ */
+ public enum MultiValueSelector {
+ // trying to be agnostic about SortedSetSelector.Type vs SortedNumericSelector.Type
+ MIN(SortedSetSelector.Type.MIN, SortedNumericSelector.Type.MIN),
+ MAX(SortedSetSelector.Type.MAX, SortedNumericSelector.Type.MAX);
+
+ @Override
+ public String toString() { return super.toString().toLowerCase(Locale.ROOT); }
+
+ /**
+ * The appropriate <code>SortedSetSelector.Type</code> option for this <code>MultiValueSelector</code>,
+ * may be null if there is no equivilent
+ */
+ public SortedSetSelector.Type getSortedSetSelectorType() {
+ return sType;
+ }
+
+ /**
+ * The appropriate <code>SortedNumericSelector.Type</code> option for this <code>MultiValueSelector</code>,
+ * may be null if there is no equivilent
+ */
+ public SortedNumericSelector.Type getSortedNumericSelectorType() {
+ return nType;
+ }
+
+ private final SortedSetSelector.Type sType;
+ private final SortedNumericSelector.Type nType;
+
+ private MultiValueSelector(SortedSetSelector.Type sType, SortedNumericSelector.Type nType) {
+ this.sType = sType;
+ this.nType = nType;
+ }
+
+ /**
+ * Returns a MultiValueSelector matching the specified (case insensitive) label, or null if
+ * no corrisponding MultiValueSelector exists.
+ *
+ * @param label a non null label to be checked for a corrisponding MultiValueSelector
+ * @return a MultiValueSelector or null if no MultiValueSelector matches the specified label
+ */
+ public static MultiValueSelector lookup(String label) {
+ if (null == label) {
+ throw new NullPointerException("label must not be null when calling MultiValueSelector.lookup");
+ }
+ try {
+ return valueOf(label.toUpperCase(Locale.ROOT));
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+
+ }
+
}
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieDoubleField.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieDoubleField.java?rev=1693625&r1=1693624&r2=1693625&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieDoubleField.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieDoubleField.java Fri Jul 31 16:21:44 2015
@@ -17,6 +17,23 @@
package org.apache.solr.schema;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.apache.lucene.queries.function.docvalues.DoubleDocValues;
+import org.apache.lucene.queries.function.valuesource.SortedSetFieldSource;
+import org.apache.lucene.search.SortedSetSelector;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.lucene.util.mutable.MutableValue;
+import org.apache.lucene.util.mutable.MutableValueDouble;
+
/**
* A numeric field that can contain double-precision 64-bit IEEE 754 floating
* point values.
@@ -37,4 +54,49 @@ public class TrieDoubleField extends Tri
{
type=TrieTypes.DOUBLE;
}
+
+ @Override
+ protected ValueSource getSingleValueSource(SortedSetSelector.Type choice, SchemaField f) {
+
+ return new SortedSetFieldSource(f.getName(), choice) {
+ @Override
+ public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+ SortedSetFieldSource thisAsSortedSetFieldSource = this; // needed for nested anon class ref
+
+ SortedSetDocValues sortedSet = DocValues.getSortedSet(readerContext.reader(), field);
+ SortedDocValues view = SortedSetSelector.wrap(sortedSet, selector);
+
+ return new DoubleDocValues(thisAsSortedSetFieldSource) {
+ @Override
+ public double doubleVal(int doc) {
+ BytesRef bytes = view.get(doc);
+ return NumericUtils.sortableLongToDouble(NumericUtils.prefixCodedToLong(bytes));
+ }
+
+ @Override
+ public boolean exists(int doc) {
+ return -1 != view.getOrd(doc);
+ }
+
+ @Override
+ public ValueFiller getValueFiller() {
+ return new ValueFiller() {
+ private final MutableValueDouble mval = new MutableValueDouble();
+
+ @Override
+ public MutableValue getValue() {
+ return mval;
+ }
+
+ @Override
+ public void fillValue(int doc) {
+ mval.exists = exists(doc);
+ mval.value = mval.exists ? doubleVal(doc) : 0.0D;
+ }
+ };
+ }
+ };
+ }
+ };
+ }
}
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieField.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieField.java?rev=1693625&r1=1693624&r2=1693625&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieField.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieField.java Fri Jul 31 16:21:44 2015
@@ -42,6 +42,7 @@ import org.apache.lucene.queries.functio
import org.apache.lucene.search.DocValuesRangeQuery;
import org.apache.lucene.search.NumericRangeQuery;
import org.apache.lucene.search.Query;
+import org.apache.lucene.search.SortedSetSelector;
import org.apache.lucene.search.SortField;
import org.apache.lucene.uninverting.UninvertingReader.Type;
import org.apache.lucene.util.BytesRef;
@@ -244,7 +245,50 @@ public class TrieField extends Primitive
}
}
+ @Override
+ public final ValueSource getSingleValueSource(MultiValueSelector choice, SchemaField field, QParser parser) {
+ // trivial base case
+ if (!field.multiValued()) {
+ // single value matches any selector
+ return getValueSource(field, parser);
+ }
+
+ // See LUCENE-6709
+ if (! field.hasDocValues()) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+ "docValues='true' is required to select '" + choice.toString() +
+ "' value from multivalued field ("+ field.getName() +") at query time");
+ }
+
+ // multivalued Trie fields all use SortedSetDocValues, so we give a clean error if that's
+ // not supported by the specified choice, else we delegate to a helper
+ SortedSetSelector.Type selectorType = choice.getSortedSetSelectorType();
+ if (null == selectorType) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+ choice.toString() + " is not a supported option for picking a single value"
+ + " from the multivalued field: " + field.getName() +
+ " (type: " + this.getTypeName() + ")");
+ }
+
+ return getSingleValueSource(selectorType, field);
+ }
+ /**
+ * Helper method that will only be called for multivalued Trie fields that have doc values.
+ * Default impl throws an error indicating that selecting a single value from this multivalued
+ * field is not supported for this field type
+ *
+ * @param choice the selector Type to use, will never be null
+ * @param field the field to use, garunteed to be multivalued.
+ * @see #getSingleValueSource(MultiValueSelector,SchemaField,QParser)
+ */
+ protected ValueSource getSingleValueSource(SortedSetSelector.Type choice, SchemaField field) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+ "Can not select a single value for multivalued field: " + field.getName()
+ + " (single valued field selection not supported for type: " + this.getTypeName()
+ + ")");
+ }
+
@Override
public void write(TextResponseWriter writer, String name, StorableField f) throws IOException {
writer.writeVal(name, toObject(f));
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieFloatField.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieFloatField.java?rev=1693625&r1=1693624&r2=1693625&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieFloatField.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieFloatField.java Fri Jul 31 16:21:44 2015
@@ -17,6 +17,23 @@
package org.apache.solr.schema;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.apache.lucene.queries.function.docvalues.FloatDocValues;
+import org.apache.lucene.queries.function.valuesource.SortedSetFieldSource;
+import org.apache.lucene.search.SortedSetSelector;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.lucene.util.mutable.MutableValue;
+import org.apache.lucene.util.mutable.MutableValueFloat;
+
/**
* A numeric field that can contain single-precision 32-bit IEEE 754
* floating point values.
@@ -45,4 +62,50 @@ public class TrieFloatField extends Trie
if (val instanceof String) return Float.parseFloat((String) val);
return super.toNativeType(val);
}
+
+ @Override
+ protected ValueSource getSingleValueSource(SortedSetSelector.Type choice, SchemaField f) {
+
+ return new SortedSetFieldSource(f.getName(), choice) {
+ @Override
+ public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+ SortedSetFieldSource thisAsSortedSetFieldSource = this; // needed for nested anon class ref
+
+ SortedSetDocValues sortedSet = DocValues.getSortedSet(readerContext.reader(), field);
+ SortedDocValues view = SortedSetSelector.wrap(sortedSet, selector);
+
+ return new FloatDocValues(thisAsSortedSetFieldSource) {
+ @Override
+ public float floatVal(int doc) {
+ BytesRef bytes = view.get(doc);
+ return NumericUtils.sortableIntToFloat(NumericUtils.prefixCodedToInt(bytes));
+ }
+
+ @Override
+ public boolean exists(int doc) {
+ return -1 != view.getOrd(doc);
+ }
+
+ @Override
+ public ValueFiller getValueFiller() {
+ return new ValueFiller() {
+ private final MutableValueFloat mval = new MutableValueFloat();
+
+ @Override
+ public MutableValue getValue() {
+ return mval;
+ }
+
+ @Override
+ public void fillValue(int doc) {
+ mval.exists = exists(doc);
+ mval.value = mval.exists ? floatVal(doc) : 0.0F;
+ }
+ };
+ }
+ };
+ }
+ };
+ }
+
}
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieIntField.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieIntField.java?rev=1693625&r1=1693624&r2=1693625&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieIntField.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieIntField.java Fri Jul 31 16:21:44 2015
@@ -17,6 +17,23 @@
package org.apache.solr.schema;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.apache.lucene.queries.function.docvalues.IntDocValues;
+import org.apache.lucene.queries.function.valuesource.SortedSetFieldSource;
+import org.apache.lucene.search.SortedSetSelector;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.lucene.util.mutable.MutableValue;
+import org.apache.lucene.util.mutable.MutableValueInt;
+
/**
* A numeric field that can contain 32-bit signed two's complement integer values.
*
@@ -44,4 +61,49 @@ public class TrieIntField extends TrieFi
}
return super.toNativeType(val);
}
+
+ @Override
+ protected ValueSource getSingleValueSource(SortedSetSelector.Type choice, SchemaField f) {
+
+ return new SortedSetFieldSource(f.getName(), choice) {
+ @Override
+ public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+ SortedSetFieldSource thisAsSortedSetFieldSource = this; // needed for nested anon class ref
+
+ SortedSetDocValues sortedSet = DocValues.getSortedSet(readerContext.reader(), field);
+ SortedDocValues view = SortedSetSelector.wrap(sortedSet, selector);
+
+ return new IntDocValues(thisAsSortedSetFieldSource) {
+ @Override
+ public int intVal(int doc) {
+ BytesRef bytes = view.get(doc);
+ return NumericUtils.prefixCodedToInt(bytes);
+ }
+
+ @Override
+ public boolean exists(int doc) {
+ return -1 != view.getOrd(doc);
+ }
+
+ @Override
+ public ValueFiller getValueFiller() {
+ return new ValueFiller() {
+ private final MutableValueInt mval = new MutableValueInt();
+
+ @Override
+ public MutableValue getValue() {
+ return mval;
+ }
+
+ @Override
+ public void fillValue(int doc) {
+ mval.exists = exists(doc);
+ mval.value = mval.exists ? intVal(doc) : 0;
+ }
+ };
+ }
+ };
+ }
+ };
+ }
}
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieLongField.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieLongField.java?rev=1693625&r1=1693624&r2=1693625&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieLongField.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/TrieLongField.java Fri Jul 31 16:21:44 2015
@@ -17,6 +17,23 @@
package org.apache.solr.schema;
+import java.io.IOException;
+import java.util.Map;
+
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.index.DocValues;
+import org.apache.lucene.index.SortedDocValues;
+import org.apache.lucene.index.SortedSetDocValues;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.apache.lucene.queries.function.docvalues.LongDocValues;
+import org.apache.lucene.queries.function.valuesource.SortedSetFieldSource;
+import org.apache.lucene.search.SortedSetSelector;
+import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.NumericUtils;
+import org.apache.lucene.util.mutable.MutableValue;
+import org.apache.lucene.util.mutable.MutableValueLong;
+
/**
* A numeric field that can contain 64-bit signed two's complement integer values.
*
@@ -31,4 +48,49 @@ public class TrieLongField extends TrieF
{
type=TrieTypes.LONG;
}
+
+ @Override
+ protected ValueSource getSingleValueSource(SortedSetSelector.Type choice, SchemaField f) {
+
+ return new SortedSetFieldSource(f.getName(), choice) {
+ @Override
+ public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+ SortedSetFieldSource thisAsSortedSetFieldSource = this; // needed for nested anon class ref
+
+ SortedSetDocValues sortedSet = DocValues.getSortedSet(readerContext.reader(), field);
+ SortedDocValues view = SortedSetSelector.wrap(sortedSet, selector);
+
+ return new LongDocValues(thisAsSortedSetFieldSource) {
+ @Override
+ public long longVal(int doc) {
+ BytesRef bytes = view.get(doc);
+ return NumericUtils.prefixCodedToLong(bytes);
+ }
+
+ @Override
+ public boolean exists(int doc) {
+ return -1 != view.getOrd(doc);
+ }
+
+ @Override
+ public ValueFiller getValueFiller() {
+ return new ValueFiller() {
+ private final MutableValueLong mval = new MutableValueLong();
+
+ @Override
+ public MutableValue getValue() {
+ return mval;
+ }
+
+ @Override
+ public void fillValue(int doc) {
+ mval.exists = exists(doc);
+ mval.value = mval.exists ? longVal(doc) : 0;
+ }
+ };
+ }
+ };
+ }
+ };
+ }
}
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java?rev=1693625&r1=1693624&r2=1693625&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ValueSourceParser.java Fri Jul 31 16:21:44 2015
@@ -398,6 +398,17 @@ public abstract class ValueSourceParser
String fieldName = fp.parseArg();
SchemaField f = fp.getReq().getSchema().getField(fieldName);
+ if (fp.hasMoreArguments()) {
+ // multivalued selector option
+ String s = fp.parseArg();
+ FieldType.MultiValueSelector selector = FieldType.MultiValueSelector.lookup(s);
+ if (null == selector) {
+ throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+ "Multi-Valued field selector '"+s+"' not spported");
+ }
+ return f.getType().getSingleValueSource(selector, f, fp);
+ }
+ // simple field ValueSource
return f.getType().getValueSource(f, fp);
}
});
@@ -563,6 +574,7 @@ public abstract class ValueSourceParser
return new MinFloatFunction(sources.toArray(new ValueSource[sources.size()]));
}
});
+
addParser("sqedist", new ValueSourceParser() {
@Override
public ValueSource parse(FunctionQParser fp) throws SyntaxError {
Modified: lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema15.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema15.xml?rev=1693625&r1=1693624&r2=1693625&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema15.xml (original)
+++ lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema15.xml Fri Jul 31 16:21:44 2015
@@ -506,7 +506,7 @@
<!-- points to the root document of a block of nested documents -->
<field name="_root_" type="string" indexed="true" stored="true"/>
-
+ <field name="multi_int_with_docvals" type="tint" multiValued="true" docValues="true" indexed="false" />
<dynamicField name="*_coordinate" type="tdouble" indexed="true" stored="false"/>
Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java?rev=1693625&r1=1693624&r2=1693625&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/QueryEqualityTest.java Fri Jul 31 16:21:44 2015
@@ -817,6 +817,28 @@ public class QueryEqualityTest extends S
assertFuncEquals("field(\"foo_i\")",
"field('foo_i\')",
"foo_i");
+
+ // simple VS of single valued field should be same as asking for min/max on that field
+ assertFuncEquals("field(\"foo_i\")",
+ "field('foo_i',min)",
+ "field(foo_i,'min')",
+ "field('foo_i',max)",
+ "field(foo_i,'max')",
+ "foo_i");
+
+ // multivalued field with selector
+ String multif = "multi_int_with_docvals";
+ SolrQueryRequest req = req("my_field", multif);
+ // this test is only viable if it's a multivalued field, sanity check the schema
+ assertTrue(multif + " is no longer multivalued, who broke this schema?",
+ req.getSchema().getField(multif).multiValued());
+ assertFuncEquals(req,
+ "field($my_field,'MIN')",
+ "field('"+multif+"',min)");
+ assertFuncEquals(req,
+ "field($my_field,'max')",
+ "field('"+multif+"',Max)");
+
}
public void testFuncCurrency() throws Exception {
assertFuncEquals("currency(\"amount\")",
Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/function/TestMinMaxOnMultiValuedField.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/function/TestMinMaxOnMultiValuedField.java?rev=1693625&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/function/TestMinMaxOnMultiValuedField.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/function/TestMinMaxOnMultiValuedField.java Fri Jul 31 16:21:44 2015
@@ -0,0 +1,358 @@
+/*
+ * 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.util.LuceneTestCase.SuppressCodecs;
+import org.apache.lucene.util.TestUtil;
+
+import org.apache.solr.SolrTestCaseJ4;
+import org.apache.solr.schema.IndexSchema;
+import org.apache.solr.schema.SchemaField;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.SolrInputDocument;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.NamedList;
+
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Ignore;
+
+@SuppressCodecs({"Memory", "SimpleText"}) // see TestSortedSetSelector
+public class TestMinMaxOnMultiValuedField extends SolrTestCaseJ4 {
+
+ /** Initializes core and does some sanity checking of schema */
+ @BeforeClass
+ public static void beforeClass() throws Exception {
+ initCore("solrconfig-functionquery.xml","schema11.xml");
+
+ // sanity check the expected properties of our fields (ie: who broke the schema?)
+ IndexSchema schema = h.getCore().getLatestSchema();
+ for (String type : new String[] {"i", "l", "f", "d"}) {
+ for (String suffix : new String [] {"", "_dv", "_ni_dv"}) {
+ String f = "val_t" + type + "s" + suffix;
+ SchemaField sf = schema.getField(f);
+ assertTrue(f + " is not multivalued", sf.multiValued());
+ assertEquals(f + " doesn't have expected docValues status",
+ f.contains("dv"), sf.hasDocValues());
+ assertEquals(f + " doesn't have expected index status",
+ ! f.contains("ni"), sf.indexed());
+ }
+ }
+ }
+
+ /** Deletes all docs (which may be left over from a previous test */
+ @Before
+ public void before() throws Exception {
+ assertU(delQ("*:*"));
+ assertU(commit());
+ }
+
+ public void testBasics() throws Exception {
+ assertU(adoc(sdoc("id", "1"
+ // int
+ ,"val_tis_dv", "42"
+ ,"val_tis_dv", "9"
+ ,"val_tis_dv", "-54"
+ // long
+ ,"val_tls_dv", "420"
+ ,"val_tls_dv", "90"
+ ,"val_tls_dv", "-540"
+ // float
+ ,"val_tfs_dv", "-42.5"
+ ,"val_tfs_dv", "-4.5"
+ ,"val_tfs_dv", "-13.5"
+ // double
+ ,"val_tds_dv", "-420.5"
+ ,"val_tds_dv", "-40.5"
+ ,"val_tds_dv", "-130.5"
+ )));
+ assertU(commit());
+
+ assertQ(req("q","id:1"
+ // int
+ ,"fl","exists_min_i:exists(field(val_tis_dv,min))"
+ ,"fl","exists_max_i:exists(field(val_tis_dv,max))"
+ ,"fl","min_i:field(val_tis_dv,min)"
+ ,"fl","max_i:field(val_tis_dv,max)"
+ // long
+ ,"fl","exists_min_l:exists(field(val_tls_dv,min))"
+ ,"fl","exists_max_l:exists(field(val_tls_dv,max))"
+ ,"fl","min_l:field(val_tls_dv,min)"
+ ,"fl","max_l:field(val_tls_dv,max)"
+ // float
+ ,"fl","exists_min_f:exists(field(val_tfs_dv,min))"
+ ,"fl","exists_max_f:exists(field(val_tfs_dv,max))"
+ ,"fl","min_f:field(val_tfs_dv,min)"
+ ,"fl","max_f:field(val_tfs_dv,max)"
+ // double
+ ,"fl","exists_min_d:exists(field(val_tds_dv,min))"
+ ,"fl","exists_max_d:exists(field(val_tds_dv,max))"
+ ,"fl","min_d:field(val_tds_dv,min)"
+ ,"fl","max_d:field(val_tds_dv,max)"
+
+ )
+ ,"//*[@numFound='1']"
+ // int
+ ,"//bool[@name='exists_min_i']='true'"
+ ,"//bool[@name='exists_max_i']='true'"
+ ,"//int[@name='min_i']='-54'"
+ ,"//int[@name='max_i']='42'"
+ // long
+ ,"//bool[@name='exists_min_l']='true'"
+ ,"//bool[@name='exists_max_l']='true'"
+ ,"//long[@name='min_l']='-540'"
+ ,"//long[@name='max_l']='420'"
+ // float
+ ,"//bool[@name='exists_min_f']='true'"
+ ,"//bool[@name='exists_max_f']='true'"
+ ,"//float[@name='min_f']='-42.5'"
+ ,"//float[@name='max_f']='-4.5'"
+ // double
+ ,"//bool[@name='exists_min_d']='true'"
+ ,"//bool[@name='exists_max_d']='true'"
+ ,"//double[@name='min_d']='-420.5'"
+ ,"//double[@name='max_d']='-40.5'"
+ );
+
+
+ }
+
+
+ @AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/LUCENE-6709")
+ public void testIntFieldCache() {
+ testSimpleInt("val_tis");
+ }
+
+ public void testIntDocValues() {
+ testSimpleInt("val_tis_dv");
+ testSimpleInt("val_tis_ni_dv");
+ }
+
+ @AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/LUCENE-6709")
+ public void testLongFieldCache() {
+ testSimpleLong("val_tls");
+ }
+
+ public void testLongDocValues() {
+ testSimpleLong("val_tls_dv");
+ testSimpleLong("val_tls_ni_dv");
+ }
+
+
+ @AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/LUCENE-6709")
+ public void testFloatFieldCache() {
+ testSimpleFloat("val_tfs");
+ }
+
+ public void testFloatDocValues() {
+ testSimpleFloat("val_tfs_dv");
+ testSimpleFloat("val_tfs_ni_dv");
+ }
+
+ @AwaitsFix(bugUrl = "https://issues.apache.org/jira/browse/LUCENE-6709")
+ public void testDoubleFieldCache() {
+ testSimpleDouble("val_tds");
+ }
+
+ public void testDoubleDocValues() {
+ testSimpleDouble("val_tds_dv");
+ testSimpleDouble("val_tds_ni_dv");
+ }
+
+ public void testBadRequests() {
+
+ // useful error msg when bogus selector is requested (ie: not min or max)
+ assertQEx("no error asking for bogus selector",
+ "hoss",
+ req("q","*:*", "fl", "field(val_tds_dv,'hoss')"),
+ SolrException.ErrorCode.BAD_REQUEST);
+
+ // useful error until/unless LUCENE-6709
+ assertQEx("no error asking for max on a non docVals field",
+ "val_tds",
+ req("q","*:*", "fl", "field(val_tds,'max')"),
+ SolrException.ErrorCode.BAD_REQUEST);
+ assertQEx("no error asking for max on a non docVals field",
+ "max",
+ req("q","*:*", "fl", "field(val_tds,'max')"),
+ SolrException.ErrorCode.BAD_REQUEST);
+ assertQEx("no error asking for max on a non docVals field",
+ "docValues",
+ req("q","*:*", "fl", "field(val_tds,'max')"),
+ SolrException.ErrorCode.BAD_REQUEST);
+
+ // useful error if min/max is unsupported for fieldtype
+ assertQEx("no error asking for max on a str field",
+ "cat_docValues",
+ req("q","*:*", "fl", "field(cat_docValues,'max')"),
+ SolrException.ErrorCode.BAD_REQUEST);
+ assertQEx("no error asking for max on a str field",
+ "string",
+ req("q","*:*", "fl", "field(cat_docValues,'max')"),
+ SolrException.ErrorCode.BAD_REQUEST);
+
+ }
+
+ public void testRandom() throws Exception {
+
+ Comparable[] vals = new Comparable[TestUtil.nextInt(random(), 1, 17)];
+
+ // random ints
+ for (int i = 0; i < vals.length; i++) {
+ vals[i] = random().nextInt();
+ }
+ testSimpleValues("val_tis_dv", int.class, vals);
+
+ // random longs
+ for (int i = 0; i < vals.length; i++) {
+ vals[i] = random().nextLong();
+ }
+ testSimpleValues("val_tls_dv", long.class, vals);
+
+ // random floats
+ for (int i = 0; i < vals.length; i++) {
+ // Random.nextFloat is lame
+ Float f = Float.NaN;
+ while (f.isNaN()) {
+ f = Float.intBitsToFloat(random().nextInt());
+ }
+ vals[i] = f;
+ }
+ testSimpleValues("val_tfs_dv", float.class, vals);
+
+ // random doubles
+ for (int i = 0; i < vals.length; i++) {
+ // Random.nextDouble is lame
+ Double d = Double.NaN;
+ while (d.isNaN()) {
+ d = Double.longBitsToDouble(random().nextLong());
+ }
+ vals[i] = d;
+ }
+ testSimpleValues("val_tds_dv", double.class, vals);
+
+ }
+
+
+ /** @see #testSimpleValues */
+ protected void testSimpleInt(final String fieldname) {
+ // most basic case
+ testSimpleValues(fieldname, int.class, 0);
+
+ // order of values shouldn't matter
+ testSimpleValues(fieldname, int.class, -42, 51, 3);
+ testSimpleValues(fieldname, int.class, 51, 3, -42);
+
+ // extreme's of the data type
+ testSimpleValues(fieldname, int.class, Integer.MIN_VALUE, 42, -550);
+ testSimpleValues(fieldname, int.class, Integer.MAX_VALUE, 0, Integer.MIN_VALUE);
+ }
+
+ /** @see #testSimpleValues */
+ protected void testSimpleLong(final String fieldname) {
+ // most basic case
+ testSimpleValues(fieldname, long.class, 0);
+
+ // order of values shouldn't matter
+ testSimpleValues(fieldname, long.class, -42L, 51L, 3L);
+ testSimpleValues(fieldname, long.class, 51L, 3L, -42L);
+
+ // extreme's of the data type
+ testSimpleValues(fieldname, long.class, Long.MIN_VALUE, 42L, -550L);
+ testSimpleValues(fieldname, long.class, Long.MAX_VALUE, 0L, Long.MIN_VALUE);
+ }
+
+ /** @see #testSimpleValues */
+ protected void testSimpleFloat(final String fieldname) {
+ // most basic case
+ testSimpleValues(fieldname, float.class, 0.0F);
+
+ // order of values shouldn't matter
+ testSimpleValues(fieldname, float.class, -42.5F, 51.3F, 3.1415F);
+ testSimpleValues(fieldname, float.class, 51.3F, 3.1415F, -42.5F);
+
+ // extreme's of the data type
+ testSimpleValues(fieldname, float.class, Float.NEGATIVE_INFINITY, 42.5F, -550.4F);
+ testSimpleValues(fieldname, float.class, Float.POSITIVE_INFINITY, 0.0F, Float.NEGATIVE_INFINITY);
+ }
+
+ /** @see #testSimpleValues */
+ protected void testSimpleDouble(final String fieldname) {
+ // most basic case
+ testSimpleValues(fieldname, double.class, 0.0D);
+
+ // order of values shouldn't matter
+ testSimpleValues(fieldname, double.class, -42.5D, 51.3D, 3.1415D);
+ testSimpleValues(fieldname, double.class, 51.3D, 3.1415D, -42.5D);
+
+ // extreme's of the data type
+ testSimpleValues(fieldname, double.class, Double.NEGATIVE_INFINITY, 42.5D, -550.4D);
+ testSimpleValues(fieldname, double.class, Double.POSITIVE_INFINITY, 0.0D, Double.NEGATIVE_INFINITY);
+ }
+
+ /** Tests a single doc with a few explicit values, as well as testing exists with and w/o values */
+ protected void testSimpleValues(final String fieldname, final Class clazz, final Comparable... vals) {
+
+ assert 0 < vals.length;
+
+ Comparable min = vals[0];
+ Comparable max = vals[0];
+
+ final String type = clazz.getName();
+ final SolrInputDocument doc1 = sdoc("id", "1");
+ for (Comparable v : vals) {
+ doc1.addField(fieldname, v);
+ if (0 < min.compareTo(v)) {
+ min = v;
+ }
+ if (0 > max.compareTo(v)) {
+ max = v;
+ }
+ }
+ assertU(adoc(doc1));
+ assertU(adoc(sdoc("id", "2"))); // fieldname doesn't exist
+ assertU(commit());
+
+ assertQ(fieldname,
+ req("q","id:1",
+ "fl","exists_val_min:exists(field("+fieldname+",min))",
+ "fl","exists_val_max:exists(field("+fieldname+",max))",
+ "fl","val_min:field("+fieldname+",min)",
+ "fl","val_max:field("+fieldname+",max)")
+ ,"//*[@numFound='1']"
+ ,"//bool[@name='exists_val_min']='true'"
+ ,"//bool[@name='exists_val_max']='true'"
+ ,"//"+type+"[@name='val_min']='"+min+"'"
+ ,"//"+type+"[@name='val_max']='"+max+"'"
+ );
+
+ assertQ(fieldname,
+ req("q","id:2",
+ "fl","exists_val_min:exists(field("+fieldname+",min))",
+ "fl","exists_val_max:exists(field("+fieldname+",max))",
+ "fl","val_min:field("+fieldname+",min)",
+ "fl","val_max:field("+fieldname+",max)")
+ ,"//*[@numFound='1']"
+ ,"//bool[@name='exists_val_min']='false'"
+ ,"//bool[@name='exists_val_max']='false'"
+ ,"count(//"+type+"[@name='val_min'])=0"
+ ,"count(//"+type+"[@name='val_max'])=0"
+ );
+ }
+
+}