summaryrefslogtreecommitdiff
path: root/core/src/test/java/org/elasticsearch/common/xcontent/support/AbstractFilteringTestCase.java
diff options
context:
space:
mode:
authorTanguy Leroux <tlrx.dev@gmail.com>2017-07-03 14:45:26 +0200
committerGitHub <noreply@github.com>2017-07-03 14:45:26 +0200
commit0e2cfc66bb8ad091d3cf5ade2833f9688af1240c (patch)
treeda730afea4005cfe9bf43d666b1fef89e134cb13 /core/src/test/java/org/elasticsearch/common/xcontent/support/AbstractFilteringTestCase.java
parenta9ea742a85f225cdc93cd97a21303445ab189f1f (diff)
[Test] Use a common testing class for all XContent filtering tests (#25491)
We have two ways to filter XContent: - The first method is to parse the XContent as a map and use XContentMapValues.filter(). This method filters the content of the map using an automaton. It is used for source filtering, both at search and indexing time. It performs well but can generate a lot of objects and garbage collections when large XContent are filtered. It also returns empty objects (see f2710c16ebd918f646be9d0ab64b4871c25be4c2) when all the sub fields have been filtered out and handle dots in field names as if they were sub fields. - The second method is to parse the XContent and copy the XContentParser structure to a XContentBuilder initialized with includes/excludes filters. This method uses the Jackson streaming filter feature. It is used by the Response Filtering ('filter_path') feature. It does not generate a lot of objects, and does not return empty objects and also does not handle dots in field names explicitely. Both methods have similar goals but different tests. This commit changes the current XContentBuilder test class so that it becomes a more generic testing class and we can now ensure that filtering methods generate the same results. It also removes some tests from the XContentMapValuesTests class that should be in XContentParserTests.
Diffstat (limited to 'core/src/test/java/org/elasticsearch/common/xcontent/support/AbstractFilteringTestCase.java')
-rw-r--r--core/src/test/java/org/elasticsearch/common/xcontent/support/AbstractFilteringTestCase.java1430
1 files changed, 1430 insertions, 0 deletions
diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/support/AbstractFilteringTestCase.java b/core/src/test/java/org/elasticsearch/common/xcontent/support/AbstractFilteringTestCase.java
new file mode 100644
index 0000000000..9d95ea6013
--- /dev/null
+++ b/core/src/test/java/org/elasticsearch/common/xcontent/support/AbstractFilteringTestCase.java
@@ -0,0 +1,1430 @@
+/*
+ * Licensed to Elasticsearch under one or more contributor
+ * license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright
+ * ownership. Elasticsearch licenses this file to you 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.elasticsearch.common.xcontent.support;
+
+import org.elasticsearch.common.CheckedFunction;
+import org.elasticsearch.common.bytes.BytesReference;
+import org.elasticsearch.common.util.set.Sets;
+import org.elasticsearch.common.xcontent.XContent;
+import org.elasticsearch.common.xcontent.XContentBuilder;
+import org.elasticsearch.test.ESTestCase;
+
+import java.io.IOException;
+import java.util.Set;
+
+import static java.util.Collections.emptySet;
+import static java.util.Collections.singleton;
+
+/**
+ * Tests for {@link XContent} filtering.
+ */
+public abstract class AbstractFilteringTestCase extends ESTestCase {
+
+ @FunctionalInterface
+ protected interface Builder extends CheckedFunction<XContentBuilder, XContentBuilder, IOException> {
+ }
+
+ protected abstract void testFilter(Builder expected, Builder actual, Set<String> includes, Set<String> excludes) throws IOException;
+
+ /** Sample test case **/
+ private static final Builder SAMPLE = builder -> builder.startObject()
+ .field("title", "My awesome book")
+ .field("pages", 456)
+ .field("price", 27.99)
+ .field("timestamp", 1428582942867L)
+ .nullField("default")
+ .startArray("tags")
+ .value("elasticsearch")
+ .value("java")
+ .endArray()
+ .startArray("authors")
+ .startObject()
+ .field("name", "John Doe")
+ .field("lastname", "John")
+ .field("firstname", "Doe")
+ .endObject()
+ .startObject()
+ .field("name", "William Smith")
+ .field("lastname", "William")
+ .field("firstname", "Smith")
+ .endObject()
+ .endArray()
+ .startObject("properties")
+ .field("weight", 0.8d)
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "The Book Shop")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Hampton St")
+ .field("city", "London")
+ .endObject()
+ .startObject()
+ .field("name", "address #2")
+ .field("street", "Queen St")
+ .field("city", "Stornoway")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Sussex Books House")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .field("lang", "French")
+ .field("available", false)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "La Maison du Livre")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Rue Mouffetard")
+ .field("city", "Paris")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Thetra")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ public void testNoFiltering() throws Exception {
+ final Builder expected = SAMPLE;
+
+ testFilter(expected, SAMPLE, emptySet(), emptySet());
+ testFilter(expected, SAMPLE, singleton("*"), emptySet());
+ testFilter(expected, SAMPLE, singleton("**"), emptySet());
+ testFilter(expected, SAMPLE, emptySet(), singleton("xyz"));
+ }
+
+ public void testNoMatch() throws Exception {
+ final Builder expected = builder -> builder.startObject().endObject();
+
+ testFilter(expected, SAMPLE, singleton("xyz"), emptySet());
+ testFilter(expected, SAMPLE, emptySet(), singleton("*"));
+ testFilter(expected, SAMPLE, emptySet(), singleton("**"));
+ }
+
+ public void testSimpleFieldInclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject().field("title", "My awesome book").endObject();
+
+ testFilter(expected, SAMPLE, singleton("title"), emptySet());
+ }
+
+ public void testSimpleFieldExclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .field("pages", 456)
+ .field("price", 27.99)
+ .field("timestamp", 1428582942867L)
+ .nullField("default")
+ .startArray("tags")
+ .value("elasticsearch")
+ .value("java")
+ .endArray()
+ .startArray("authors")
+ .startObject()
+ .field("name", "John Doe")
+ .field("lastname", "John")
+ .field("firstname", "Doe")
+ .endObject()
+ .startObject()
+ .field("name", "William Smith")
+ .field("lastname", "William")
+ .field("firstname", "Smith")
+ .endObject()
+ .endArray()
+ .startObject("properties")
+ .field("weight", 0.8d)
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "The Book Shop")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Hampton St")
+ .field("city", "London")
+ .endObject()
+ .startObject()
+ .field("name", "address #2")
+ .field("street", "Queen St")
+ .field("city", "Stornoway")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Sussex Books House")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .field("lang", "French")
+ .field("available", false)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "La Maison du Livre")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Rue Mouffetard")
+ .field("city", "Paris")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Thetra")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, emptySet(), singleton("title"));
+ }
+
+ public void testSimpleFieldWithWildcardInclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .field("price", 27.99)
+ .startObject("properties")
+ .field("weight", 0.8d)
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "The Book Shop")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Hampton St")
+ .field("city", "London")
+ .endObject()
+ .startObject()
+ .field("name", "address #2")
+ .field("street", "Queen St")
+ .field("city", "Stornoway")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Sussex Books House")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .field("lang", "French")
+ .field("available", false)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "La Maison du Livre")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Rue Mouffetard")
+ .field("city", "Paris")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Thetra")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, singleton("pr*"), emptySet());
+ }
+
+ public void testSimpleFieldWithWildcardExclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .field("title", "My awesome book")
+ .field("pages", 456)
+ .field("timestamp", 1428582942867L)
+ .nullField("default")
+ .startArray("tags")
+ .value("elasticsearch")
+ .value("java")
+ .endArray()
+ .startArray("authors")
+ .startObject()
+ .field("name", "John Doe")
+ .field("lastname", "John")
+ .field("firstname", "Doe")
+ .endObject()
+ .startObject()
+ .field("name", "William Smith")
+ .field("lastname", "William")
+ .field("firstname", "Smith")
+ .endObject()
+ .endArray()
+ .endObject();
+
+ testFilter(expected, SAMPLE, emptySet(), singleton("pr*"));
+ }
+
+ public void testMultipleFieldsInclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .field("title", "My awesome book")
+ .field("pages", 456)
+ .endObject();
+
+ testFilter(expected, SAMPLE, Sets.newHashSet("title", "pages"), emptySet());
+ }
+
+ public void testMultipleFieldsExclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .field("price", 27.99)
+ .field("timestamp", 1428582942867L)
+ .nullField("default")
+ .startArray("tags")
+ .value("elasticsearch")
+ .value("java")
+ .endArray()
+ .startArray("authors")
+ .startObject()
+ .field("name", "John Doe")
+ .field("lastname", "John")
+ .field("firstname", "Doe")
+ .endObject()
+ .startObject()
+ .field("name", "William Smith")
+ .field("lastname", "William")
+ .field("firstname", "Smith")
+ .endObject()
+ .endArray()
+ .startObject("properties")
+ .field("weight", 0.8d)
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "The Book Shop")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Hampton St")
+ .field("city", "London")
+ .endObject()
+ .startObject()
+ .field("name", "address #2")
+ .field("street", "Queen St")
+ .field("city", "Stornoway")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Sussex Books House")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .field("lang", "French")
+ .field("available", false)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "La Maison du Livre")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Rue Mouffetard")
+ .field("city", "Paris")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Thetra")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, emptySet(), Sets.newHashSet("title", "pages"));
+ }
+
+ public void testSimpleArrayInclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .startArray("tags")
+ .value("elasticsearch")
+ .value("java")
+ .endArray()
+ .endObject();
+
+ testFilter(expected, SAMPLE, singleton("tags"), emptySet());
+ }
+
+ public void testSimpleArrayExclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .field("title", "My awesome book")
+ .field("pages", 456)
+ .field("price", 27.99)
+ .field("timestamp", 1428582942867L)
+ .nullField("default")
+ .startArray("authors")
+ .startObject()
+ .field("name", "John Doe")
+ .field("lastname", "John")
+ .field("firstname", "Doe")
+ .endObject()
+ .startObject()
+ .field("name", "William Smith")
+ .field("lastname", "William")
+ .field("firstname", "Smith")
+ .endObject()
+ .endArray()
+ .startObject("properties")
+ .field("weight", 0.8d)
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "The Book Shop")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Hampton St")
+ .field("city", "London")
+ .endObject()
+ .startObject()
+ .field("name", "address #2")
+ .field("street", "Queen St")
+ .field("city", "Stornoway")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Sussex Books House")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .field("lang", "French")
+ .field("available", false)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "La Maison du Livre")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Rue Mouffetard")
+ .field("city", "Paris")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Thetra")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, emptySet(), singleton("tags"));
+ }
+
+ public void testSimpleArrayOfObjectsInclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .startArray("authors")
+ .startObject()
+ .field("name", "John Doe")
+ .field("lastname", "John")
+ .field("firstname", "Doe")
+ .endObject()
+ .startObject()
+ .field("name", "William Smith")
+ .field("lastname", "William")
+ .field("firstname", "Smith")
+ .endObject()
+ .endArray()
+ .endObject();
+
+ testFilter(expected, SAMPLE, singleton("authors"), emptySet());
+ testFilter(expected, SAMPLE, singleton("authors.*"), emptySet());
+ testFilter(expected, SAMPLE, singleton("authors.*name"), emptySet());
+ }
+
+ public void testSimpleArrayOfObjectsExclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .field("title", "My awesome book")
+ .field("pages", 456)
+ .field("price", 27.99)
+ .field("timestamp", 1428582942867L)
+ .nullField("default")
+ .startArray("tags")
+ .value("elasticsearch")
+ .value("java")
+ .endArray()
+ .startObject("properties")
+ .field("weight", 0.8d)
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "The Book Shop")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Hampton St")
+ .field("city", "London")
+ .endObject()
+ .startObject()
+ .field("name", "address #2")
+ .field("street", "Queen St")
+ .field("city", "Stornoway")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Sussex Books House")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .field("lang", "French")
+ .field("available", false)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "La Maison du Livre")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Rue Mouffetard")
+ .field("city", "Paris")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Thetra")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, emptySet(), singleton("authors"));
+ testFilter(expected, SAMPLE, emptySet(), singleton("authors.*"));
+ testFilter(expected, SAMPLE, emptySet(), singleton("authors.*name"));
+ }
+
+ public void testSimpleArrayOfObjectsPropertyInclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .startArray("authors")
+ .startObject()
+ .field("lastname", "John")
+ .endObject()
+ .startObject()
+ .field("lastname", "William")
+ .endObject()
+ .endArray()
+ .endObject();
+
+ testFilter(expected, SAMPLE, singleton("authors.lastname"), emptySet());
+ testFilter(expected, SAMPLE, singleton("authors.l*"), emptySet());
+ }
+
+ public void testSimpleArrayOfObjectsPropertyExclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .field("title", "My awesome book")
+ .field("pages", 456)
+ .field("price", 27.99)
+ .field("timestamp", 1428582942867L)
+ .nullField("default")
+ .startArray("tags")
+ .value("elasticsearch")
+ .value("java")
+ .endArray()
+ .startArray("authors")
+ .startObject()
+ .field("name", "John Doe")
+ .field("firstname", "Doe")
+ .endObject()
+ .startObject()
+ .field("name", "William Smith")
+ .field("firstname", "Smith")
+ .endObject()
+ .endArray()
+ .startObject("properties")
+ .field("weight", 0.8d)
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "The Book Shop")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Hampton St")
+ .field("city", "London")
+ .endObject()
+ .startObject()
+ .field("name", "address #2")
+ .field("street", "Queen St")
+ .field("city", "Stornoway")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Sussex Books House")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .field("lang", "French")
+ .field("available", false)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "La Maison du Livre")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Rue Mouffetard")
+ .field("city", "Paris")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Thetra")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, emptySet(), singleton("authors.lastname"));
+ testFilter(expected, SAMPLE, emptySet(), singleton("authors.l*"));
+ }
+
+ public void testRecurseField1Inclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .startArray("authors")
+ .startObject()
+ .field("name", "John Doe")
+ .endObject()
+ .startObject()
+ .field("name", "William Smith")
+ . endObject()
+ .endArray()
+ .startObject("properties")
+ .startObject("language")
+ .startObject("en")
+ .startArray("distributors")
+ .startObject()
+ .field("name", "The Book Shop")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .endObject()
+ .startObject()
+ .field("name", "address #2")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Sussex Books House")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .startArray("distributors")
+ .startObject()
+ .field("name", "La Maison du Livre")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Thetra")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, singleton("**.name"), emptySet());
+ }
+
+ public void testRecurseField1Exclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .field("title", "My awesome book")
+ .field("pages", 456)
+ .field("price", 27.99)
+ .field("timestamp", 1428582942867L)
+ .nullField("default")
+ .startArray("tags")
+ .value("elasticsearch")
+ .value("java")
+ .endArray()
+ .startArray("authors")
+ .startObject()
+ .field("lastname", "John")
+ .field("firstname", "Doe")
+ .endObject()
+ .startObject()
+ .field("lastname", "William")
+ .field("firstname", "Smith")
+ .endObject()
+ .endArray()
+ .startObject("properties")
+ .field("weight", 0.8d)
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .startArray("distributors")
+ .startObject()
+ .startArray("addresses")
+ .startObject()
+ .field("street", "Hampton St")
+ .field("city", "London")
+ .endObject()
+ .startObject()
+ .field("street", "Queen St")
+ .field("city", "Stornoway")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .field("lang", "French")
+ .field("available", false)
+ .startArray("distributors")
+ .startObject()
+ .startArray("addresses")
+ .startObject()
+ .field("street", "Rue Mouffetard")
+ .field("city", "Paris")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, emptySet(), singleton("**.name"));
+ }
+
+ public void testRecurseField2Inclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .startObject("properties")
+ .startObject("language")
+ .startObject("en")
+ .startArray("distributors")
+ .startObject()
+ .field("name", "The Book Shop")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .endObject()
+ .startObject()
+ .field("name", "address #2")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Sussex Books House")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .startArray("distributors")
+ .startObject()
+ .field("name", "La Maison du Livre")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Thetra")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, singleton("properties.**.name"), emptySet());
+ }
+
+ public void testRecurseField2Exclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .field("title", "My awesome book")
+ .field("pages", 456)
+ .field("price", 27.99)
+ .field("timestamp", 1428582942867L)
+ .nullField("default")
+ .startArray("tags")
+ .value("elasticsearch")
+ .value("java")
+ .endArray()
+ .startArray("authors")
+ .startObject()
+ .field("name", "John Doe")
+ .field("lastname", "John")
+ .field("firstname", "Doe")
+ .endObject()
+ .startObject()
+ .field("name", "William Smith")
+ .field("lastname", "William")
+ .field("firstname", "Smith")
+ .endObject()
+ .endArray()
+ .startObject("properties")
+ .field("weight", 0.8d)
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .startArray("distributors")
+ .startObject()
+ .startArray("addresses")
+ .startObject()
+ .field("street", "Hampton St")
+ .field("city", "London")
+ .endObject()
+ .startObject()
+ .field("street", "Queen St")
+ .field("city", "Stornoway")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .field("lang", "French")
+ .field("available", false)
+ .startArray("distributors")
+ .startObject()
+ .startArray("addresses")
+ .startObject()
+ .field("street", "Rue Mouffetard")
+ .field("city", "Paris")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, emptySet(), singleton("properties.**.name"));
+ }
+
+ public void testRecurseField3Inclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .startObject("properties")
+ .startObject("language")
+ .startObject("en")
+ .startArray("distributors")
+ .startObject()
+ .field("name", "The Book Shop")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .endObject()
+ .startObject()
+ .field("name", "address #2")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Sussex Books House")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, singleton("properties.*.en.**.name"), emptySet());
+ }
+
+ public void testRecurseField3Exclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .field("title", "My awesome book")
+ .field("pages", 456)
+ .field("price", 27.99)
+ .field("timestamp", 1428582942867L)
+ .nullField("default")
+ .startArray("tags")
+ .value("elasticsearch")
+ .value("java")
+ .endArray()
+ .startArray("authors")
+ .startObject()
+ .field("name", "John Doe")
+ .field("lastname", "John")
+ .field("firstname", "Doe")
+ .endObject()
+ .startObject()
+ .field("name", "William Smith")
+ .field("lastname", "William")
+ .field("firstname", "Smith")
+ .endObject()
+ .endArray()
+ .startObject("properties")
+ .field("weight", 0.8d)
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .startArray("distributors")
+ .startObject()
+ .startArray("addresses")
+ .startObject()
+ .field("street", "Hampton St")
+ .field("city", "London")
+ .endObject()
+ .startObject()
+ .field("street", "Queen St")
+ .field("city", "Stornoway")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .field("lang", "French")
+ .field("available", false)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "La Maison du Livre")
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Rue Mouffetard")
+ .field("city", "Paris")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Thetra")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, emptySet(), singleton("properties.*.en.**.name"));
+ }
+
+ public void testRecurseField4Inclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .startObject("properties")
+ .startObject("language")
+ .startObject("en")
+ .startArray("distributors")
+ .startObject()
+ .field("name", "The Book Shop")
+ .endObject()
+ .startObject()
+ .field("name", "Sussex Books House")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .startArray("distributors")
+ .startObject()
+ .field("name", "La Maison du Livre")
+ .endObject()
+ .startObject()
+ .field("name", "Thetra")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, singleton("properties.**.distributors.name"), emptySet());
+ }
+
+ public void testRecurseField4Exclusive() throws Exception {
+ final Builder expected = builder -> builder.startObject()
+ .field("title", "My awesome book")
+ .field("pages", 456)
+ .field("price", 27.99)
+ .field("timestamp", 1428582942867L)
+ .nullField("default")
+ .startArray("tags")
+ .value("elasticsearch")
+ .value("java")
+ .endArray()
+ .startArray("authors")
+ .startObject()
+ .field("name", "John Doe")
+ .field("lastname", "John")
+ .field("firstname", "Doe")
+ .endObject()
+ .startObject()
+ .field("name", "William Smith")
+ .field("lastname", "William")
+ .field("firstname", "Smith")
+ .endObject()
+ .endArray()
+ .startObject("properties")
+ .field("weight", 0.8d)
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .startArray("distributors")
+ .startObject()
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Hampton St")
+ .field("city", "London")
+ .endObject()
+ .startObject()
+ .field("name", "address #2")
+ .field("street", "Queen St")
+ .field("city", "Stornoway")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject("fr")
+ .field("lang", "French")
+ .field("available", false)
+ .startArray("distributors")
+ .startObject()
+ .startArray("addresses")
+ .startObject()
+ .field("name", "address #1")
+ .field("street", "Rue Mouffetard")
+ .field("city", "Paris")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, emptySet(), singleton("properties.**.distributors.name"));
+ }
+
+ public void testRawField() throws Exception {
+ final Builder expectedRawField = builder -> builder
+ .startObject()
+ .field("foo", 0)
+ .startObject("raw")
+ .field("content", "hello world!")
+ .endObject()
+ .endObject();
+
+ final Builder expectedRawFieldFiltered = builder -> builder
+ .startObject()
+ .field("foo", 0)
+ .endObject();
+
+ final Builder expectedRawFieldNotFiltered = builder -> builder
+ .startObject()
+ .startObject("raw")
+ .field("content", "hello world!")
+ .endObject()
+ .endObject();
+
+ Builder sampleWithRaw = builder -> {
+ BytesReference raw = XContentBuilder.builder(builder.contentType().xContent())
+ .startObject()
+ .field("content", "hello world!")
+ .endObject()
+ .bytes();
+ return builder.startObject().field("foo", 0).rawField("raw", raw).endObject();
+ };
+
+ // Test method: rawField(String fieldName, BytesReference content)
+ testFilter(expectedRawField, sampleWithRaw, emptySet(), emptySet());
+ testFilter(expectedRawFieldFiltered, sampleWithRaw, singleton("f*"), emptySet());
+ testFilter(expectedRawFieldFiltered, sampleWithRaw, emptySet(), singleton("r*"));
+ testFilter(expectedRawFieldNotFiltered, sampleWithRaw, singleton("r*"), emptySet());
+ testFilter(expectedRawFieldNotFiltered, sampleWithRaw, emptySet(), singleton("f*"));
+
+ sampleWithRaw = builder -> {
+ BytesReference raw = XContentBuilder.builder(builder.contentType().xContent())
+ .startObject()
+ . field("content", "hello world!")
+ .endObject()
+ .bytes();
+ return builder.startObject().field("foo", 0).rawField("raw", raw.streamInput()).endObject();
+ };
+
+ // Test method: rawField(String fieldName, InputStream content)
+ testFilter(expectedRawField, sampleWithRaw, emptySet(), emptySet());
+ testFilter(expectedRawFieldFiltered, sampleWithRaw, singleton("f*"), emptySet());
+ testFilter(expectedRawFieldFiltered, sampleWithRaw, emptySet(), singleton("r*"));
+ testFilter(expectedRawFieldNotFiltered, sampleWithRaw, singleton("r*"), emptySet());
+ testFilter(expectedRawFieldNotFiltered, sampleWithRaw, emptySet(), singleton("f*"));
+ }
+
+ public void testArrays() throws Exception {
+ // Test: Array of values (no filtering)
+ final Builder sampleArrayOfValues = builder -> builder
+ .startObject()
+ .startArray("tags")
+ .value("lorem").value("ipsum").value("dolor")
+ .endArray()
+ .endObject();
+ testFilter(sampleArrayOfValues, sampleArrayOfValues, singleton("t*"), emptySet());
+ testFilter(sampleArrayOfValues, sampleArrayOfValues, singleton("tags"), emptySet());
+ testFilter(sampleArrayOfValues, sampleArrayOfValues, emptySet(), singleton("a"));
+
+ // Test: Array of values (with filtering)
+ Builder expected = builder -> builder.startObject().endObject();
+ testFilter(expected, sampleArrayOfValues, singleton("foo"), emptySet());
+ testFilter(expected, sampleArrayOfValues, emptySet(), singleton("t*"));
+ testFilter(expected, sampleArrayOfValues, emptySet(), singleton("tags"));
+
+ // Test: Array of objects (no filtering)
+ final Builder sampleArrayOfObjects = builder -> builder
+ .startObject()
+ .startArray("tags")
+ .startObject()
+ .field("lastname", "lorem")
+ .endObject()
+ .startObject()
+ .field("firstname", "ipsum")
+ .endObject()
+ .endArray()
+ .endObject();
+ testFilter(sampleArrayOfObjects, sampleArrayOfObjects, singleton("t*"), emptySet());
+ testFilter(sampleArrayOfObjects, sampleArrayOfObjects, singleton("tags"), emptySet());
+ testFilter(sampleArrayOfObjects, sampleArrayOfObjects, emptySet(), singleton("a"));
+
+ // Test: Array of objects (with filtering)
+ testFilter(expected, sampleArrayOfObjects, singleton("foo"), emptySet());
+ testFilter(expected, sampleArrayOfObjects, emptySet(), singleton("t*"));
+ testFilter(expected, sampleArrayOfObjects, emptySet(), singleton("tags"));
+
+ // Test: Array of objects (with partial filtering)
+ expected = builder -> builder
+ .startObject()
+ .startArray("tags")
+ .startObject()
+ .field("firstname", "ipsum")
+ .endObject()
+ .endArray()
+ .endObject();
+ testFilter(expected, sampleArrayOfObjects, singleton("t*.firstname"), emptySet());
+ testFilter(expected, sampleArrayOfObjects, emptySet(), singleton("t*.lastname"));
+ }
+
+ public void testEmptyObject() throws IOException {
+ final Builder sample = builder -> builder.startObject().startObject("foo").endObject().endObject();
+
+ Builder expected = builder -> builder.startObject().startObject("foo").endObject().endObject();
+ testFilter(expected, sample, singleton("foo"), emptySet());
+ testFilter(expected, sample, emptySet(), singleton("bar"));
+ testFilter(expected, sample, singleton("f*"), singleton("baz"));
+
+ expected = builder -> builder.startObject().endObject();
+ testFilter(expected, sample, emptySet(), singleton("foo"));
+ testFilter(expected, sample, singleton("bar"), emptySet());
+ testFilter(expected, sample, singleton("f*"), singleton("foo"));
+ }
+
+ public void testSingleFieldWithBothExcludesIncludes() throws IOException {
+ final Builder expected = builder -> builder
+ .startObject()
+ .field("pages", 456)
+ .field("price", 27.99)
+ .endObject();
+
+ testFilter(expected, SAMPLE, singleton("p*"), singleton("properties"));
+ }
+
+ public void testObjectsInArrayWithBothExcludesIncludes() throws IOException {
+ Set<String> includes = Sets.newHashSet("tags", "authors");
+ Set<String> excludes = singleton("authors.name");
+
+ final Builder expected = builder -> builder
+ .startObject()
+ .startArray("tags")
+ .value("elasticsearch")
+ .value("java")
+ .endArray()
+ .startArray("authors")
+ .startObject()
+ .field("lastname", "John")
+ .field("firstname", "Doe")
+ .endObject()
+ .startObject()
+ .field("lastname", "William")
+ .field("firstname", "Smith")
+ .endObject()
+ .endArray()
+ .endObject();
+
+ testFilter(expected, SAMPLE, includes, excludes);
+ }
+
+ public void testRecursiveObjectsInArrayWithBothExcludesIncludes() throws IOException {
+ Set<String> includes = Sets.newHashSet("**.language", "properties.weight");
+ Set<String> excludes = singleton("**.distributors");
+
+ final Builder expected = builder -> builder
+ .startObject()
+ .startObject("properties")
+ .field("weight", 0.8d)
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .endObject()
+ .startObject("fr")
+ .field("lang", "French")
+ .field("available", false)
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, includes, excludes);
+ }
+
+ public void testRecursiveSameObjectWithBothExcludesIncludes() throws IOException {
+ Set<String> includes = singleton("**.distributors");
+ Set<String> excludes = singleton("**.distributors");
+
+ final Builder expected = builder -> builder.startObject().endObject();
+ testFilter(expected, SAMPLE, includes, excludes);
+ }
+
+ public void testRecursiveObjectsPropertiesWithBothExcludesIncludes() throws IOException {
+ Set<String> includes = singleton("**.en.*");
+ Set<String> excludes = Sets.newHashSet("**.distributors.*.name", "**.street");
+
+ final Builder expected = builder -> builder
+ .startObject()
+ .startObject("properties")
+ .startObject("language")
+ .startObject("en")
+ .field("lang", "English")
+ .field("available", true)
+ .startArray("distributors")
+ .startObject()
+ .field("name", "The Book Shop")
+ .startArray("addresses")
+ .startObject()
+ .field("city", "London")
+ .endObject()
+ .startObject()
+ .field("city", "Stornoway")
+ .endObject()
+ .endArray()
+ .endObject()
+ .startObject()
+ .field("name", "Sussex Books House")
+ .endObject()
+ .endArray()
+ .endObject()
+ .endObject()
+ .endObject()
+ .endObject();
+
+ testFilter(expected, SAMPLE, includes, excludes);
+ }
+
+ public void testWithLfAtEnd() throws IOException {
+ final Builder sample = builder -> builder
+ .startObject()
+ .startObject("foo")
+ .field("bar", "baz")
+ .endObject()
+ .endObject()
+ .prettyPrint()
+ .lfAtEnd();
+
+ testFilter(sample, sample, singleton("foo"), emptySet());
+ testFilter(sample, sample, emptySet(), singleton("bar"));
+ testFilter(sample, sample, singleton("f*"), singleton("baz"));
+
+ final Builder expected = builder -> builder.startObject().endObject().prettyPrint().lfAtEnd();
+ testFilter(expected, sample, emptySet(), singleton("foo"));
+ testFilter(expected, sample, singleton("bar"), emptySet());
+ testFilter(expected, sample, singleton("f*"), singleton("foo"));
+ }
+
+ public void testBasics() throws Exception {
+ final Builder sample = builder -> builder
+ .startObject()
+ .field("test1", "value1")
+ .field("test2", "value2")
+ .field("something_else", "value3")
+ .endObject();
+
+ Builder expected = builder -> builder
+ .startObject()
+ .field("test1", "value1")
+ .endObject();
+ testFilter(expected, sample, singleton("test1"), emptySet());
+
+ expected = builder -> builder
+ .startObject()
+ .field("test1", "value1")
+ .field("test2", "value2")
+ .endObject();
+ testFilter(expected, sample, singleton("test*"), emptySet());
+
+ expected = builder -> builder
+ .startObject()
+ .field("test2", "value2")
+ .field("something_else", "value3")
+ .endObject();
+ testFilter(expected, sample, emptySet(), singleton("test1"));
+
+ // more complex object...
+ final Builder complex = builder -> builder
+ .startObject()
+ .startObject("path1")
+ .startArray("path2")
+ .startObject().field("test", "value1").endObject()
+ .startObject().field("test", "value2").endObject()
+ .endArray()
+ .endObject()
+ .field("test1", "value1")
+ .endObject();
+
+ expected = builder -> builder
+ .startObject()
+ .startObject("path1")
+ .startArray("path2")
+ .startObject().field("test", "value1").endObject()
+ .startObject().field("test", "value2").endObject()
+ .endArray()
+ .endObject()
+ .endObject();
+ testFilter(expected, complex, singleton("path1"), emptySet());
+ testFilter(expected, complex, singleton("path1*"), emptySet());
+ testFilter(expected, complex, singleton("path1.path2.*"), emptySet());
+
+ expected = builder -> builder
+ .startObject()
+ .field("test1", "value1")
+ .endObject();
+ testFilter(expected, complex, singleton("test1*"), emptySet());
+ }
+
+ /**
+ * Generalization of {@link XContentMapValuesTests#testSupplementaryCharactersInPaths()}
+ */
+ public void testFilterSupplementaryCharactersInPaths() throws IOException {
+ final Builder sample = builder -> builder
+ .startObject()
+ .field("搜索", 2)
+ .field("指数", 3)
+ .endObject();
+
+ Builder expected = builder -> builder
+ .startObject()
+ .field("搜索", 2)
+ .endObject();
+ testFilter(expected, sample, singleton("搜索"), emptySet());
+
+ expected = builder -> builder
+ .startObject()
+ .field("指数", 3)
+ .endObject();
+ testFilter(expected, sample, emptySet(), singleton("搜索"));
+ }
+
+ /**
+ * Generalization of {@link XContentMapValuesTests#testSharedPrefixes()}
+ */
+ public void testFilterSharedPrefixes() throws IOException {
+ final Builder sample = builder -> builder
+ .startObject()
+ .field("foobar", 2)
+ .field("foobaz", 3)
+ .endObject();
+
+ Builder expected = builder -> builder
+ .startObject()
+ .field("foobar", 2)
+ .endObject();
+ testFilter(expected, sample, singleton("foobar"), emptySet());
+
+ expected = builder -> builder
+ .startObject()
+ .field("foobaz", 3)
+ .endObject();
+ testFilter(expected, sample, emptySet(), singleton("foobar"));
+ }
+
+ /**
+ * Generalization of {@link XContentMapValuesTests#testPrefix()}
+ */
+ public void testFilterPrefix() throws IOException {
+ final Builder sample = builder -> builder
+ .startObject()
+ .array("photos", "foo", "bar")
+ .field("photosCount", 2)
+ .endObject();
+
+ Builder expected = builder -> builder
+ .startObject()
+ .field("photosCount", 2)
+ .endObject();
+ testFilter(expected, sample, singleton("photosCount"), emptySet());
+ }
+}