From 73b7dff985f048282b6e3d1f568f5df746622bad Mon Sep 17 00:00:00 2001 From: Benedikt Waldvogel Date: Thu, 1 Jun 2023 10:38:09 +0200 Subject: [PATCH] Fix $nin query with array of documents #215 --- .../mongo/backend/DefaultQueryMatcher.java | 18 +++---- .../backend/DefaultQueryMatcherTest.java | 40 ---------------- .../mongo/backend/AbstractBackendTest.java | 47 +++++++++++++++++++ 3 files changed, 57 insertions(+), 48 deletions(-) diff --git a/core/src/main/java/de/bwaldvogel/mongo/backend/DefaultQueryMatcher.java b/core/src/main/java/de/bwaldvogel/mongo/backend/DefaultQueryMatcher.java index 35eb680f..e61e43b5 100644 --- a/core/src/main/java/de/bwaldvogel/mongo/backend/DefaultQueryMatcher.java +++ b/core/src/main/java/de/bwaldvogel/mongo/backend/DefaultQueryMatcher.java @@ -134,15 +134,17 @@ private boolean checkMatch(Object queryValue, List keys, Object value) { return checkMatchesValue(queryValue, value); } - // handle $all - if (queryValue instanceof Document && ((Document) queryValue).containsKey(QueryOperator.ALL.getValue())) { - // clone first - queryValue = ((Document) queryValue).clone(); - Object allQuery = ((Document) queryValue).remove(QueryOperator.ALL.getValue()); - if (!checkMatchesAllDocuments(allQuery, keys, value)) { - return false; + if (queryValue instanceof Document) { + Document query = (Document) queryValue; + if (query.containsKey(QueryOperator.ALL.getValue())) { + Object allQuery = query.get(QueryOperator.ALL.getValue()); + return checkMatchesAllDocuments(allQuery, keys, value); + } + if (query.containsKey(QueryOperator.NOT_IN.getValue())) { + Object notInQueryValue = query.get(QueryOperator.NOT_IN.getValue()); + Document inQuery = new Document(QueryOperator.IN.getValue(), notInQueryValue); + return !checkMatchesAnyDocument(inQuery, keys, value); } - // continue matching the remainder of queryValue } return checkMatchesAnyDocument(queryValue, keys, value); diff --git a/core/src/test/java/de/bwaldvogel/mongo/backend/DefaultQueryMatcherTest.java b/core/src/test/java/de/bwaldvogel/mongo/backend/DefaultQueryMatcherTest.java index 5e714fa6..5f624eff 100644 --- a/core/src/test/java/de/bwaldvogel/mongo/backend/DefaultQueryMatcherTest.java +++ b/core/src/test/java/de/bwaldvogel/mongo/backend/DefaultQueryMatcherTest.java @@ -15,7 +15,6 @@ import static de.bwaldvogel.mongo.backend.DocumentBuilder.mod; import static de.bwaldvogel.mongo.backend.DocumentBuilder.nor; import static de.bwaldvogel.mongo.backend.DocumentBuilder.not; -import static de.bwaldvogel.mongo.backend.DocumentBuilder.notIn; import static de.bwaldvogel.mongo.backend.DocumentBuilder.or; import static de.bwaldvogel.mongo.backend.DocumentBuilder.regex; import static de.bwaldvogel.mongo.backend.DocumentBuilder.size; @@ -505,45 +504,6 @@ void testMatchesNot() throws Exception { assertThat(matcher.matches(document, query)).isTrue(); } - /** - * Test case for https://github.com/bwaldvogel/mongo-java-server/issues/7 - */ - @Test - void testMatchesNotIn() throws Exception { - Document query1 = map("map.key2", notIn("value 2.2")); - Document query2 = map("map.key2", not(in("value 2.2"))); - Document query3 = map("map.key2", not(notIn("value 2.2"))); - Document query4 = map("map.key2", not(not(in("value 2.2")))); - - Document document1 = json("code: 'c1', map: {key1: 'value 1.1', key2: ['value 2.1']}"); - Document document2 = json("code: 'c1', map: {key1: 'value 1.2', key2: ['value 2.2']}"); - Document document3 = json("code: 'c1', map: {key1: 'value 2.1', key2: ['value 2.1']}"); - - assertThat(matcher.matches(document1, query1)).isTrue(); - assertThat(matcher.matches(document2, query1)).isFalse(); - assertThat(matcher.matches(document3, query1)).isTrue(); - - assertThat(matcher.matches(document1, query2)).isTrue(); - assertThat(matcher.matches(document2, query2)).isFalse(); - assertThat(matcher.matches(document3, query2)).isTrue(); - - assertThat(matcher.matches(document1, query3)).isFalse(); - assertThat(matcher.matches(document2, query3)).isTrue(); - assertThat(matcher.matches(document3, query3)).isFalse(); - - assertThat(matcher.matches(document1, query4)).isFalse(); - assertThat(matcher.matches(document2, query4)).isTrue(); - assertThat(matcher.matches(document3, query4)).isFalse(); - - assertThat(matcher.matches(json("values: [1, 2, 3]"), json("values: {$nin: []}"))).isTrue(); - assertThat(matcher.matches(json("values: null"), json("values: {$nin: []}"))).isTrue(); - assertThat(matcher.matches(json(""), json("values: {$nin: []}"))).isTrue(); - assertThat(matcher.matches(json(""), json("values: {$nin: [1]}"))).isTrue(); - assertThat(matcher.matches(json(""), json("values: {$nin: [1, 2]}"))).isTrue(); - assertThat(matcher.matches(json("values: null"), json("values: {$nin: [null]}"))).isFalse(); - assertThat(matcher.matches(json(""), json("values: {$nin: [null]}"))).isFalse(); - } - @Test void testMatchesNotSize() throws Exception { assertThat(matcher.matches(json("a: [1, 2, 3]"), json("a: {$not: {$size: 3}}"))).isFalse(); diff --git a/test-common/src/main/java/de/bwaldvogel/mongo/backend/AbstractBackendTest.java b/test-common/src/main/java/de/bwaldvogel/mongo/backend/AbstractBackendTest.java index 2917610b..47034b79 100755 --- a/test-common/src/main/java/de/bwaldvogel/mongo/backend/AbstractBackendTest.java +++ b/test-common/src/main/java/de/bwaldvogel/mongo/backend/AbstractBackendTest.java @@ -2296,6 +2296,53 @@ public void testAndQueryWithAllAndNin() throws Exception { .containsExactly(json("_id: 2, tags: ['A', 'D']")); } + // https://github.com/bwaldvogel/mongo-java-server/issues/215 + @Test + void testMatchesNinFieldInArray() throws Exception { + collection.insertOne(json("_id: 1, tags: [{'value': 'A'}, {'value': 'D'}]")); + collection.insertOne(json("_id: 2, tags: [{'value': 'A'}, {'value': 'B'}]")); + collection.insertOne(json("_id: 3, tags: [{'value': 'A'}, {'value': 'C'}]")); + + assertThat(collection.find(json("'tags.value': {$nin: ['B', 'C']}"))) + .containsExactly(json("_id: 1, tags: [{'value': 'A'}, {'value': 'D'}]")); + } + + // https://github.com/bwaldvogel/mongo-java-server/issues/7 + @Test + void testMatchesNotIn() throws Exception { + Document document1 = json("_id: 1, code: 'c1', map: {key1: 'value 1.1', key2: ['value 2.1']}"); + Document document2 = json("_id: 2, code: 'c1', map: {key1: 'value 1.2', key2: ['value 2.2']}"); + Document document3 = json("_id: 3, code: 'c1', map: {key1: 'value 2.1', key2: ['value 2.1']}"); + Document document4 = json("_id: 4, values: [1, 2, 3]"); + Document document5 = json("_id: 5, values: null"); + + collection.insertMany(List.of(document1, document2, document3, document4, document5)); + + assertThat(collection.find(json("'map.key2': {$nin: ['value 2.2']}"))) + .containsExactly(document1, document3, document4, document5); + + assertThat(collection.find(json("'map.key2': {$not: {$in: ['value 2.2']}}"))) + .containsExactly(document1, document3, document4, document5); + + assertThat(collection.find(json("'map.key2': {$not: {$nin: ['value 2.2']}}"))) + .containsExactly(document2); + + assertThat(collection.find(json("'map.key2': {$not: {$not: {$in: ['value 2.2']}}}"))) + .containsExactly(document2); + + assertThat(collection.find(json("values: {$nin: []}"))) + .containsExactly(document1, document2, document3, document4, document5); + + assertThat(collection.find(json("values: {$nin: [1]}"))) + .containsExactly(document1, document2, document3, document5); + + assertThat(collection.find(json("values: {$nin: [1, 2]}"))) + .containsExactly(document1, document2, document3, document5); + + assertThat(collection.find(json("values: {$nin: [null]}"))) + .containsExactly(document4); + } + // https://github.com/bwaldvogel/mongo-java-server/issues/96 @Test public void testAndQueryWithAllAndSize() throws Exception {