All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.solr.schema.DateRangeField Maven / Gradle / Ivy

There is a newer version: 9.6.1
Show newest version
/*
 * 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.schema;

import java.text.ParseException;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;

import org.apache.lucene.index.IndexableField;
import org.apache.lucene.search.Query;
import org.apache.lucene.spatial.prefix.NumberRangePrefixTreeStrategy;
import org.apache.lucene.spatial.prefix.tree.DateRangePrefixTree;
import org.apache.lucene.spatial.prefix.tree.NumberRangePrefixTree.NRShape;
import org.apache.lucene.spatial.prefix.tree.NumberRangePrefixTree.UnitNRShape;
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.request.SolrRequestInfo;
import org.apache.solr.search.QParser;
import org.apache.solr.search.SyntaxError;
import org.apache.solr.util.DateMathParser;
import org.locationtech.spatial4j.shape.Shape;

/**
 * A field for indexed dates and date ranges. It's mostly compatible with DatePointField.  It has the potential to allow
 * efficient faceting, similar to facet.enum.
 *
 * @see NumberRangePrefixTreeStrategy
 * @see DateRangePrefixTree
 */
public class DateRangeField extends AbstractSpatialPrefixTreeFieldType
  implements DateValueFieldType { // used by ParseDateFieldUpdateProcessorFactory

  private static final String OP_PARAM = "op";//local-param to resolve SpatialOperation

  private static final DateRangePrefixTree tree = new DateRangePrefixTree(DateRangePrefixTree.JAVA_UTIL_TIME_COMPAT_CAL);

  @Override
  protected void init(IndexSchema schema, Map args) {
    super.init(schema, args);
  }

  @Override
  protected NumberRangePrefixTreeStrategy newPrefixTreeStrategy(String fieldName) {
    return new NumberRangePrefixTreeStrategy(tree, fieldName);
  }

  @Override
  public List createFields(SchemaField field, Object val) {
    if (val instanceof Date || val instanceof Calendar)//From URP?
      val = tree.toUnitShape(val);
    return super.createFields(field, val);
  }

  @Override
  protected String getStoredValue(Shape shape, String shapeStr) {
    // even if shapeStr is set, it might have included some dateMath, so see if we can resolve it first:
    if (shape instanceof UnitNRShape) {
      UnitNRShape unitShape = (UnitNRShape) shape;
      if (unitShape.getLevel() == tree.getMaxLevels()) {
        //fully precise date. We can be fully compatible with DatePointField (incl. 'Z')
        return shape.toString() + 'Z';
      }
    }
    return (shapeStr == null ? shape.toString() : shapeStr);//we don't normalize ranges here; should we?
  }

  // Won't be called because we override getStoredValue? any way; easy to implement in terms of that
  @Override
  public String shapeToString(Shape shape) {
    return getStoredValue(shape, null);
  }


  @Override
  public NRShape parseShape(String str) {
    if (str.contains(" TO ")) {
      //TODO parsing range syntax doesn't support DateMath on either side or exclusive/inclusive
      try {
        return tree.parseShape(str);
      } catch (ParseException e) {
        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
            "Couldn't parse date because: "+ e.getMessage(), e);
      }
    } else {
      return tree.toShape(parseCalendar(str));
    }
  }

  private Calendar parseCalendar(String str) {
    if (str.startsWith("NOW") || str.lastIndexOf('Z') >= 0) { //  ? but not if Z is last char ?   Ehh, whatever.
      //use Solr standard date format parsing rules:
      //TODO add DMP utility to return ZonedDateTime alternative, then set cal fields manually, which is faster?
      Date date = DateMathParser.parseMath(null, str);
      Calendar cal = tree.newCal();
      cal.setTime(date);
      return cal;
    } else {
      try {
        return tree.parseCalendar(str);
      } catch (ParseException e) {
        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
            "Couldn't parse date because: "+ e.getMessage(), e);
      }

    }
  }

  /** For easy compatibility with {@link DateMathParser#parseMath(Date, String)}. */
  public Date parseMath(Date now, String rawval) {
    return DateMathParser.parseMath(now, rawval);
  }

  @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
  protected Query getSpecializedRangeQuery(QParser parser, SchemaField field, String startStr, String endStr, boolean minInclusive, boolean maxInclusive) {
    if (parser == null) {//null when invoked by SimpleFacets.  But getQueryFromSpatialArgs expects to get localParams.
      final SolrRequestInfo requestInfo = SolrRequestInfo.getRequestInfo();
      parser = new QParser("", null, requestInfo.getReq().getParams(), requestInfo.getReq()) {
        @Override
        public Query parse() throws SyntaxError {
          throw new IllegalStateException();
        }
      };
    }

    Calendar startCal;
    if (startStr == null) {
      startCal = tree.newCal();
    } else {
      startCal = parseCalendar(startStr);
      if (!minInclusive) {
        startCal.add(Calendar.MILLISECOND, 1);
      }
    }
    Calendar endCal;
    if (endStr == null) {
      endCal = tree.newCal();
    } else {
      endCal = parseCalendar(endStr);
      if (!maxInclusive) {
        endCal.add(Calendar.MILLISECOND, -1);
      }
    }
    Shape shape = tree.toRangeShape(tree.toShape(startCal), tree.toShape(endCal));
    SpatialArgs spatialArgs = new SpatialArgs(SpatialOperation.Intersects, shape);
    return getQueryFromSpatialArgs(parser, field, spatialArgs);
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy