/* * Copyright 2010-2017 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.core.query; import static org.hamcrest.CoreMatchers.*; import static org.junit.Assert.*; import static org.springframework.data.mongodb.core.query.Criteria.*; import static org.springframework.data.mongodb.core.query.Query.*; import java.util.Arrays; import org.bson.Document; import org.junit.Assert; import org.junit.Rule; import org.junit.Test; import org.junit.rules.ExpectedException; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; import org.springframework.data.mongodb.core.SpecialDoc; /** * Unit tests for {@link Query}. * * @author Thomas Risberg * @author Oliver Gierke * @author Patryk Wasik * @author Thomas Darimont * @author Christoph Strobl * @author Mark Paluch */ public class QueryTests { @Rule public ExpectedException exception = ExpectedException.none(); @Test public void testSimpleQuery() { Query q = new Query(where("name").is("Thomas").and("age").lt(80)); Document expected = Document.parse("{ \"name\" : \"Thomas\" , \"age\" : { \"$lt\" : 80}}"); Assert.assertEquals(expected, q.getQueryObject()); } @Test public void testQueryWithNot() { Query q = new Query(where("name").is("Thomas").and("age").not().mod(10, 0)); Document expected = Document.parse("{ \"name\" : \"Thomas\" , \"age\" : { \"$not\" : { \"$mod\" : [ 10 , 0]}}}"); Assert.assertEquals(expected, q.getQueryObject()); } @Test public void testInvalidQueryWithNotIs() { try { new Query(where("name").not().is("Thomas")); Assert.fail("This should have caused an InvalidDocumentStoreApiUsageException"); } catch (InvalidMongoDbApiUsageException e) {} } @Test public void testOrQuery() { Query q = new Query(new Criteria().orOperator(where("name").is("Sven").and("age").lt(50), where("age").lt(50), where("name").is("Thomas"))); Document expected = Document.parse( "{ \"$or\" : [ { \"name\" : \"Sven\" , \"age\" : { \"$lt\" : 50}} , { \"age\" : { \"$lt\" : 50}} , { \"name\" : \"Thomas\"}]}"); Assert.assertEquals(expected, q.getQueryObject()); } @Test public void testAndQuery() { Query q = new Query(new Criteria().andOperator(where("name").is("Sven"), where("age").lt(50))); Document expected = Document.parse("{ \"$and\" : [ { \"name\" : \"Sven\"} , { \"age\" : { \"$lt\" : 50}}]}"); Assert.assertEquals(expected, q.getQueryObject()); } @Test public void testNorQuery() { Query q = new Query( new Criteria().norOperator(where("name").is("Sven"), where("age").lt(50), where("name").is("Thomas"))); Document expected = Document .parse("{ \"$nor\" : [ { \"name\" : \"Sven\"} , { \"age\" : { \"$lt\" : 50}} , { \"name\" : \"Thomas\"}]}"); Assert.assertEquals(expected, q.getQueryObject()); } @Test public void testQueryWithLimit() { Query q = new Query(where("name").gte("M").lte("T").and("age").not().gt(22)); q.limit(50); Document expected = Document .parse("{ \"name\" : { \"$gte\" : \"M\" , \"$lte\" : \"T\"} , \"age\" : { \"$not\" : { \"$gt\" : 22}}}"); Assert.assertEquals(expected, q.getQueryObject()); Assert.assertEquals(50, q.getLimit()); } @Test public void testQueryWithFieldsAndSlice() { Query q = new Query(where("name").gte("M").lte("T").and("age").not().gt(22)); q.fields().exclude("address").include("name").slice("orders", 10); Document expected = Document .parse("{ \"name\" : { \"$gte\" : \"M\" , \"$lte\" : \"T\"} , \"age\" : { \"$not\" : { \"$gt\" : 22}}}"); Assert.assertEquals(expected, q.getQueryObject()); Document expectedFields = Document.parse("{ \"address\" : 0 , \"name\" : 1 , \"orders\" : { \"$slice\" : 10}}"); Assert.assertEquals(expectedFields, q.getFieldsObject()); } @Test // DATAMONGO-652 public void testQueryWithFieldsElemMatchAndPositionalOperator() { Query query = query(where("name").gte("M").lte("T").and("age").not().gt(22)); query.fields().elemMatch("products", where("name").is("milk")).position("comments", 2); Document expected = Document .parse("{ \"name\" : { \"$gte\" : \"M\" , \"$lte\" : \"T\"} , \"age\" : { \"$not\" : { \"$gt\" : 22}}}"); assertThat(query.getQueryObject(), is(expected)); Document expectedFields = Document .parse("{ \"products\" : { \"$elemMatch\" : { \"name\" : \"milk\"}} , \"comments.$\" : 2}"); assertThat(query.getFieldsObject(), is(expectedFields)); } @Test public void testSimpleQueryWithChainedCriteria() { Query q = new Query(where("name").is("Thomas").and("age").lt(80)); Document expected = Document.parse("{ \"name\" : \"Thomas\" , \"age\" : { \"$lt\" : 80}}"); Assert.assertEquals(expected, q.getQueryObject()); } @Test public void testComplexQueryWithMultipleChainedCriteria() { Query q = new Query( where("name").regex("^T.*").and("age").gt(20).lt(80).and("city").in("Stockholm", "London", "New York")); Document expected = Document .parse("{ \"name\" : { \"$regex\" : \"^T.*\"} , \"age\" : { \"$gt\" : 20 , \"$lt\" : 80} , " + "\"city\" : { \"$in\" : [ \"Stockholm\" , \"London\" , \"New York\"]}}"); Assert.assertEquals(expected.toJson(), q.getQueryObject().toJson()); } @Test public void testAddCriteriaWithComplexQueryWithMultipleChainedCriteria() { Query q1 = new Query( where("name").regex("^T.*").and("age").gt(20).lt(80).and("city").in("Stockholm", "London", "New York")); Query q2 = new Query(where("name").regex("^T.*").and("age").gt(20).lt(80)) .addCriteria(where("city").in("Stockholm", "London", "New York")); Assert.assertEquals(q1.getQueryObject().toString(), q2.getQueryObject().toString()); Query q3 = new Query(where("name").regex("^T.*")).addCriteria(where("age").gt(20).lt(80)) .addCriteria(where("city").in("Stockholm", "London", "New York")); Assert.assertEquals(q1.getQueryObject().toString(), q3.getQueryObject().toString()); } @Test public void testQueryWithElemMatch() { Query q = new Query(where("openingHours").elemMatch(where("dayOfWeek").is("Monday").and("open").lte("1800"))); Document expected = Document.parse( "{ \"openingHours\" : { \"$elemMatch\" : { \"dayOfWeek\" : \"Monday\" , \"open\" : { \"$lte\" : \"1800\"}}}}"); Assert.assertEquals(expected, q.getQueryObject()); } @Test public void testQueryWithIn() { Query q = new Query(where("state").in("NY", "NJ", "PA")); Document expected = Document.parse("{ \"state\" : { \"$in\" : [ \"NY\" , \"NJ\" , \"PA\"]}}"); Assert.assertEquals(expected, q.getQueryObject()); } @Test public void testQueryWithRegex() { Query q = new Query(where("name").regex("b.*")); Document expected = Document.parse("{ \"name\" : { \"$regex\" : \"b.*\"}}"); Assert.assertEquals(expected.toJson(), q.getQueryObject().toJson()); } @Test public void testQueryWithRegexAndOption() { Query q = new Query(where("name").regex("b.*", "i")); Document expected = Document.parse("{ \"name\" : { \"$regex\" : \"b.*\" , \"$options\" : \"i\"}}"); Assert.assertEquals(expected.toJson(), q.getQueryObject().toJson()); } @Test // DATAMONGO-538 public void addsSortCorrectly() { Query query = new Query().with(Sort.by(Direction.DESC, "foo")); assertThat(query.getSortObject(), is(Document.parse("{ \"foo\" : -1}"))); } @Test public void rejectsOrderWithIgnoreCase() { exception.expect(IllegalArgumentException.class); exception.expectMessage("foo"); new Query().with(Sort.by(new Sort.Order("foo").ignoreCase())); } @Test // DATAMONGO-709 @SuppressWarnings("unchecked") public void shouldReturnClassHierarchyOfRestrictedTypes() { Query query = new Query(where("name").is("foo")).restrict(SpecialDoc.class); assertThat(query.toString(), is( "Query: { \"name\" : \"foo\", \"_$RESTRICTED_TYPES\" : [ { $java : class org.springframework.data.mongodb.core.SpecialDoc } ] }, Fields: null, Sort: null")); assertThat(query.getRestrictedTypes(), is(notNullValue())); assertThat(query.getRestrictedTypes().size(), is(1)); assertThat(query.getRestrictedTypes(), hasItems(Arrays.asList(SpecialDoc.class).toArray(new Class<?>[0]))); } @Test // DATAMONGO-1421 public void addCriteriaForSamePropertyMultipleTimesShouldThrowAndSafelySerializeErrorMessage() { exception.expect(InvalidMongoDbApiUsageException.class); exception.expectMessage("second 'value' criteria"); exception.expectMessage("already contains '{ \"value\" : { $java : VAL_1 } }'"); Query query = new Query(); query.addCriteria(where("value").is(EnumType.VAL_1)); query.addCriteria(where("value").is(EnumType.VAL_2)); } enum EnumType { VAL_1, VAL_2 } }