SOLR-6103: DateRangeField

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1600557 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
David Wayne Smiley 2014-06-05 02:05:27 +00:00
parent 1e24c0218f
commit eee1f14787
4 changed files with 178 additions and 0 deletions

View File

@ -67,6 +67,8 @@ New Features
modifying solr configuration files. (Erick Erickson)
- SOLR-5539: Admin UI - Remove ability to create/modify files (steffkes)
* SOLR-6103: Added DateRangeField for indexing date ranges, especially
multi-valued ones. Based on LUCENE-5648. (David Smiley)
Other Changes
----------------------

View File

@ -0,0 +1,110 @@
package org.apache.solr.schema;
/*
* 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.
*/
import com.spatial4j.core.shape.Shape;
import org.apache.lucene.index.StorableField;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.NumberRangePrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.tree.DateRangePrefixTree;
import org.apache.lucene.spatial.query.SpatialArgs;
import org.apache.lucene.spatial.query.SpatialOperation;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.search.QParser;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
/**
* @see NumberRangePrefixTreeStrategy
* @see DateRangePrefixTree
*/
public class DateRangeField extends AbstractSpatialPrefixTreeFieldType<NumberRangePrefixTreeStrategy> {
private static final String OP_PARAM = "op";//local-param to resolve SpatialOperation
private final DateRangePrefixTree tree = DateRangePrefixTree.INSTANCE;
@Override
protected void init(IndexSchema schema, Map<String, String> args) {
super.init(schema, addDegrees(args));
}
private Map<String, String> addDegrees(Map<String, String> args) {
args.put("units", "degrees");//HACK!
return args;
}
@Override
protected NumberRangePrefixTreeStrategy newPrefixTreeStrategy(String fieldName) {
return new NumberRangePrefixTreeStrategy(tree, fieldName);
}
@Override
public List<StorableField> createFields(SchemaField field, Object val, float boost) {
if (val instanceof Date || val instanceof Calendar)//From URP
val = tree.toShape(val);
return super.createFields(field, val, boost);
}
@Override
protected Shape parseShape(String str) {
try {
return tree.parseShape(str);
} catch (ParseException e) {
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
"Couldn't parse date because: "+ e.getMessage(), e);
}
}
@Override
protected String shapeToString(Shape shape) {
return shape.toString();//generally round-trips for DateRangePrefixTree
}
@Override
protected SpatialArgs parseSpatialArgs(QParser parser, String externalVal) {
//We avoid SpatialArgsParser entirely because it isn't very Solr-friendly
final Shape shape = parseShape(externalVal);
final SolrParams localParams = parser.getLocalParams();
SpatialOperation op = SpatialOperation.Intersects;
if (localParams != null) {
String opStr = localParams.get(OP_PARAM);
if (opStr != null)
op = SpatialOperation.get(opStr);
}
return new SpatialArgs(op, shape);
}
@Override
public Query getRangeQuery(QParser parser, SchemaField field, String part1, String part2, boolean minInclusive, boolean maxInclusive) {
if (!minInclusive || !maxInclusive)
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "exclusive range boundary not supported");
if (part1 == null)
part1 = "*";
if (part2 == null)
part2 = "*";
Shape shape = tree.toRangeShape(parseShape(part1), parseShape(part2));
SpatialArgs spatialArgs = new SpatialArgs(SpatialOperation.Intersects, shape);
return getQueryFromSpatialArgs(parser, field, spatialArgs);
}
}

View File

@ -84,6 +84,7 @@
<fieldtype name="date" class="solr.TrieDateField" precisionStep="0"/>
<fieldtype name="tdate" class="solr.TrieDateField" precisionStep="6"/>
<fieldtype name="dateRange" class="solr.DateRangeField" />
<!-- solr.TextField allows the specification of custom
text analyzers specified as a tokenizer and a list
@ -498,6 +499,7 @@
<field name="nullfirst" type="string" indexed="true" stored="true" sortMissingFirst="true" multiValued="false"/>
<field name="dateRange" type="dateRange" multiValued="false" />
<field name="cat" type="string" indexed="true" stored="true" multiValued="true"/>
<field name="price" type="float" indexed="true" stored="true" multiValued="false"/>

View File

@ -0,0 +1,64 @@
package org.apache.solr.schema;
/*
* 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.
*/
import org.apache.solr.SolrTestCaseJ4;
import org.junit.BeforeClass;
public class DateRangeFieldTest extends SolrTestCaseJ4 {
@BeforeClass
public static void beforeClass() throws Exception {
initCore("solrconfig.xml", "schema.xml");
}
public void test() {
assertU(adoc("id", "0", "dateRange", "[* TO *]"));
assertU(adoc("id", "1", "dateRange", "2014-05-21T12:00:00.000Z"));
assertU(adoc("id", "2", "dateRange", "[2000 TO 2014-05-21]"));
assertU(commit());
//ensure stored value is the same (not toString of Shape)
assertQ(req("q", "id:1", "fl", "dateRange"), "//result/doc/str[.='2014-05-21T12:00:00.000Z']");
String[] commonParams = {"q", "{!field f=dateRange op=$op v=$qq}", "sort", "id asc"};
assertQ(req(commonParams, "qq", "[* TO *]"), xpathMatches(0, 1, 2));
assertQ(req(commonParams, "qq", "2012"), xpathMatches(0, 2));
assertQ(req(commonParams, "qq", "2013", "op", "Contains"), xpathMatches(0, 2));
assertQ(req(commonParams, "qq", "2014", "op", "Contains"), xpathMatches(0));
assertQ(req(commonParams, "qq", "[1999 TO 2001]", "op", "IsWithin"), xpathMatches());
assertQ(req(commonParams, "qq", "2014-05", "op", "IsWithin"), xpathMatches(1));
//show without local-params
assertQ(req("q", "dateRange:\"2014-05-21T12:00:00.000Z\""), xpathMatches(0, 1, 2));
assertQ(req("q", "dateRange:[1999 TO 2001]"), xpathMatches(0, 2));
}
private String[] xpathMatches(int... docIds) {
String[] tests = new String[docIds != null ? docIds.length + 1 : 1];
tests[0] = "*[count(//doc)=" + (tests.length-1) + "]";
if (docIds != null && docIds.length > 0) {
int i = 1;
for (int docId : docIds) {
tests[i++] = "//result/doc/int[@name='id'][.='" + docId + "']";
}
}
return tests;
}
}