You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ds...@apache.org on 2014/06/05 04:05:28 UTC

svn commit: r1600557 - in /lucene/dev/trunk/solr: CHANGES.txt core/src/java/org/apache/solr/schema/DateRangeField.java core/src/test-files/solr/collection1/conf/schema.xml core/src/test/org/apache/solr/schema/DateRangeFieldTest.java

Author: dsmiley
Date: Thu Jun  5 02:05:27 2014
New Revision: 1600557

URL: http://svn.apache.org/r1600557
Log:
SOLR-6103: DateRangeField

Added:
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/DateRangeField.java   (with props)
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/DateRangeFieldTest.java   (with props)
Modified:
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema.xml

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1600557&r1=1600556&r2=1600557&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Thu Jun  5 02:05:27 2014
@@ -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
 ----------------------

Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/DateRangeField.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/DateRangeField.java?rev=1600557&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/DateRangeField.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/DateRangeField.java Thu Jun  5 02:05:27 2014
@@ -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);
+  }
+}

Modified: lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema.xml?rev=1600557&r1=1600556&r2=1600557&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema.xml (original)
+++ lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema.xml Thu Jun  5 02:05:27 2014
@@ -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"/>

Added: lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/DateRangeFieldTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/DateRangeFieldTest.java?rev=1600557&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/DateRangeFieldTest.java (added)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/schema/DateRangeFieldTest.java Thu Jun  5 02:05:27 2014
@@ -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;
+  }
+
+}