SOLR-2348: Fix field types to explicitly generate an error if you attempt to get a ValueSource for a multiValued field.

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1071459 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Chris M. Hostetter 2011-02-17 00:38:04 +00:00
parent 4dd86bf8ab
commit 0d57f3b786
26 changed files with 101 additions and 58 deletions

View File

@ -53,12 +53,13 @@ Upgrading from Solr 3.1-dev
legacy behavior should set a default value for the 'mm' param in
their solrconfig.xml file.
* In previous releases, sorting on fields that are "multiValued"
(either by explicit declaration in schema.xml or by implict behavior
because the "version" attribute on the schema was less then 1.2) did
not generally work, but it would sometimes silently act as if it
succeeded and order the docs arbitrarily. Solr will now fail on any
attempt to sort on a multivalued field
* In previous releases, sorting or evaluating function queries on
fields that were "multiValued" (either by explicit declaration in
schema.xml or by implict behavior because the "version" attribute on
the schema was less then 1.2) did not generally work, but it would
sometimes silently act as if it succeeded and order the docs
arbitrarily. Solr will now fail on any attempt to sort, or apply a
function to, multi-valued fields
@ -182,6 +183,9 @@ Bug Fixes
* SOLR-2339: Fix sorting to explicitly generate an error if you
attempt to sort on a multiValued field. (hossman)
* SOLR-2348: Fix field types to explicitly generate an error if you
attempt to get a ValueSource for a multiValued field. (hossman)
Other Changes
----------------------

View File

@ -85,6 +85,7 @@ public abstract class AbstractSubTypeFieldType extends FieldType implements Sche
//Just set these, delegate everything else to the field type
props.put("indexed", "true");
props.put("stored", "false");
props.put("multiValued", "false");
int p = SchemaField.calcProps(name, type, props);
SchemaField proto = SchemaField.create(name,
type, p, null);

View File

@ -49,6 +49,7 @@ public class BoolField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser qparser) {
field.checkFieldCacheSource(qparser);
return new OrdFieldSource(field.name);
}

View File

@ -47,6 +47,7 @@ public class ByteField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser qparser) {
field.checkFieldCacheSource(qparser);
return new ByteFieldSource( new ByteValuesCreator( field.name, null, CachedArrayCreator.CACHE_VALUES_AND_BITS ) );
}

View File

@ -220,10 +220,6 @@ public class DateField extends FieldType {
return getStringSort(field,reverse);
}
public ValueSource getValueSource(SchemaField field) {
return new OrdFieldSource(field.name);
}
@Override
public void write(TextResponseWriter writer, String name, Fieldable f) throws IOException {
writer.writeDate(name, toExternal(f));
@ -408,6 +404,7 @@ public class DateField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser parser) {
field.checkFieldCacheSource(parser);
return new DateFieldSource(field.getName(), field.getType());
}
@ -499,4 +496,4 @@ class DateFieldSource extends FieldCacheSource {
public int hashCode() {
return hcode + super.hashCode();
};
}
}

View File

@ -47,7 +47,7 @@ public class DoubleField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser qparser) {
// fieldCache doesn't support double
field.checkFieldCacheSource(qparser);
return new DoubleFieldSource( new DoubleValuesCreator( field.name, null, CachedArrayCreator.CACHE_VALUES_AND_BITS ) );
}

View File

@ -496,6 +496,7 @@ public abstract class FieldType extends FieldProperties {
* Lucene FieldCache.)
*/
public ValueSource getValueSource(SchemaField field, QParser parser) {
field.checkFieldCacheSource(parser);
return new StrFieldSource(field.name);
}

View File

@ -45,6 +45,7 @@ public class FloatField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser qparser) {
field.checkFieldCacheSource(qparser);
return new FloatFieldSource( new FloatValuesCreator( field.name, null, CachedArrayCreator.CACHE_VALUES_AND_BITS ) );
}

View File

@ -97,6 +97,7 @@ public class GeoHashField extends FieldType implements SpatialQueryable {
@Override
public ValueSource getValueSource(SchemaField field, QParser parser) {
field.checkFieldCacheSource(parser);
return new StrFieldSource(field.name);
}

View File

@ -45,6 +45,7 @@ public class IntField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser qparser) {
field.checkFieldCacheSource(qparser);
return new IntFieldSource(new IntValuesCreator( field.name, null, CachedArrayCreator.CACHE_VALUES_AND_BITS ) );
}

View File

@ -47,6 +47,7 @@ public class LongField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser qparser) {
field.checkFieldCacheSource(qparser);
return new LongFieldSource( new LongValuesCreator( field.name, null, CachedArrayCreator.CACHE_VALUES_AND_BITS ) );
}

View File

@ -22,6 +22,7 @@ import org.apache.solr.common.SolrException.ErrorCode;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.Fieldable;
import org.apache.lucene.search.SortField;
import org.apache.solr.search.QParser;
import org.apache.solr.response.TextResponseWriter;
@ -133,8 +134,8 @@ public final class SchemaField extends FieldProperties {
/**
* Sanity checks that the properties of this field type are plausible
* for a field that may be used in sorting, throwing an appropraite
* exception (including hte field name) if it is not. FieldType subclasses
* for a field that may be used in sorting, throwing an appropriate
* exception (including the field name) if it is not. FieldType subclasses
* can choose to call this method in their getSortField implementation
* @see FieldType#getSortField
*/
@ -152,6 +153,27 @@ public final class SchemaField extends FieldProperties {
}
/**
* Sanity checks that the properties of this field type are plausible
* for a field that may be used to get a FieldCacheSource, throwing
* an appropriate exception (including the field name) if it is not.
* FieldType subclasses can choose to call this method in their
* getValueSource implementation
* @see FieldType#getValueSource
*/
public void checkFieldCacheSource(QParser parser) throws SolrException {
if (! indexed() ) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"can not use FieldCache on unindexed field: "
+ getName());
}
if ( multiValued() ) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"can not use FieldCache on multivalued field: "
+ getName());
}
}
static SchemaField create(String name, FieldType ft, Map<String,String> props) {

View File

@ -50,7 +50,7 @@ public class ShortField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser qparser) {
field.checkFieldCacheSource(qparser);
return new ShortFieldSource(new ShortValuesCreator( field.name, null, CachedArrayCreator.CACHE_VALUES_AND_BITS ) );
}

View File

@ -53,6 +53,7 @@ public class SortableDoubleField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser qparser) {
field.checkFieldCacheSource(qparser);
return new SortableDoubleFieldSource(field.name);
}

View File

@ -53,6 +53,7 @@ public class SortableFloatField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser qparser) {
field.checkFieldCacheSource(qparser);
return new SortableFloatFieldSource(field.name);
}

View File

@ -53,6 +53,7 @@ public class SortableIntField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser qparser) {
field.checkFieldCacheSource(qparser);
return new SortableIntFieldSource(field.name);
}

View File

@ -53,6 +53,7 @@ public class SortableLongField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser qparser) {
field.checkFieldCacheSource(qparser);
return new SortableLongFieldSource(field.name);
}

View File

@ -48,6 +48,7 @@ public class StrField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser parser) {
field.checkFieldCacheSource(parser);
return new StrFieldSource(field.getName());
}

View File

@ -82,13 +82,9 @@ public class TrieDateField extends DateField {
return new SortField(new LongValuesCreator( field.getName(), FieldCache.NUMERIC_UTILS_LONG_PARSER, CachedArrayCreator.CACHE_VALUES_AND_BITS ), top);
}
@Override
public ValueSource getValueSource(SchemaField field) {
return new TrieDateFieldSource( new LongValuesCreator( field.getName(), FieldCache.NUMERIC_UTILS_LONG_PARSER, CachedArrayCreator.CACHE_VALUES_AND_BITS ));
}
@Override
public ValueSource getValueSource(SchemaField field, QParser parser) {
field.checkFieldCacheSource(parser);
return new TrieDateFieldSource( new LongValuesCreator( field.getName(), FieldCache.NUMERIC_UTILS_LONG_PARSER, CachedArrayCreator.CACHE_VALUES_AND_BITS ));
}

View File

@ -179,6 +179,7 @@ public class TrieField extends FieldType {
@Override
public ValueSource getValueSource(SchemaField field, QParser qparser) {
field.checkFieldCacheSource(qparser);
int flags = CachedArrayCreator.CACHE_VALUES_AND_BITS;
switch (type) {
case INTEGER:

View File

@ -385,7 +385,7 @@
<fieldType name="x" class="solr.PointType" dimension="1" subFieldType="double"/>
<fieldType name="tenD" class="solr.PointType" dimension="10" subFieldType="double"/>
<!-- Use the sub field suffix -->
<fieldType name="xyd" class="solr.PointType" dimension="2" subFieldSuffix="*_d"/>
<fieldType name="xyd" class="solr.PointType" dimension="2" subFieldSuffix="_d1"/>
<fieldtype name="geohash" class="solr.GeoHashField"/>
@ -533,6 +533,7 @@
<dynamicField name="*_tf1" type="tfloat" indexed="true" stored="true" multiValued="false"/>
<dynamicField name="*_td" type="tdouble" indexed="true" stored="true"/>
<dynamicField name="*_td1" type="tdouble" indexed="true" stored="true" multiValued="false"/>
<dynamicField name="*_tds" type="tdouble" indexed="true" stored="true" multiValued="false"/>
<dynamicField name="*_tdt" type="tdate" indexed="true" stored="true"/>
<dynamicField name="*_tdt1" type="tdate" indexed="true" stored="true" multiValued="false"/>

View File

@ -102,10 +102,10 @@ public class QueryParsingTest extends SolrTestCaseJ4 {
assertEquals(flds[0].getField(), "pow(float(weight),const(2.0))");
//test functions (more deep)
sort = QueryParsing.parseSort("sum(product(r_f,sum(d_f,t_f,1)),a_f) asc", req);
sort = QueryParsing.parseSort("sum(product(r_f1,sum(d_f1,t_f1,1)),a_f1) asc", req);
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.CUSTOM);
assertEquals(flds[0].getField(), "sum(product(float(r_f),sum(float(d_f),float(t_f),const(1.0))),float(a_f))");
assertEquals(flds[0].getField(), "sum(product(float(r_f1),sum(float(d_f1),float(t_f1),const(1.0))),float(a_f1))");
sort = QueryParsing.parseSort("pow(weight, 2) desc", req);
flds = sort.getSort();
@ -135,11 +135,11 @@ public class QueryParsingTest extends SolrTestCaseJ4 {
assertEquals(flds[0].getField(), "weight");
//Test literals in functions
sort = QueryParsing.parseSort("strdist(foo_s, \"junk\", jw) desc", req);
sort = QueryParsing.parseSort("strdist(foo_s1, \"junk\", jw) desc", req);
flds = sort.getSort();
assertEquals(flds[0].getType(), SortField.CUSTOM);
//the value sources get wrapped, so the out field is different than the input
assertEquals(flds[0].getField(), "strdist(str(foo_s),literal(junk), dist=org.apache.lucene.search.spell.JaroWinklerDistance)");
assertEquals(flds[0].getField(), "strdist(str(foo_s1),literal(junk), dist=org.apache.lucene.search.spell.JaroWinklerDistance)");
sort = QueryParsing.parseSort("", req);
assertNull(sort);

View File

@ -59,18 +59,18 @@ public class TestIndexSearcher extends SolrTestCaseJ4 {
public void testReopen() throws Exception {
assertU(adoc("id","1", "v_t","Hello Dude", "v_s","string1"));
assertU(adoc("id","2", "v_t","Hello Yonik", "v_s","string2"));
assertU(adoc("id","1", "v_t","Hello Dude", "v_s1","string1"));
assertU(adoc("id","2", "v_t","Hello Yonik", "v_s1","string2"));
assertU(commit());
SolrQueryRequest sr1 = req("q","foo");
ReaderContext rCtx1 = sr1.getSearcher().getTopReaderContext();
String sval1 = getStringVal(sr1, "v_s",0);
String sval1 = getStringVal(sr1, "v_s1",0);
assertEquals("string1", sval1);
assertU(adoc("id","3", "v_s","{!literal}"));
assertU(adoc("id","4", "v_s","other stuff"));
assertU(adoc("id","3", "v_s1","{!literal}"));
assertU(adoc("id","4", "v_s1","other stuff"));
assertU(commit());
SolrQueryRequest sr2 = req("q","foo");
@ -81,7 +81,7 @@ public class TestIndexSearcher extends SolrTestCaseJ4 {
assertEquals(ReaderUtil.leaves(rCtx1)[0].reader, ReaderUtil.leaves(rCtx2)[0].reader);
assertU(adoc("id","5", "v_f","3.14159"));
assertU(adoc("id","6", "v_f","8983", "v_s","string6"));
assertU(adoc("id","6", "v_f","8983", "v_s1","string6"));
assertU(commit());
SolrQueryRequest sr3 = req("q","foo");
@ -129,4 +129,4 @@ public class TestIndexSearcher extends SolrTestCaseJ4 {
sr5.close();
sr6.close();
}
}
}

View File

@ -87,7 +87,6 @@ public class TestQueryTypes extends AbstractSolrTestCase {
,"//*[@name='id'][.='999.0']"
,"//*[@name='" + f + "'][.='" + v + "']"
);
// System.out.println("#########################################" + f + "=" + v);
// field qparser
assertQ(req( "q", "{!field f="+f+"}"+v)
@ -98,20 +97,34 @@ public class TestQueryTypes extends AbstractSolrTestCase {
assertQ(req( "q", f + ":[\"" + v + "\" TO \"" + v + "\"]" )
,"//result[@numFound='1']"
);
}
// frange and function query only work on single valued field types
Object[] fc_vals = new Object[] {
"id",999.0
,"v_s","wow dude"
,"v_ti",-1
,"v_tl",-1234567891234567890L
,"v_tf",-2.0f
,"v_td",-2.0
,"v_tdt","2000-05-10T01:01:01Z"
};
for (int i=0; i<fc_vals.length; i+=2) {
String f = fc_vals[i].toString();
String v = fc_vals[i+1].toString();
// frange qparser
assertQ(req( "q", "{!frange v="+f+" l='"+v+"' u='"+v+"'}" )
,"//result[@numFound='1']"
);
// function query... just make sure it doesn't throw an exception
assertQ(req( "q", "+id:999 _val_:\"" + f + "\"")
,"//result[@numFound='1']"
);
assertQ(req( "q", "+id:999 _val_:\"" + f + "\"")
,"//result[@numFound='1']"
);
}
// Some basic tests to ensure that parsing local params is working
assertQ("test prefix query",
req("q","{!prefix f=v_t}hel")
@ -318,4 +331,4 @@ public class TestQueryTypes extends AbstractSolrTestCase {
);
}
}
}

View File

@ -35,10 +35,10 @@ public class SortByFunctionTest extends AbstractSolrTestCase {
}
public void test() throws Exception {
assertU(adoc("id", "1", "x_td", "0", "y_td", "2", "w_td1", "25", "z_td", "5", "f_t", "ipod"));
assertU(adoc("id", "2", "x_td", "2", "y_td", "2", "w_td1", "15", "z_td", "5", "f_t", "ipod ipod ipod ipod ipod"));
assertU(adoc("id", "3", "x_td", "3", "y_td", "2", "w_td1", "55", "z_td", "5", "f_t", "ipod ipod ipod ipod ipod ipod ipod ipod ipod"));
assertU(adoc("id", "4", "x_td", "4", "y_td", "2", "w_td1", "45", "z_td", "5", "f_t", "ipod ipod ipod ipod ipod ipod ipod"));
assertU(adoc("id", "1", "x_td1", "0", "y_td1", "2", "w_td1", "25", "z_td1", "5", "f_t", "ipod"));
assertU(adoc("id", "2", "x_td1", "2", "y_td1", "2", "w_td1", "15", "z_td1", "5", "f_t", "ipod ipod ipod ipod ipod"));
assertU(adoc("id", "3", "x_td1", "3", "y_td1", "2", "w_td1", "55", "z_td1", "5", "f_t", "ipod ipod ipod ipod ipod ipod ipod ipod ipod"));
assertU(adoc("id", "4", "x_td1", "4", "y_td1", "2", "w_td1", "45", "z_td1", "5", "f_t", "ipod ipod ipod ipod ipod ipod ipod"));
assertU(commit());
assertQ(req("fl", "*,score", "q", "*:*"),
@ -66,7 +66,7 @@ public class SortByFunctionTest extends AbstractSolrTestCase {
);
assertQ(req("fl", "*,score", "q", "*:*", "sort", "sum(x_td, y_td) desc"),
assertQ(req("fl", "*,score", "q", "*:*", "sort", "sum(x_td1, y_td1) desc"),
"//*[@numFound='4']",
"//float[@name='score']='1.0'",
"//result/doc[1]/int[@name='id'][.='4']",
@ -74,7 +74,7 @@ public class SortByFunctionTest extends AbstractSolrTestCase {
"//result/doc[3]/int[@name='id'][.='2']",
"//result/doc[4]/int[@name='id'][.='1']"
);
assertQ(req("fl", "*,score", "q", "*:*", "sort", "sum(x_td, y_td) asc"),
assertQ(req("fl", "*,score", "q", "*:*", "sort", "sum(x_td1, y_td1) asc"),
"//*[@numFound='4']",
"//float[@name='score']='1.0'",
"//result/doc[1]/int[@name='id'][.='1']",
@ -83,7 +83,7 @@ public class SortByFunctionTest extends AbstractSolrTestCase {
"//result/doc[4]/int[@name='id'][.='4']"
);
//the function is equal, w_td1 separates
assertQ(req("q", "*:*", "fl", "id", "sort", "sum(z_td, y_td) asc, w_td1 asc"),
assertQ(req("q", "*:*", "fl", "id", "sort", "sum(z_td1, y_td1) asc, w_td1 asc"),
"//*[@numFound='4']",
"//result/doc[1]/int[@name='id'][.='2']",
"//result/doc[2]/int[@name='id'][.='1']",
@ -124,7 +124,3 @@ public class SortByFunctionTest extends AbstractSolrTestCase {
);
}
}
/*
<lst name="responseHeader"><int name="status">0</int><int name="QTime">93</int></lst><result name="response" numFound="4" start="0" maxScore="1.0"><doc><float name="score">1.0</float><int name="id">4</int><int name="intDefault">42</int><arr name="multiDefault"><str>muLti-Default</str></arr><date name="timestamp">2009-12-12T12:59:46.412Z</date><arr name="x_td"><double>4.0</double></arr><arr name="y_td"><double>2.0</double></arr></doc><doc><float name="score">1.0</float><int name="id">3</int><int name="intDefault">42</int><arr name="multiDefault"><str>muLti-Default</str></arr><date name="timestamp">2009-12-12T12:59:46.409Z</date><arr name="x_td"><double>3.0</double></arr><arr name="y_td"><double>2.0</double></arr></doc><doc><float name="score">1.0</float><int name="id">2</int><int name="intDefault">42</int><arr name="multiDefault"><str>muLti-Default</str></arr><date name="timestamp">2009-12-12T12:59:46.406Z</date><arr name="x_td"><double>2.0</double></arr><arr name="y_td"><double>2.0</double></arr></doc><doc><float name="score">1.0</float><int name="id">1</int><int name="intDefault">42</int><arr name="multiDefault"><str>muLti-Default</str></arr><date name="timestamp">2009-12-12T12:59:46.361Z</date><arr name="x_td"><double>0.0</double></arr><arr name="y_td"><double>2.0</double></arr></doc></result>
*/

View File

@ -36,12 +36,12 @@ public class DistanceFunctionTest extends SolrTestCaseJ4 {
@Test
public void testHaversine() throws Exception {
clearIndex();
assertU(adoc("id", "1", "x_td", "0", "y_td", "0", "gh_s", GeoHashUtils.encode(32.7693246, -79.9289094)));
assertU(adoc("id", "2", "x_td", "0", "y_td", String.valueOf(Math.PI / 2), "gh_s", GeoHashUtils.encode(32.7693246, -78.9289094)));
assertU(adoc("id", "3", "x_td", String.valueOf(Math.PI / 2), "y_td", String.valueOf(Math.PI / 2), "gh_s", GeoHashUtils.encode(32.7693246, -80.9289094)));
assertU(adoc("id", "4", "x_td", String.valueOf(Math.PI / 4), "y_td", String.valueOf(Math.PI / 4), "gh_s", GeoHashUtils.encode(32.7693246, -81.9289094)));
assertU(adoc("id", "1", "x_td", "0", "y_td", "0", "gh_s1", GeoHashUtils.encode(32.7693246, -79.9289094)));
assertU(adoc("id", "2", "x_td", "0", "y_td", String.valueOf(Math.PI / 2), "gh_s1", GeoHashUtils.encode(32.7693246, -78.9289094)));
assertU(adoc("id", "3", "x_td", String.valueOf(Math.PI / 2), "y_td", String.valueOf(Math.PI / 2), "gh_s1", GeoHashUtils.encode(32.7693246, -80.9289094)));
assertU(adoc("id", "4", "x_td", String.valueOf(Math.PI / 4), "y_td", String.valueOf(Math.PI / 4), "gh_s1", GeoHashUtils.encode(32.7693246, -81.9289094)));
assertU(adoc("id", "5", "x_td", "45.0", "y_td", "45.0",
"gh_s", GeoHashUtils.encode(32.7693246, -81.9289094)));
"gh_s1", GeoHashUtils.encode(32.7693246, -81.9289094)));
assertU(adoc("id", "6", "point_hash", "32.5, -79.0", "point", "32.5, -79.0"));
assertU(adoc("id", "7", "point_hash", "32.6, -78.0", "point", "32.6, -78.0"));
assertU(commit());
@ -56,7 +56,7 @@ public class DistanceFunctionTest extends SolrTestCaseJ4 {
//Geo Hash Haversine
//Can verify here: http://www.movable-type.co.uk/scripts/latlong.html, but they use a slightly different radius for the earth, so just be close
assertQ(req("fl", "*,score", "q", "{!func}ghhsin(" + DistanceUtils.EARTH_MEAN_RADIUS_KM + ", gh_s, \"" + GeoHashUtils.encode(32, -79) +
assertQ(req("fl", "*,score", "q", "{!func}ghhsin(" + DistanceUtils.EARTH_MEAN_RADIUS_KM + ", gh_s1, \"" + GeoHashUtils.encode(32, -79) +
"\",)", "fq", "id:1"), "//float[@name='score']='122.171875'");
assertQ(req("fl", "id,point_hash,score", "q", "{!func}recip(ghhsin(" + DistanceUtils.EARTH_MEAN_RADIUS_KM + ", point_hash, \"" + GeoHashUtils.encode(32, -79) + "\"), 1, 1, 0)"),
@ -66,7 +66,7 @@ public class DistanceFunctionTest extends SolrTestCaseJ4 {
);
assertQ(req("fl", "*,score", "q", "{!func}ghhsin(" + DistanceUtils.EARTH_MEAN_RADIUS_KM + ", gh_s, geohash(32, -79))", "fq", "id:1"), "//float[@name='score']='122.171875'");
assertQ(req("fl", "*,score", "q", "{!func}ghhsin(" + DistanceUtils.EARTH_MEAN_RADIUS_KM + ", gh_s1, geohash(32, -79))", "fq", "id:1"), "//float[@name='score']='122.171875'");
}