org.springframework.data.mongodb.repository.query.MongoQueryCreator.java Source code

Java tutorial

Introduction

Here is the source code for org.springframework.data.mongodb.repository.query.MongoQueryCreator.java

Source

/*
 * Copyright 2002-2010 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.Collection;
import java.util.Iterator;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.data.domain.Sort;
import org.springframework.data.mapping.context.MappingContext;
import org.springframework.data.mapping.context.PersistentPropertyPath;
import org.springframework.data.mongodb.core.geo.Distance;
import org.springframework.data.mongodb.core.geo.Point;
import org.springframework.data.mongodb.core.geo.Shape;
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.Type;
import org.springframework.data.repository.query.parser.PartTree;
import org.springframework.util.Assert;

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

    private static final Log LOG = LogFactory.getLog(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());
        Criteria criteria = from(part.getType(),
                where(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
                (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());

        return new Criteria().andOperator(base,
                from(part.getType(),
                        where(path.toDotPath(MongoPersistentProperty.PropertyToFieldNameConverter.INSTANCE)),
                        (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) {

        if (criteria == null) {
            return null;
        }

        Query query = new Query(criteria);
        QueryUtils.applySorting(query, sort);

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

        return query;
    }

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

        switch (type) {
        case GREATER_THAN:
            return criteria.gt(parameters.nextConverted());
        case GREATER_THAN_EQUAL:
            return criteria.gte(parameters.nextConverted());
        case LESS_THAN:
            return criteria.lt(parameters.nextConverted());
        case LESS_THAN_EQUAL:
            return criteria.lte(parameters.nextConverted());
        case BETWEEN:
            return criteria.gt(parameters.nextConverted()).lt(parameters.nextConverted());
        case IS_NOT_NULL:
            return criteria.ne(null);
        case IS_NULL:
            return criteria.is(null);
        case NOT_IN:
            return criteria.nin(nextAsArray(parameters));
        case IN:
            return criteria.in(nextAsArray(parameters));
        case LIKE:
            String value = parameters.next().toString();
            return criteria.regex(toLikeRegex(value));
        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 criteria.is(parameters.nextConverted());
        case NEGATING_SIMPLE_PROPERTY:
            return criteria.not().is(parameters.nextConverted());
        }

        throw new IllegalArgumentException("Unsupported keyword!");
    }

    /**
     * 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) {
        Object next = iterator.nextConverted();

        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) {

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