/*
* Copyright 2008-present MongoDB, Inc.
*
* 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 com.mongodb;
import com.mongodb.lang.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.regex.Pattern;
import static com.mongodb.assertions.Assertions.notNull;
import static java.util.Arrays.asList;
Utility for creating DBObject queries
@mongodb.driver.manual tutorial/query-documents/ Querying
/**
* Utility for creating DBObject queries
*
* @mongodb.driver.manual tutorial/query-documents/ Querying
*/
@SuppressWarnings("rawtypes")
public class QueryBuilder {
Creates a builder with an empty query
/**
* Creates a builder with an empty query
*/
public QueryBuilder() {
_query = new BasicDBObject();
}
Returns a new QueryBuilder.
Returns: a builder
/**
* Returns a new QueryBuilder.
*
* @return a builder
*/
public static QueryBuilder start() {
return new QueryBuilder();
}
Creates a new query with a document key
Params: - key – MongoDB document key
Returns: this
/**
* Creates a new query with a document key
*
* @param key MongoDB document key
* @return {@code this}
*/
public static QueryBuilder start(final String key) {
return (new QueryBuilder()).put(key);
}
Adds a new key to the query if not present yet. Sets this key as the current key.
Params: - key – MongoDB document key
Returns: this
/**
* Adds a new key to the query if not present yet. Sets this key as the current key.
*
* @param key MongoDB document key
* @return {@code this}
*/
public QueryBuilder put(final String key) {
_currentKey = key;
if (_query.get(key) == null) {
_query.put(_currentKey, new NullObject());
}
return this;
}
Equivalent to QueryBuilder.put(key)
. Intended for compound query chains to be more readable, e.g.
QueryBuilder.start("a").greaterThan(1).and("b").lessThan(3)
Params: - key – MongoDB document key
Returns: this
/**
* Equivalent to {@code QueryBuilder.put(key)}. Intended for compound query chains to be more readable, e.g. {@code
* QueryBuilder.start("a").greaterThan(1).and("b").lessThan(3) }
*
* @param key MongoDB document key
* @return {@code this}
*/
public QueryBuilder and(final String key) {
return put(key);
}
Equivalent to the $gt operator
Params: - object – Value to query
Returns: this
/**
* Equivalent to the $gt operator
*
* @param object Value to query
* @return {@code this}
*/
public QueryBuilder greaterThan(final Object object) {
addOperand(QueryOperators.GT, object);
return this;
}
Equivalent to the $gte operator
Params: - object – Value to query
Returns: this
/**
* Equivalent to the $gte operator
*
* @param object Value to query
* @return {@code this}
*/
public QueryBuilder greaterThanEquals(final Object object) {
addOperand(QueryOperators.GTE, object);
return this;
}
Equivalent to the $lt operand
Params: - object – Value to query
Returns: this
/**
* Equivalent to the $lt operand
*
* @param object Value to query
* @return {@code this}
*/
public QueryBuilder lessThan(final Object object) {
addOperand(QueryOperators.LT, object);
return this;
}
Equivalent to the $lte operand
Params: - object – Value to query
Returns: this
/**
* Equivalent to the $lte operand
*
* @param object Value to query
* @return {@code this}
*/
public QueryBuilder lessThanEquals(final Object object) {
addOperand(QueryOperators.LTE, object);
return this;
}
Equivalent of the find({key:value})
Params: - object – Value to query
Returns: this
/**
* Equivalent of the find({key:value})
*
* @param object Value to query
* @return {@code this}
*/
public QueryBuilder is(final Object object) {
addOperand(null, object);
return this;
}
Equivalent of the $ne operand
Params: - object – Value to query
Returns: this
/**
* Equivalent of the $ne operand
*
* @param object Value to query
* @return {@code this}
*/
public QueryBuilder notEquals(final Object object) {
addOperand(QueryOperators.NE, object);
return this;
}
Equivalent of the $in operand
Params: - object – Value to query
Returns: this
/**
* Equivalent of the $in operand
*
* @param object Value to query
* @return {@code this}
*/
public QueryBuilder in(final Object object) {
addOperand(QueryOperators.IN, object);
return this;
}
Equivalent of the $nin operand
Params: - object – Value to query
Returns: this
/**
* Equivalent of the $nin operand
*
* @param object Value to query
* @return {@code this}
*/
public QueryBuilder notIn(final Object object) {
addOperand(QueryOperators.NIN, object);
return this;
}
Equivalent of the $mod operand
Params: - object – Value to query
Returns: this
/**
* Equivalent of the $mod operand
*
* @param object Value to query
* @return {@code this}
*/
public QueryBuilder mod(final Object object) {
addOperand(QueryOperators.MOD, object);
return this;
}
Equivalent of the $all operand
Params: - object – Value to query
Returns: this
/**
* Equivalent of the $all operand
*
* @param object Value to query
* @return {@code this}
*/
public QueryBuilder all(final Object object) {
addOperand(QueryOperators.ALL, object);
return this;
}
Equivalent of the $size operand
Params: - object – Value to query
Returns: this
/**
* Equivalent of the $size operand
*
* @param object Value to query
* @return {@code this}
*/
public QueryBuilder size(final Object object) {
addOperand(QueryOperators.SIZE, object);
return this;
}
Equivalent of the $exists operand
Params: - object – Value to query
Returns: this
/**
* Equivalent of the $exists operand
*
* @param object Value to query
* @return {@code this}
*/
public QueryBuilder exists(final Object object) {
addOperand(QueryOperators.EXISTS, object);
return this;
}
Passes a regular expression for a query
Params: - regex – Regex pattern object
Returns: this
/**
* Passes a regular expression for a query
*
* @param regex Regex pattern object
* @return {@code this}
*/
public QueryBuilder regex(final Pattern regex) {
addOperand(null, regex);
return this;
}
Equivalent to the $elemMatch operand
Params: - match – the object to match
Returns: this
/**
* Equivalent to the $elemMatch operand
*
* @param match the object to match
* @return {@code this}
*/
public QueryBuilder elemMatch(final DBObject match) {
addOperand(QueryOperators.ELEM_MATCH, match);
return this;
}
Equivalent of the $within operand, used for geospatial operation
Params: - x – x coordinate
- y – y coordinate
- radius – radius
Returns: this
/**
* Equivalent of the $within operand, used for geospatial operation
*
* @param x x coordinate
* @param y y coordinate
* @param radius radius
* @return {@code this}
*/
public QueryBuilder withinCenter(final double x, final double y, final double radius) {
addOperand(QueryOperators.WITHIN,
new BasicDBObject(QueryOperators.CENTER, asList(asList(x, y), radius)));
return this;
}
Equivalent of the $near operand
Params: - x – x coordinate
- y – y coordinate
Returns: this
/**
* Equivalent of the $near operand
*
* @param x x coordinate
* @param y y coordinate
* @return {@code this}
*/
public QueryBuilder near(final double x, final double y) {
addOperand(QueryOperators.NEAR,
asList(x, y));
return this;
}
Equivalent of the $near operand
Params: - x – x coordinate
- y – y coordinate
- maxDistance – max distance
Returns: this
/**
* Equivalent of the $near operand
*
* @param x x coordinate
* @param y y coordinate
* @param maxDistance max distance
* @return {@code this}
*/
public QueryBuilder near(final double x, final double y, final double maxDistance) {
addOperand(QueryOperators.NEAR,
asList(x, y));
addOperand(QueryOperators.MAX_DISTANCE,
maxDistance);
return this;
}
Equivalent of the $nearSphere operand
Params: - longitude – coordinate in decimal degrees
- latitude – coordinate in decimal degrees
Returns: this
/**
* Equivalent of the $nearSphere operand
*
* @param longitude coordinate in decimal degrees
* @param latitude coordinate in decimal degrees
* @return {@code this}
*/
public QueryBuilder nearSphere(final double longitude, final double latitude) {
addOperand(QueryOperators.NEAR_SPHERE,
asList(longitude, latitude));
return this;
}
Equivalent of the $nearSphere operand
Params: - longitude – coordinate in decimal degrees
- latitude – coordinate in decimal degrees
- maxDistance – max spherical distance
Returns: this
/**
* Equivalent of the $nearSphere operand
*
* @param longitude coordinate in decimal degrees
* @param latitude coordinate in decimal degrees
* @param maxDistance max spherical distance
* @return {@code this}
*/
public QueryBuilder nearSphere(final double longitude, final double latitude, final double maxDistance) {
addOperand(QueryOperators.NEAR_SPHERE,
asList(longitude, latitude));
addOperand(QueryOperators.MAX_DISTANCE,
maxDistance);
return this;
}
Equivalent of the $centerSphere operand mostly intended for queries up to a few hundred miles or km.
Params: - longitude – coordinate in decimal degrees
- latitude – coordinate in decimal degrees
- maxDistance – max spherical distance
Returns: this
/**
* Equivalent of the $centerSphere operand mostly intended for queries up to a few hundred miles or km.
*
* @param longitude coordinate in decimal degrees
* @param latitude coordinate in decimal degrees
* @param maxDistance max spherical distance
* @return {@code this}
*/
public QueryBuilder withinCenterSphere(final double longitude, final double latitude, final double maxDistance) {
addOperand(QueryOperators.WITHIN,
new BasicDBObject(QueryOperators.CENTER_SPHERE,
asList(asList(longitude, latitude), maxDistance)));
return this;
}
Equivalent to a $within operand, based on a bounding box using represented by two corners
Params: - x – the x coordinate of the first box corner.
- y – the y coordinate of the first box corner.
- x2 – the x coordinate of the second box corner.
- y2 – the y coordinate of the second box corner.
Returns: this
/**
* Equivalent to a $within operand, based on a bounding box using represented by two corners
*
* @param x the x coordinate of the first box corner.
* @param y the y coordinate of the first box corner.
* @param x2 the x coordinate of the second box corner.
* @param y2 the y coordinate of the second box corner.
* @return {@code this}
*/
@SuppressWarnings("unchecked")
public QueryBuilder withinBox(final double x, final double y, final double x2, final double y2) {
addOperand(QueryOperators.WITHIN,
new BasicDBObject(QueryOperators.BOX, new Object[]{new Double[]{x, y}, new Double[]{x2, y2}}));
return this;
}
Equivalent to a $within operand, based on a bounding polygon represented by an array of points
Params: - points – an array of Double[] defining the vertices of the search area
Returns: this
/**
* Equivalent to a $within operand, based on a bounding polygon represented by an array of points
*
* @param points an array of Double[] defining the vertices of the search area
* @return {@code this}
*/
public QueryBuilder withinPolygon(final List<Double[]> points) {
notNull("points", points);
if (points.isEmpty() || points.size() < 3) {
throw new IllegalArgumentException("Polygon insufficient number of vertices defined");
}
addOperand(QueryOperators.WITHIN,
new BasicDBObject(QueryOperators.POLYGON, convertToListOfLists(points)));
return this;
}
private List<List<Double>> convertToListOfLists(final List<Double[]> points) {
List<List<Double>> listOfLists = new ArrayList<List<Double>>(points.size());
for (Double[] cur : points) {
List<Double> list = new ArrayList<Double>(cur.length);
Collections.addAll(list, cur);
listOfLists.add(list);
}
return listOfLists;
}
Equivalent to a $text operand.
Params: - search – the search terms to apply to the text index.
Returns: this
@mongodb.server.release 2.6
/**
* Equivalent to a $text operand.
*
* @param search the search terms to apply to the text index.
* @return {@code this}
* @mongodb.server.release 2.6
*/
public QueryBuilder text(final String search) {
return text(search, null);
}
Equivalent to a $text operand.
Params: - search – the search terms to apply to the text index.
- language – the language to use.
Returns: this
@mongodb.server.release 2.6
/**
* Equivalent to a $text operand.
*
* @param search the search terms to apply to the text index.
* @param language the language to use.
* @return {@code this}
* @mongodb.server.release 2.6
*/
public QueryBuilder text(final String search, @Nullable final String language) {
if (_currentKey != null) {
throw new QueryBuilderException("The text operand may only occur at the top-level of a query. It does"
+ " not apply to a specific element, but rather to a document as a whole.");
}
put(QueryOperators.TEXT);
addOperand(QueryOperators.SEARCH, search);
if (language != null) {
addOperand(QueryOperators.LANGUAGE, language);
}
return this;
}
Equivalent to $not meta operator. Must be followed by an operand, not a value, e.g.
QueryBuilder.start("val").not().mod(Arrays.asList(10, 1))
Returns: this
/**
* Equivalent to $not meta operator. Must be followed by an operand, not a value, e.g. {@code
* QueryBuilder.start("val").not().mod(Arrays.asList(10, 1)) }
*
* @return {@code this}
*/
public QueryBuilder not() {
_hasNot = true;
return this;
}
Equivalent to an $or operand
Params: - ors – the list of conditions to or together
Returns: this
/**
* Equivalent to an $or operand
*
* @param ors the list of conditions to or together
* @return {@code this}
*/
@SuppressWarnings("unchecked")
public QueryBuilder or(final DBObject... ors) {
List l = (List) _query.get(QueryOperators.OR);
if (l == null) {
l = new ArrayList();
_query.put(QueryOperators.OR, l);
}
Collections.addAll(l, ors);
return this;
}
Equivalent to an $and operand
Params: - ands – the list of conditions to and together
Returns: this
/**
* Equivalent to an $and operand
*
* @param ands the list of conditions to and together
* @return {@code this}
*/
@SuppressWarnings("unchecked")
public QueryBuilder and(final DBObject... ands) {
List l = (List) _query.get(QueryOperators.AND);
if (l == null) {
l = new ArrayList();
_query.put(QueryOperators.AND, l);
}
Collections.addAll(l, ands);
return this;
}
Creates a DBObject
query to be used for the driver's find operations Throws: - RuntimeException – if a key does not have a matching operand
Returns: this
/**
* Creates a {@code DBObject} query to be used for the driver's find operations
*
* @return {@code this}
* @throws RuntimeException if a key does not have a matching operand
*/
public DBObject get() {
for (final String key : _query.keySet()) {
if (_query.get(key) instanceof NullObject) {
throw new QueryBuilderException("No operand for key:" + key);
}
}
return _query;
}
private void addOperand(@Nullable final String op, final Object value) {
Object valueToPut = value;
if (op == null) {
if (_hasNot) {
valueToPut = new BasicDBObject(QueryOperators.NOT, valueToPut);
_hasNot = false;
}
_query.put(_currentKey, valueToPut);
return;
}
Object storedValue = _query.get(_currentKey);
BasicDBObject operand;
if (!(storedValue instanceof DBObject)) {
operand = new BasicDBObject();
if (_hasNot) {
DBObject notOperand = new BasicDBObject(QueryOperators.NOT, operand);
_query.put(_currentKey, notOperand);
_hasNot = false;
} else {
_query.put(_currentKey, operand);
}
} else {
operand = (BasicDBObject) _query.get(_currentKey);
if (operand.get(QueryOperators.NOT) != null) {
operand = (BasicDBObject) operand.get(QueryOperators.NOT);
}
}
operand.put(op, valueToPut);
}
@SuppressWarnings("serial")
static class QueryBuilderException extends RuntimeException {
QueryBuilderException(final String message) {
super(message);
}
}
private static class NullObject {
}
private final DBObject _query;
private String _currentKey;
private boolean _hasNot;
}