diff --git a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java index 2874fe2a1..dbce295e8 100644 --- a/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java +++ b/spring-data-mongodb/src/main/java/org/springframework/data/mongodb/core/query/Criteria.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2011 the original author or authors. + * 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. @@ -39,6 +39,10 @@ import com.mongodb.DBObject; /** * Central class for creating queries. It follows a fluent API style so that you can easily chain together multiple * criteria. Static import of the 'Criteria.where' method will improve readability. + * + * @author Thomas Risberg + * @author Oliver Gierke + * @author Thomas Darimont */ public class Criteria implements CriteriaDefinition { @@ -396,34 +400,54 @@ public class Criteria implements CriteriaDefinition { /** * Creates an 'or' criteria using the $or operator for all of the provided criteria + *
+ * Note that mongodb doesn't support an $or operator to be wrapped in a $not operator. + *
* + * @throws IllegalArgumentException if {@link #orOperator(Criteria...)} follows a not() call directly. * @param criteria */ public Criteria orOperator(Criteria... criteria) { BasicDBList bsonList = createCriteriaList(criteria); - criteriaChain.add(new Criteria("$or").is(bsonList)); - return this; + return registerCriteriaChainElement(new Criteria("$or").is(bsonList)); } /** - * Creates a 'nor' criteria using the $nor operator for all of the provided criteria + * Creates a 'nor' criteria using the $nor operator for all of the provided criteria. + *
+ * Note that mongodb doesn't support an $nor operator to be wrapped in a $not operator. + *
* + * @throws IllegalArgumentException if {@link #norOperator(Criteria...)} follows a not() call directly. * @param criteria */ public Criteria norOperator(Criteria... criteria) { BasicDBList bsonList = createCriteriaList(criteria); - criteriaChain.add(new Criteria("$nor").is(bsonList)); - return this; + return registerCriteriaChainElement(new Criteria("$nor").is(bsonList)); } /** - * Creates an 'and' criteria using the $and operator for all of the provided criteria + * Creates an 'and' criteria using the $and operator for all of the provided criteria. + *
+ * Note that mongodb doesn't support an $and operator to be wrapped in a $not operator. + *
* + * @throws IllegalArgumentException if {@link #andOperator(Criteria...)} follows a not() call directly. * @param criteria */ public Criteria andOperator(Criteria... criteria) { BasicDBList bsonList = createCriteriaList(criteria); - criteriaChain.add(new Criteria("$and").is(bsonList)); + return registerCriteriaChainElement(new Criteria("$and").is(bsonList)); + } + + private Criteria registerCriteriaChainElement(Criteria criteria) { + + if (lastOperatorWasNot()) { + throw new IllegalArgumentException("operator $not is not allowed around criteria chain element: " + + criteria.getCriteriaObject()); + } else { + criteriaChain.add(criteria); + } return this; } @@ -468,6 +492,7 @@ public class Criteria implements CriteriaDefinition { } } } + DBObject queryCriteria = new BasicDBObject(); if (isValue != NOT_SET) { queryCriteria.put(this.key, this.isValue); diff --git a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java index fac13a5d6..5676f30a0 100644 --- a/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java +++ b/spring-data-mongodb/src/test/java/org/springframework/data/mongodb/core/query/CriteriaTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2010-2011 the original author or authors. + * 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. @@ -24,6 +24,10 @@ import org.springframework.data.mongodb.InvalidMongoDbApiUsageException; import com.mongodb.BasicDBObject; import com.mongodb.DBObject; +/** + * @author Oliver Gierke + * @author Thomas Darimont + */ public class CriteriaTests { @Test @@ -68,4 +72,50 @@ public class CriteriaTests { assertThat(left, is(not(right))); assertThat(right, is(not(left))); } + + /** + * @see DATAMONGO-507 + */ + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionWhenTryingToNegateAndOperation() { + + new Criteria() // + .not() // + .andOperator(Criteria.where("delete").is(true).and("_id").is(42)); // + } + + /** + * @see DATAMONGO-507 + */ + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionWhenTryingToNegateOrOperation() { + + new Criteria() // + .not() // + .orOperator(Criteria.where("delete").is(true).and("_id").is(42)); // + } + + /** + * @see DATAMONGO-507 + */ + @Test(expected = IllegalArgumentException.class) + public void shouldThrowExceptionWhenTryingToNegateNorOperation() { + + new Criteria() // + .not() // + .norOperator(Criteria.where("delete").is(true).and("_id").is(42)); // + } + + /** + * @see DATAMONGO-507 + */ + @Test + public void shouldNegateFollowingSimpleExpression() { + + Criteria c = Criteria.where("age").not().gt(18).and("status").is("student"); + DBObject co = c.getCriteriaObject(); + + assertThat(co, is(notNullValue())); + assertThat(co.toString(), is("{ \"age\" : { \"$not\" : { \"$gt\" : 18}} , \"status\" : \"student\"}")); + } }