Package org.springframework.data.mongodb.repository.query

Source Code of org.springframework.data.mongodb.repository.query.MongoQueryCreator

/*
* Copyright 2010-2013 the original author or authors.
*
* Licensed 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.springframework.data.mongodb.repository.query;

import static org.springframework.data.mongodb.core.query.Criteria.*;

import java.util.Arrays;
import java.util.Collection;
import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.Sort;
import org.springframework.data.geo.Distance;
import org.springframework.data.geo.Point;
import org.springframework.data.geo.Shape;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath;
import org.springframework.data.mongodb.core.mapping.MongoPersistentProperty;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.CriteriaDefinition;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.repository.query.ConvertingParameterAccessor.PotentiallyConvertingIterator;
import org.springframework.data.repository.query.parser.AbstractQueryCreator;
import org.springframework.data.repository.query.parser.Part;
import org.springframework.data.repository.query.parser.Part.IgnoreCaseType;
import org.springframework.data.repository.query.parser.Part.Type;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.Assert;

/**
* Custom query creator to create Mongo criterias.
*
* @author Oliver Gierke
* @author Thomas Darimont
*/
class MongoQueryCreator extends AbstractQueryCreator<Query, Criteria> {

  private static final Logger LOG = LoggerFactory.getLogger(MongoQueryCreator.class);
  private final MongoParameterAccessor accessor;
  private final boolean isGeoNearQuery;

  private final MappingContext<?, MongoPersistentProperty> context;

  /**
   * Creates a new {@link MongoQueryCreator} from the given {@link PartTree}, {@link ConvertingParameterAccessor} and
   * {@link MappingContext}.
   *
   * @param tree
   * @param accessor
   * @param context
   */
  public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor,
      MappingContext<?, MongoPersistentProperty> context) {
    this(tree, accessor, context, false);
  }

  /**
   * Creates a new {@link MongoQueryCreator} from the given {@link PartTree}, {@link ConvertingParameterAccessor} and
   * {@link MappingContext}.
   *
   * @param tree
   * @param accessor
   * @param context
   * @param isGeoNearQuery
   */
  public MongoQueryCreator(PartTree tree, ConvertingParameterAccessor accessor,
      MappingContext<?, MongoPersistentProperty> context, boolean isGeoNearQuery) {

    super(tree, accessor);

    Assert.notNull(context);

    this.accessor = accessor;
    this.isGeoNearQuery = isGeoNearQuery;
    this.context = context;
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.query.parser.AbstractQueryCreator#create(org.springframework.data.repository.query.parser.Part, java.util.Iterator)
   */
  @Override
  protected Criteria create(Part part, Iterator<Object> iterator) {

    if (isGeoNearQuery && part.getType().equals(Type.NEAR)) {
      return null;
    }

    PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
    MongoPersistentProperty property = path.getLeafProperty();
    Criteria criteria = from(part, property, where(path.toDotPath()), (PotentiallyConvertingIterator) iterator);

    return criteria;
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.query.parser.AbstractQueryCreator#and(org.springframework.data.repository.query.parser.Part, java.lang.Object, java.util.Iterator)
   */
  @Override
  protected Criteria and(Part part, Criteria base, Iterator<Object> iterator) {

    if (base == null) {
      return create(part, iterator);
    }

    PersistentPropertyPath<MongoPersistentProperty> path = context.getPersistentPropertyPath(part.getProperty());
    MongoPersistentProperty property = path.getLeafProperty();

    return from(part, property, base.and(path.toDotPath()), (PotentiallyConvertingIterator) iterator);
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.query.parser.AbstractQueryCreator#or(java.lang.Object, java.lang.Object)
   */
  @Override
  protected Criteria or(Criteria base, Criteria criteria) {

    Criteria result = new Criteria();
    return result.orOperator(base, criteria);
  }

  /*
   * (non-Javadoc)
   * @see org.springframework.data.repository.query.parser.AbstractQueryCreator#complete(java.lang.Object, org.springframework.data.domain.Sort)
   */
  @Override
  protected Query complete(Criteria criteria, Sort sort) {

    Query query = (criteria == null ? new Query() : new Query(criteria)).with(sort);

    if (LOG.isDebugEnabled()) {
      LOG.debug("Created query " + query);
    }

    return query;
  }

  /**
   * Populates the given {@link CriteriaDefinition} depending on the {@link Part} given.
   *
   * @param part
   * @param property
   * @param criteria
   * @param parameters
   * @return
   */
  private Criteria from(Part part, MongoPersistentProperty property, Criteria criteria,
      PotentiallyConvertingIterator parameters) {

    Type type = part.getType();

    switch (type) {
      case AFTER:
      case GREATER_THAN:
        return criteria.gt(parameters.nextConverted(property));
      case GREATER_THAN_EQUAL:
        return criteria.gte(parameters.nextConverted(property));
      case BEFORE:
      case LESS_THAN:
        return criteria.lt(parameters.nextConverted(property));
      case LESS_THAN_EQUAL:
        return criteria.lte(parameters.nextConverted(property));
      case BETWEEN:
        return criteria.gt(parameters.nextConverted(property)).lt(parameters.nextConverted(property));
      case IS_NOT_NULL:
        return criteria.ne(null);
      case IS_NULL:
        return criteria.is(null);
      case NOT_IN:
        return criteria.nin(nextAsArray(parameters, property));
      case IN:
        return criteria.in(nextAsArray(parameters, property));
      case LIKE:
      case STARTING_WITH:
      case ENDING_WITH:
      case CONTAINING:
        return addAppropriateLikeRegexTo(criteria, part, parameters.next().toString());
      case REGEX:
        return criteria.regex(parameters.next().toString());
      case EXISTS:
        return criteria.exists((Boolean) parameters.next());
      case TRUE:
        return criteria.is(true);
      case FALSE:
        return criteria.is(false);
      case NEAR:

        Distance distance = accessor.getMaxDistance();
        Point point = accessor.getGeoNearLocation();
        point = point == null ? nextAs(parameters, Point.class) : point;

        if (distance == null) {
          return criteria.near(point);
        } else {
          if (distance.getMetric() != null) {
            criteria.nearSphere(point);
          } else {
            criteria.near(point);
          }
          criteria.maxDistance(distance.getNormalizedValue());
        }
        return criteria;
      case WITHIN:

        Object parameter = parameters.next();
        return criteria.within((Shape) parameter);
      case SIMPLE_PROPERTY:

        return isSimpleComparisionPossible(part) ? criteria.is(parameters.nextConverted(property))
            : createLikeRegexCriteriaOrThrow(part, property, criteria, parameters, false);

      case NEGATING_SIMPLE_PROPERTY:

        return isSimpleComparisionPossible(part) ? criteria.ne(parameters.nextConverted(property))
            : createLikeRegexCriteriaOrThrow(part, property, criteria, parameters, true);
      default:
        throw new IllegalArgumentException("Unsupported keyword!");
    }
  }

  private boolean isSimpleComparisionPossible(Part part) {

    switch (part.shouldIgnoreCase()) {
      case NEVER:
        return true;
      case WHEN_POSSIBLE:
        return part.getProperty().getType() != String.class;
      case ALWAYS:
        return false;
      default:
        return true;
    }
  }

  /**
   * Creates and extends the given criteria with a like-regex if necessary.
   *
   * @param part
   * @param property
   * @param criteria
   * @param parameters
   * @param shouldNegateExpression
   * @return the criteria extended with the like-regex.
   */
  private Criteria createLikeRegexCriteriaOrThrow(Part part, MongoPersistentProperty property, Criteria criteria,
      PotentiallyConvertingIterator parameters, boolean shouldNegateExpression) {

    switch (part.shouldIgnoreCase()) {

      case ALWAYS:
        if (part.getProperty().getType() != String.class) {
          throw new IllegalArgumentException(String.format("part %s must be of type String but was %s",
              part.getProperty(), part.getType()));
        }
        // fall-through

      case WHEN_POSSIBLE:
        if (shouldNegateExpression) {
          criteria = criteria.not();
        }
        return addAppropriateLikeRegexTo(criteria, part, parameters.nextConverted(property).toString());

      case NEVER:
        // intentional no-op
    }

    throw new IllegalArgumentException(String.format("part.shouldCaseIgnore must be one of %s, but was %s",
        Arrays.asList(IgnoreCaseType.ALWAYS, IgnoreCaseType.WHEN_POSSIBLE), part.shouldIgnoreCase()));
  }

  /**
   * Creates an appropriate like-regex and appends it to the given criteria.
   *
   * @param criteria
   * @param part
   * @param value
   * @return the criteria extended with the regex.
   */
  private Criteria addAppropriateLikeRegexTo(Criteria criteria, Part part, String value) {

    return criteria.regex(toLikeRegex(value, part), toRegexOptions(part));
  }

  /**
   * @param part
   * @return the regex options or {@literal null}.
   */
  private String toRegexOptions(Part part) {

    String regexOptions = null;
    switch (part.shouldIgnoreCase()) {
      case WHEN_POSSIBLE:
      case ALWAYS:
        regexOptions = "i";
      case NEVER:
    }
    return regexOptions;
  }

  /**
   * Returns the next element from the given {@link Iterator} expecting it to be of a certain type.
   *
   * @param <T>
   * @param iterator
   * @param type
   * @throws IllegalArgumentException in case the next element in the iterator is not of the given type.
   * @return
   */
  @SuppressWarnings("unchecked")
  private <T> T nextAs(Iterator<Object> iterator, Class<T> type) {
    Object parameter = iterator.next();
    if (parameter.getClass().isAssignableFrom(type)) {
      return (T) parameter;
    }

    throw new IllegalArgumentException(String.format("Expected parameter type of %s but got %s!", type,
        parameter.getClass()));
  }

  private Object[] nextAsArray(PotentiallyConvertingIterator iterator, MongoPersistentProperty property) {
    Object next = iterator.nextConverted(property);

    if (next instanceof Collection) {
      return ((Collection<?>) next).toArray();
    } else if (next.getClass().isArray()) {
      return (Object[]) next;
    }

    return new Object[] { next };
  }

  private String toLikeRegex(String source, Part part) {

    Type type = part.getType();

    switch (type) {
      case STARTING_WITH:
        source = "^" + source;
        break;
      case ENDING_WITH:
        source = source + "$";
        break;
      case CONTAINING:
        source = "*" + source + "*";
        break;
      case SIMPLE_PROPERTY:
      case NEGATING_SIMPLE_PROPERTY:
        source = "^" + source + "$";
      default:
    }

    return source.replaceAll("\\*", ".*");
  }
TOP

Related Classes of org.springframework.data.mongodb.repository.query.MongoQueryCreator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.