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.7.0
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) {
    // null when invoked by SimpleFacets. But getQueryFromSpatialArgs expects to get localParams.
    if (parser == null) {
      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