From e49fed4c9094c40785de01e5ac15737ce94f3597 Mon Sep 17 00:00:00 2001 From: Tim Swast Date: Mon, 16 May 2016 20:09:02 -0700 Subject: [PATCH 1/2] Fix "surprising queries" samples. We were setting the filter twice (overwriting the first) rather than using a composite filter. This was causing the query results to be even more surprising than we intending. --- .../com/example/appengine/QueriesTest.java | 43 +++++++++---------- 1 file changed, 20 insertions(+), 23 deletions(-) diff --git a/appengine/datastore/src/test/java/com/example/appengine/QueriesTest.java b/appengine/datastore/src/test/java/com/example/appengine/QueriesTest.java index 82283b97106..aa8be04f13c 100644 --- a/appengine/datastore/src/test/java/com/example/appengine/QueriesTest.java +++ b/appengine/datastore/src/test/java/com/example/appengine/QueriesTest.java @@ -694,15 +694,15 @@ public void queryRestrictions_surprisingMultipleValuesAllMustMatch_returnsNoEnti // [START surprising_behavior_example_1] Query q = new Query("Widget") - .setFilter(new FilterPredicate("x", FilterOperator.GREATER_THAN, 1)) - .setFilter(new FilterPredicate("x", FilterOperator.LESS_THAN, 2)); + .setFilter( + CompositeFilterOperator.and( + new FilterPredicate("x", FilterOperator.GREATER_THAN, 1), + new FilterPredicate("x", FilterOperator.LESS_THAN, 2))); // [END surprising_behavior_example_1] - // Note: The documentation describes that the entity "a" will not match - // because no value matches all filters. When run with the local test - // runner, the entity "a" *is* matched. This may be a difference in - // behavior between the local devserver and Cloud Datastore, so there - // aren't any assertions we can make in this test. + // Entity "a" will not match because no individual value matches all filters. + List results = datastore.prepare(q).asList(FetchOptions.Builder.withDefaults()); + assertThat(results).named("query results").isEmpty(); } @Test @@ -716,21 +716,21 @@ public void queryRestrictions_surprisingMultipleValuesEquals_returnsMatchedEntit c.setProperty("x", ImmutableList.of(-6L, 2L)); Entity d = new Entity("Widget", "d"); d.setProperty("x", ImmutableList.of(-6L, 4L)); - datastore.put(ImmutableList.of(a, b, c, d)); + Entity e = new Entity("Widget", "e"); + e.setProperty("x", ImmutableList.of(1L, 2L, 3L)); + datastore.put(ImmutableList.of(a, b, c, d, e)); // [START surprising_behavior_example_2] Query q = new Query("Widget") - .setFilter(new FilterPredicate("x", FilterOperator.EQUAL, 1)) - .setFilter(new FilterPredicate("x", FilterOperator.EQUAL, 2)); + .setFilter( + CompositeFilterOperator.and( + new FilterPredicate("x", FilterOperator.EQUAL, 1), + new FilterPredicate("x", FilterOperator.EQUAL, 2))); // [END surprising_behavior_example_2] List results = datastore.prepare(q).asList(FetchOptions.Builder.withDefaults()); - assertThat(getKeys(results)).named("query result keys").contains(a.getKey()); - - // Note: When run in the test server, this matches "c" as expected and does - // not match "d" as expected. For some reason it does *not* match "b". - // The behavior of queries on repeated values is definitely surprising. + assertThat(getKeys(results)).named("query result keys").containsExactly(a.getKey(), e.getKey()); } @Test @@ -770,17 +770,14 @@ public void queryRestrictions_surprisingMultipleValuesTwoNotEquals_returnsMatche // [START surprising_behavior_example_4] Query q = new Query("Widget") - .setFilter(new FilterPredicate("x", FilterOperator.NOT_EQUAL, 1)) - .setFilter(new FilterPredicate("x", FilterOperator.NOT_EQUAL, 2)); + .setFilter( + CompositeFilterOperator.and( + new FilterPredicate("x", FilterOperator.NOT_EQUAL, 1), + new FilterPredicate("x", FilterOperator.NOT_EQUAL, 2))); // [END surprising_behavior_example_4] List results = datastore.prepare(q).asList(FetchOptions.Builder.withDefaults()); - assertThat(getKeys(results)).named("query result keys").contains(b.getKey()); - - // Note: The documentation describes that the entity "a" will not match. - // When run with the local test runner, the entity "a" *is* matched. This - // may be a difference in behavior between the local devserver and Cloud - // Datastore. + assertThat(getKeys(results)).named("query result keys").containsExactly(b.getKey()); } private Entity retrievePersonWithLastName(String targetLastName) { From 0f11a9f7c2636d68cc9dd3f885a923289b47199b Mon Sep 17 00:00:00 2001 From: Tim Swast Date: Tue, 17 May 2016 14:33:11 -0700 Subject: [PATCH 2/2] Add comments pointing to documentation for surprising query results. --- .../com/example/appengine/QueriesTest.java | 20 +++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/appengine/datastore/src/test/java/com/example/appengine/QueriesTest.java b/appengine/datastore/src/test/java/com/example/appengine/QueriesTest.java index aa8be04f13c..3976103d693 100644 --- a/appengine/datastore/src/test/java/com/example/appengine/QueriesTest.java +++ b/appengine/datastore/src/test/java/com/example/appengine/QueriesTest.java @@ -44,7 +44,6 @@ import java.io.PrintWriter; import java.io.StringWriter; -import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -685,9 +684,7 @@ public void queryRestrictions_sortWrongOrderOnInequality_isInvalid() throws Exce public void queryRestrictions_surprisingMultipleValuesAllMustMatch_returnsNoEntities() throws Exception { Entity a = new Entity("Widget", "a"); - ArrayList xs = new ArrayList<>(); - xs.add(1L); - xs.add(2L); + List xs = Arrays.asList(1L, 2L); a.setProperty("x", xs); datastore.put(a); @@ -701,6 +698,8 @@ public void queryRestrictions_surprisingMultipleValuesAllMustMatch_returnsNoEnti // [END surprising_behavior_example_1] // Entity "a" will not match because no individual value matches all filters. + // See the documentation for more details: + // https://cloud.google.com/appengine/docs/java/datastore/query-restrictions#properties_with_multiple_values_can_behave_in_surprising_ways List results = datastore.prepare(q).asList(FetchOptions.Builder.withDefaults()); assertThat(results).named("query results").isEmpty(); } @@ -729,6 +728,9 @@ public void queryRestrictions_surprisingMultipleValuesEquals_returnsMatchedEntit new FilterPredicate("x", FilterOperator.EQUAL, 2))); // [END surprising_behavior_example_2] + // Only "a" and "e" have both 1 and 2 in the "x" array-valued property. + // See the documentation for more details: + // https://cloud.google.com/appengine/docs/java/datastore/query-restrictions#properties_with_multiple_values_can_behave_in_surprising_ways List results = datastore.prepare(q).asList(FetchOptions.Builder.withDefaults()); assertThat(getKeys(results)).named("query result keys").containsExactly(a.getKey(), e.getKey()); } @@ -752,6 +754,9 @@ public void queryRestrictions_surprisingMultipleValuesNotEquals_returnsMatchedEn Query q = new Query("Widget").setFilter(new FilterPredicate("x", FilterOperator.NOT_EQUAL, 1)); // [END surprising_behavior_example_3] + // The query matches any entity that has a some value other than 1. Only + // entity "e" is not matched. See the documentation for more details: + // https://cloud.google.com/appengine/docs/java/datastore/query-restrictions#properties_with_multiple_values_can_behave_in_surprising_ways List results = datastore.prepare(q).asList(FetchOptions.Builder.withDefaults()); assertThat(getKeys(results)) .named("query result keys") @@ -776,6 +781,13 @@ public void queryRestrictions_surprisingMultipleValuesTwoNotEquals_returnsMatche new FilterPredicate("x", FilterOperator.NOT_EQUAL, 2))); // [END surprising_behavior_example_4] + // The two NOT_EQUAL filters in the query become like the combination of queries: + // x < 1 OR (x > 1 AND x < 2) OR x > 2 + // + // Only "b" has some value which matches the "x > 2" portion of this query. + // + // See the documentation for more details: + // https://cloud.google.com/appengine/docs/java/datastore/query-restrictions#properties_with_multiple_values_can_behave_in_surprising_ways List results = datastore.prepare(q).asList(FetchOptions.Builder.withDefaults()); assertThat(getKeys(results)).named("query result keys").containsExactly(b.getKey()); }