diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/XContent.java b/core/src/main/java/org/elasticsearch/common/xcontent/XContent.java index 03e526ce8e5..35579965f30 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/XContent.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/XContent.java @@ -41,13 +41,28 @@ public interface XContent { /** * Creates a new generator using the provided output stream. */ - XContentGenerator createGenerator(OutputStream os) throws IOException; + default XContentGenerator createGenerator(OutputStream os) throws IOException { + return createGenerator(os, null, true); + } /** - * Creates a new generator using the provided output stream and some filters. + * Creates a new generator using the provided output stream and some + * inclusive filters. Same as createGenerator(os, filters, true). */ - XContentGenerator createGenerator(OutputStream os, String[] filters) throws IOException; + default XContentGenerator createGenerator(OutputStream os, String[] filters) throws IOException { + return createGenerator(os, filters, true); + } + /** + * Creates a new generator using the provided output stream and some + * filters. + * + * @param inclusive + * If true only paths matching a filter will be included in + * output. If false no path matching a filter will be included in + * output + */ + XContentGenerator createGenerator(OutputStream os, String[] filters, boolean inclusive) throws IOException; /** * Creates a parser over the provided string content. */ @@ -77,4 +92,5 @@ public interface XContent { * Creates a parser over the provided reader. */ XContentParser createParser(Reader reader) throws IOException; + } diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java b/core/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java index 6733a75db9c..a82a747624b 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/XContentBuilder.java @@ -83,6 +83,10 @@ public final class XContentBuilder implements BytesStream, Releasable { return new XContentBuilder(xContent, new BytesStreamOutput(), filters); } + public static XContentBuilder builder(XContent xContent, String[] filters, boolean inclusive) throws IOException { + return new XContentBuilder(xContent, new BytesStreamOutput(), filters, inclusive); + } + private XContentGenerator generator; private final OutputStream bos; @@ -102,13 +106,25 @@ public final class XContentBuilder implements BytesStream, Releasable { } /** - * Constructs a new builder using the provided xcontent, an OutputStream and some filters. The - * filters are used to filter fields that won't be written to the OutputStream. Make sure - * to call {@link #close()} when the builder is done with. + * Constructs a new builder using the provided xcontent, an OutputStream and + * some filters. If filters are specified, only those values matching a + * filter will be written to the output stream. Make sure to call + * {@link #close()} when the builder is done with. */ public XContentBuilder(XContent xContent, OutputStream bos, String[] filters) throws IOException { + this(xContent, bos, filters, true); + } + + /** + * Constructs a new builder using the provided xcontent, an OutputStream and + * some filters. If {@code filters} are specified and {@code inclusive} is + * true, only those values matching a filter will be written to the output + * stream. If {@code inclusive} is false, those matching will be excluded. + * Make sure to call {@link #close()} when the builder is done with. + */ + public XContentBuilder(XContent xContent, OutputStream bos, String[] filters, boolean inclusive) throws IOException { this.bos = bos; - this.generator = xContent.createGenerator(bos, filters); + this.generator = xContent.createGenerator(bos, filters, inclusive); } public XContentBuilder fieldCaseConversion(FieldCaseConversion fieldCaseConversion) { diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/XContentGenerator.java b/core/src/main/java/org/elasticsearch/common/xcontent/XContentGenerator.java index 11a42e37279..e11fc42b5a8 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/XContentGenerator.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/XContentGenerator.java @@ -121,5 +121,6 @@ public interface XContentGenerator extends Closeable { void flush() throws IOException; + @Override void close() throws IOException; } diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/cbor/CborXContent.java b/core/src/main/java/org/elasticsearch/common/xcontent/cbor/CborXContent.java index b4af0fdc78e..bc2bc81a2c0 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/cbor/CborXContent.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/cbor/CborXContent.java @@ -67,13 +67,8 @@ public class CborXContent implements XContent { } @Override - public XContentGenerator createGenerator(OutputStream os) throws IOException { - return new CborXContentGenerator(cborFactory.createGenerator(os, JsonEncoding.UTF8), os); - } - - @Override - public XContentGenerator createGenerator(OutputStream os, String[] filters) throws IOException { - return new CborXContentGenerator(cborFactory.createGenerator(os, JsonEncoding.UTF8), os, filters); + public XContentGenerator createGenerator(OutputStream os, String[] filters, boolean inclusive) throws IOException { + return new CborXContentGenerator(cborFactory.createGenerator(os, JsonEncoding.UTF8), os, filters, inclusive); } @Override @@ -108,4 +103,5 @@ public class CborXContent implements XContent { public XContentParser createParser(Reader reader) throws IOException { return new CborXContentParser(cborFactory.createParser(reader)); } + } diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/cbor/CborXContentGenerator.java b/core/src/main/java/org/elasticsearch/common/xcontent/cbor/CborXContentGenerator.java index 517266b8170..9ec690f2d1f 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/cbor/CborXContentGenerator.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/cbor/CborXContentGenerator.java @@ -20,6 +20,7 @@ package org.elasticsearch.common.xcontent.cbor; import com.fasterxml.jackson.core.JsonGenerator; + import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.json.JsonXContentGenerator; @@ -31,7 +32,11 @@ import java.io.OutputStream; public class CborXContentGenerator extends JsonXContentGenerator { public CborXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String... filters) { - super(jsonGenerator, os, filters); + this(jsonGenerator, os, filters, true); + } + + public CborXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String[] filters, boolean inclusive) { + super(jsonGenerator, os, filters, inclusive); } @Override diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/json/JsonXContent.java b/core/src/main/java/org/elasticsearch/common/xcontent/json/JsonXContent.java index 06e7fe8bd47..ee730f10b2a 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/json/JsonXContent.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/json/JsonXContent.java @@ -71,13 +71,8 @@ public class JsonXContent implements XContent { } @Override - public XContentGenerator createGenerator(OutputStream os) throws IOException { - return new JsonXContentGenerator(jsonFactory.createGenerator(os, JsonEncoding.UTF8), os); - } - - @Override - public XContentGenerator createGenerator(OutputStream os, String[] filters) throws IOException { - return new JsonXContentGenerator(jsonFactory.createGenerator(os, JsonEncoding.UTF8), os, filters); + public XContentGenerator createGenerator(OutputStream os, String[] filters, boolean inclusive) throws IOException { + return new JsonXContentGenerator(jsonFactory.createGenerator(os, JsonEncoding.UTF8), os, filters, inclusive); } @Override diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/json/JsonXContentGenerator.java b/core/src/main/java/org/elasticsearch/common/xcontent/json/JsonXContentGenerator.java index 8974021266e..96cfd63f753 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/json/JsonXContentGenerator.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/json/JsonXContentGenerator.java @@ -72,6 +72,10 @@ public class JsonXContentGenerator implements XContentGenerator { private boolean prettyPrint = false; public JsonXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String... filters) { + this(jsonGenerator, os, filters, true); + } + + public JsonXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String[] filters, boolean inclusive) { if (jsonGenerator instanceof GeneratorBase) { this.base = (GeneratorBase) jsonGenerator; } else { @@ -82,7 +86,8 @@ public class JsonXContentGenerator implements XContentGenerator { this.generator = jsonGenerator; this.filter = null; } else { - this.filter = new FilteringGeneratorDelegate(jsonGenerator, new FilterPathBasedFilter(filters), true, true); + this.filter = new FilteringGeneratorDelegate(jsonGenerator, + new FilterPathBasedFilter(filters, inclusive), true, true); this.generator = this.filter; } @@ -375,6 +380,7 @@ public class JsonXContentGenerator implements XContentGenerator { } } + @Override public final void writeRawValue(BytesReference content) throws IOException { XContentType contentType = XContentFactory.xContentType(content); if (contentType == null) { @@ -450,4 +456,5 @@ public class JsonXContentGenerator implements XContentGenerator { } generator.close(); } + } diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/smile/SmileXContent.java b/core/src/main/java/org/elasticsearch/common/xcontent/smile/SmileXContent.java index 27d6ee4626b..fc12e40f5a3 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/smile/SmileXContent.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/smile/SmileXContent.java @@ -68,13 +68,8 @@ public class SmileXContent implements XContent { } @Override - public XContentGenerator createGenerator(OutputStream os) throws IOException { - return new SmileXContentGenerator(smileFactory.createGenerator(os, JsonEncoding.UTF8), os); - } - - @Override - public XContentGenerator createGenerator(OutputStream os, String[] filters) throws IOException { - return new SmileXContentGenerator(smileFactory.createGenerator(os, JsonEncoding.UTF8), os, filters); + public XContentGenerator createGenerator(OutputStream os, String[] filters, boolean inclusive) throws IOException { + return new SmileXContentGenerator(smileFactory.createGenerator(os, JsonEncoding.UTF8), os, filters, inclusive); } @Override diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/smile/SmileXContentGenerator.java b/core/src/main/java/org/elasticsearch/common/xcontent/smile/SmileXContentGenerator.java index 451abab33d5..ac294c1db85 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/smile/SmileXContentGenerator.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/smile/SmileXContentGenerator.java @@ -20,6 +20,7 @@ package org.elasticsearch.common.xcontent.smile; import com.fasterxml.jackson.core.JsonGenerator; + import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.json.JsonXContentGenerator; @@ -31,7 +32,11 @@ import java.io.OutputStream; public class SmileXContentGenerator extends JsonXContentGenerator { public SmileXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String... filters) { - super(jsonGenerator, os, filters); + this(jsonGenerator, os, filters, true); + } + + public SmileXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String[] filters, boolean inclusive) { + super(jsonGenerator, os, filters, inclusive); } @Override diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/support/filtering/FilterPathBasedFilter.java b/core/src/main/java/org/elasticsearch/common/xcontent/support/filtering/FilterPathBasedFilter.java index d2f28611bae..69e4e79110d 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/support/filtering/FilterPathBasedFilter.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/support/filtering/FilterPathBasedFilter.java @@ -31,25 +31,30 @@ public class FilterPathBasedFilter extends TokenFilter { * Marker value that should be used to indicate that a property name * or value matches one of the filter paths. */ - private static final TokenFilter MATCHING = new TokenFilter(){}; + private static final TokenFilter MATCHING = new TokenFilter() { + }; /** * Marker value that should be used to indicate that none of the * property names/values matches one of the filter paths. */ - private static final TokenFilter NO_MATCHING = new TokenFilter(){}; + private static final TokenFilter NO_MATCHING = new TokenFilter() { + }; private final FilterPath[] filters; - public FilterPathBasedFilter(FilterPath[] filters) { + private final boolean inclusive; + + public FilterPathBasedFilter(FilterPath[] filters, boolean inclusive) { if (CollectionUtils.isEmpty(filters)) { throw new IllegalArgumentException("filters cannot be null or empty"); } + this.inclusive = inclusive; this.filters = filters; } - public FilterPathBasedFilter(String[] filters) { - this(FilterPath.compile(filters)); + public FilterPathBasedFilter(String[] filters, boolean inclusive) { + this(FilterPath.compile(filters), inclusive); } /** @@ -77,31 +82,32 @@ public class FilterPathBasedFilter extends TokenFilter { } if ((nextFilters != null) && (nextFilters.isEmpty() == false)) { - return new FilterPathBasedFilter(nextFilters.toArray(new FilterPath[nextFilters.size()])); + return new FilterPathBasedFilter(nextFilters.toArray(new FilterPath[nextFilters.size()]), inclusive); } } return NO_MATCHING; } + @Override public TokenFilter includeProperty(String name) { - TokenFilter include = evaluate(name, filters); - if (include == MATCHING) { - return TokenFilter.INCLUDE_ALL; + TokenFilter filter = evaluate(name, filters); + if (filter == MATCHING) { + return inclusive ? TokenFilter.INCLUDE_ALL : null; } - if (include == NO_MATCHING) { - return null; + if (filter == NO_MATCHING) { + return inclusive ? null : TokenFilter.INCLUDE_ALL; } - return include; + return filter; } @Override protected boolean _includeScalar() { for (FilterPath filter : filters) { if (filter.matches()) { - return true; + return inclusive; } } - return false; + return !inclusive; } } diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/yaml/YamlXContent.java b/core/src/main/java/org/elasticsearch/common/xcontent/yaml/YamlXContent.java index 994df37de30..012781cca3f 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/yaml/YamlXContent.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/yaml/YamlXContent.java @@ -66,13 +66,8 @@ public class YamlXContent implements XContent { } @Override - public XContentGenerator createGenerator(OutputStream os) throws IOException { - return new YamlXContentGenerator(yamlFactory.createGenerator(os, JsonEncoding.UTF8), os); - } - - @Override - public XContentGenerator createGenerator(OutputStream os, String[] filters) throws IOException { - return new YamlXContentGenerator(yamlFactory.createGenerator(os, JsonEncoding.UTF8), os, filters); + public XContentGenerator createGenerator(OutputStream os, String[] filters, boolean inclusive) throws IOException { + return new YamlXContentGenerator(yamlFactory.createGenerator(os, JsonEncoding.UTF8), os, filters, inclusive); } @Override diff --git a/core/src/main/java/org/elasticsearch/common/xcontent/yaml/YamlXContentGenerator.java b/core/src/main/java/org/elasticsearch/common/xcontent/yaml/YamlXContentGenerator.java index dcb2155c82e..f801401a229 100644 --- a/core/src/main/java/org/elasticsearch/common/xcontent/yaml/YamlXContentGenerator.java +++ b/core/src/main/java/org/elasticsearch/common/xcontent/yaml/YamlXContentGenerator.java @@ -20,6 +20,7 @@ package org.elasticsearch.common.xcontent.yaml; import com.fasterxml.jackson.core.JsonGenerator; + import org.elasticsearch.common.xcontent.XContentType; import org.elasticsearch.common.xcontent.json.JsonXContentGenerator; @@ -31,7 +32,11 @@ import java.io.OutputStream; public class YamlXContentGenerator extends JsonXContentGenerator { public YamlXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String... filters) { - super(jsonGenerator, os, filters); + this(jsonGenerator, os, filters, true); + } + + public YamlXContentGenerator(JsonGenerator jsonGenerator, OutputStream os, String[] filters, boolean inclusive) { + super(jsonGenerator, os, filters, inclusive); } @Override diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/support/filtering/AbstractFilteringJsonGeneratorTestCase.java b/core/src/test/java/org/elasticsearch/common/xcontent/support/filtering/AbstractFilteringJsonGeneratorTestCase.java index ed7aee33eba..e3d8735e05e 100644 --- a/core/src/test/java/org/elasticsearch/common/xcontent/support/filtering/AbstractFilteringJsonGeneratorTestCase.java +++ b/core/src/test/java/org/elasticsearch/common/xcontent/support/filtering/AbstractFilteringJsonGeneratorTestCase.java @@ -83,8 +83,16 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase } } - private XContentBuilder newXContentBuilder(String... filters) throws IOException { - return XContentBuilder.builder(getXContentType().xContent(), filters); + private XContentBuilder newXContentBuilder() throws IOException { + return XContentBuilder.builder(getXContentType().xContent()); + } + + private XContentBuilder newXContentBuilder(String filter, boolean inclusive) throws IOException { + return XContentBuilder.builder(getXContentType().xContent(), new String[] { filter }, inclusive); + } + + private XContentBuilder newXContentBuilder(String[] filters, boolean inclusive) throws IOException { + return XContentBuilder.builder(getXContentType().xContent(), filters, inclusive); } /** @@ -167,33 +175,123 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase } /** - * Instanciates a new XContentBuilder with the given filters and builds a sample with it. + * Instanciates a new XContentBuilder with the given filters and builds a + * sample with it. + * @param inclusive + * Specifies if filters are inclusive or exclusive */ - private XContentBuilder sample(String... filters) throws IOException { - return sample(newXContentBuilder(filters)); + private XContentBuilder sample(String filter, boolean inclusive) throws IOException { + return sample(newXContentBuilder(filter, inclusive)); + } + + private XContentBuilder sample(String[] filters, boolean inclusive) throws IOException { + return sample(newXContentBuilder(filters, inclusive)); + } + + private XContentBuilder sample() throws IOException { + return sample(newXContentBuilder()); } public void testNoFiltering() throws Exception { XContentBuilder expected = sample(); assertXContentBuilder(expected, sample()); - assertXContentBuilder(expected, sample("*")); - assertXContentBuilder(expected, sample("**")); + assertXContentBuilder(expected, sample("*", true)); + assertXContentBuilder(expected, sample("**", true)); + assertXContentBuilder(expected, sample("xyz", false)); } public void testNoMatch() throws Exception { XContentBuilder expected = newXContentBuilder().startObject().endObject(); - assertXContentBuilder(expected, sample("xyz")); + assertXContentBuilder(expected, sample("xyz", true)); + assertXContentBuilder(expected, sample("*", false)); + assertXContentBuilder(expected, sample("**", false)); } - public void testSimpleField() throws Exception { + public void testSimpleFieldInclusive() throws Exception { XContentBuilder expected = newXContentBuilder().startObject().field("title", "My awesome book").endObject(); - assertXContentBuilder(expected, sample("title")); + assertXContentBuilder(expected, sample("title", true)); } - public void testSimpleFieldWithWildcard() throws Exception { + public void testSimpleFieldExclusive() throws Exception { + XContentBuilder expected = newXContentBuilder().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(); + + assertXContentBuilder(expected, sample("title", false)); + } + + + public void testSimpleFieldWithWildcardInclusive() throws Exception { XContentBuilder expected = newXContentBuilder().startObject() .field("price", 27.99) .startObject("properties") @@ -246,51 +344,292 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase .endObject() .endObject(); - assertXContentBuilder(expected, sample("pr*")); + assertXContentBuilder(expected, sample("pr*", true)); } - public void testMultipleFields() throws Exception { + public void testSimpleFieldWithWildcardExclusive() throws Exception { + XContentBuilder expected = newXContentBuilder().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(); + + assertXContentBuilder(expected, sample("pr*", false)); + } + + public void testMultipleFieldsInclusive() throws Exception { XContentBuilder expected = newXContentBuilder().startObject() .field("title", "My awesome book") .field("pages", 456) .endObject(); - assertXContentBuilder(expected, sample("title", "pages")); + assertXContentBuilder(expected, sample(new String[] { "title", "pages" }, true)); } - public void testSimpleArray() throws Exception { + public void testMultipleFieldsExclusive() throws Exception { XContentBuilder expected = newXContentBuilder().startObject() - .startArray("tags") - .value("elasticsearch") - .value("java") - .endArray() + .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(); + + assertXContentBuilder(expected, sample(new String[] { "title", "pages" }, false)); + } + + + public void testSimpleArrayInclusive() throws Exception { + XContentBuilder expected = newXContentBuilder().startObject() + .startArray("tags") + .value("elasticsearch") + .value("java") + .endArray() .endObject(); - assertXContentBuilder(expected, sample("tags")); + assertXContentBuilder(expected, sample("tags", true)); } - public void testSimpleArrayOfObjects() throws Exception { + public void testSimpleArrayExclusive() throws Exception { XContentBuilder expected = newXContentBuilder().startObject() - .startArray("authors") - .startObject() - .field("name", "John Doe") - .field("lastname", "John") - .field("firstname", "Doe") + .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() - .startObject() - .field("name", "William Smith") - .field("lastname", "William") - .field("firstname", "Smith") - .endObject() - .endArray() - .endObject(); + .endObject(); - assertXContentBuilder(expected, sample("authors")); - assertXContentBuilder(expected, sample("authors.*")); - assertXContentBuilder(expected, sample("authors.*name")); + assertXContentBuilder(expected, sample("tags", false)); } - public void testSimpleArrayOfObjectsProperty() throws Exception { + + public void testSimpleArrayOfObjectsInclusive() throws Exception { + XContentBuilder expected = newXContentBuilder().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(); + + assertXContentBuilder(expected, sample("authors", true)); + assertXContentBuilder(expected, sample("authors.*", true)); + assertXContentBuilder(expected, sample("authors.*name", true)); + } + + public void testSimpleArrayOfObjectsExclusive() throws Exception { + XContentBuilder expected = newXContentBuilder().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(); + + assertXContentBuilder(expected, sample("authors", false)); + assertXContentBuilder(expected, sample("authors.*", false)); + assertXContentBuilder(expected, sample("authors.*name", false)); + } + + public void testSimpleArrayOfObjectsPropertyInclusive() throws Exception { XContentBuilder expected = newXContentBuilder().startObject() .startArray("authors") .startObject() @@ -302,11 +641,86 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase .endArray() .endObject(); - assertXContentBuilder(expected, sample("authors.lastname")); - assertXContentBuilder(expected, sample("authors.l*")); + assertXContentBuilder(expected, sample("authors.lastname", true)); + assertXContentBuilder(expected, sample("authors.l*", true)); } - public void testRecurseField1() throws Exception { + public void testSimpleArrayOfObjectsPropertyExclusive() throws Exception { + XContentBuilder expected = newXContentBuilder().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(); + + assertXContentBuilder(expected, sample("authors.lastname", false)); + assertXContentBuilder(expected, sample("authors.l*", false)); + } + + public void testRecurseField1Inclusive() throws Exception { XContentBuilder expected = newXContentBuilder().startObject() .startArray("authors") .startObject() @@ -355,10 +769,73 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase .endObject() .endObject(); - assertXContentBuilder(expected, sample("**.name")); + assertXContentBuilder(expected, sample("**.name", true)); } - public void testRecurseField2() throws Exception { + public void testRecurseField1Exclusive() throws Exception { + XContentBuilder expected = newXContentBuilder().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(); + + assertXContentBuilder(expected, sample("**.name", false)); + } + + public void testRecurseField2Inclusive() throws Exception { XContentBuilder expected = newXContentBuilder().startObject() .startObject("properties") .startObject("language") @@ -399,39 +876,176 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase .endObject() .endObject(); - assertXContentBuilder(expected, sample("properties.**.name")); + assertXContentBuilder(expected, sample("properties.**.name", true)); } - public void testRecurseField3() throws Exception { + public void testRecurseField2Exclusive() throws Exception { XContentBuilder expected = newXContentBuilder().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() + .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() - .endObject(); + .endObject(); - assertXContentBuilder(expected, sample("properties.*.en.**.name")); + assertXContentBuilder(expected, sample("properties.**.name", false)); } - public void testRecurseField4() throws Exception { + + public void testRecurseField3Inclusive() throws Exception { + XContentBuilder expected = newXContentBuilder().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(); + + assertXContentBuilder(expected, sample("properties.*.en.**.name", true)); + } + + public void testRecurseField3Exclusive() throws Exception { + XContentBuilder expected = newXContentBuilder().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(); + + assertXContentBuilder(expected, sample("properties.*.en.**.name", false)); + } + + + public void testRecurseField4Inclusive() throws Exception { XContentBuilder expected = newXContentBuilder().startObject() .startObject("properties") .startObject("language") @@ -459,47 +1073,156 @@ public abstract class AbstractFilteringJsonGeneratorTestCase extends ESTestCase .endObject() .endObject(); - assertXContentBuilder(expected, sample("properties.**.distributors.name")); + assertXContentBuilder(expected, sample("properties.**.distributors.name", true)); + } + + public void testRecurseField4Exclusive() throws Exception { + XContentBuilder expected = newXContentBuilder().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(); + + assertXContentBuilder(expected, sample("properties.**.distributors.name", false)); } public void testRawField() throws Exception { - - XContentBuilder expectedRawField = newXContentBuilder().startObject().field("foo", 0).startObject("raw").field("content", "hello world!").endObject().endObject(); + XContentBuilder expectedRawField = newXContentBuilder().startObject().field("foo", 0).startObject("raw") + .field("content", "hello world!").endObject().endObject(); XContentBuilder expectedRawFieldFiltered = newXContentBuilder().startObject().field("foo", 0).endObject(); - XContentBuilder expectedRawFieldNotFiltered =newXContentBuilder().startObject().startObject("raw").field("content", "hello world!").endObject().endObject(); + XContentBuilder expectedRawFieldNotFiltered = newXContentBuilder().startObject().startObject("raw").field("content", "hello world!") + .endObject().endObject(); BytesReference raw = newXContentBuilder().startObject().field("content", "hello world!").endObject().bytes(); // Test method: rawField(String fieldName, BytesReference content) assertXContentBuilder(expectedRawField, newXContentBuilder().startObject().field("foo", 0).rawField("raw", raw).endObject()); - assertXContentBuilder(expectedRawFieldFiltered, newXContentBuilder("f*").startObject().field("foo", 0).rawField("raw", raw).endObject()); - assertXContentBuilder(expectedRawFieldNotFiltered, newXContentBuilder("r*").startObject().field("foo", 0).rawField("raw", raw).endObject()); + assertXContentBuilder(expectedRawFieldFiltered, + newXContentBuilder("f*", true).startObject().field("foo", 0).rawField("raw", raw).endObject()); + assertXContentBuilder(expectedRawFieldFiltered, + newXContentBuilder("r*", false).startObject().field("foo", 0).rawField("raw", raw).endObject()); + assertXContentBuilder(expectedRawFieldNotFiltered, + newXContentBuilder("r*", true).startObject().field("foo", 0).rawField("raw", raw).endObject()); + assertXContentBuilder(expectedRawFieldNotFiltered, + newXContentBuilder("f*", false).startObject().field("foo", 0).rawField("raw", raw).endObject()); // Test method: rawField(String fieldName, InputStream content) - assertXContentBuilder(expectedRawField, newXContentBuilder().startObject().field("foo", 0).rawField("raw", new ByteArrayInputStream(raw.toBytes())).endObject()); - assertXContentBuilder(expectedRawFieldFiltered, newXContentBuilder("f*").startObject().field("foo", 0).rawField("raw", new ByteArrayInputStream(raw.toBytes())).endObject()); - assertXContentBuilder(expectedRawFieldNotFiltered, newXContentBuilder("r*").startObject().field("foo", 0).rawField("raw", new ByteArrayInputStream(raw.toBytes())).endObject()); + assertXContentBuilder(expectedRawField, + newXContentBuilder().startObject().field("foo", 0).rawField("raw", new ByteArrayInputStream(raw.toBytes())).endObject()); + assertXContentBuilder(expectedRawFieldFiltered, newXContentBuilder("f*", true).startObject().field("foo", 0) + .rawField("raw", new ByteArrayInputStream(raw.toBytes())).endObject()); + assertXContentBuilder(expectedRawFieldFiltered, newXContentBuilder("r*", false).startObject().field("foo", 0) + .rawField("raw", new ByteArrayInputStream(raw.toBytes())).endObject()); + assertXContentBuilder(expectedRawFieldNotFiltered, newXContentBuilder("r*", true).startObject().field("foo", 0) + .rawField("raw", new ByteArrayInputStream(raw.toBytes())).endObject()); + assertXContentBuilder(expectedRawFieldNotFiltered, newXContentBuilder("f*", false).startObject().field("foo", 0) + .rawField("raw", new ByteArrayInputStream(raw.toBytes())).endObject()); } public void testArrays() throws Exception { // Test: Array of values (no filtering) - XContentBuilder expected = newXContentBuilder().startObject().startArray("tags").value("lorem").value("ipsum").value("dolor").endArray().endObject(); - assertXContentBuilder(expected, newXContentBuilder("t*").startObject().startArray("tags").value("lorem").value("ipsum").value("dolor").endArray().endObject()); - assertXContentBuilder(expected, newXContentBuilder("tags").startObject().startArray("tags").value("lorem").value("ipsum").value("dolor").endArray().endObject()); + XContentBuilder expected = newXContentBuilder().startObject().startArray("tags").value("lorem").value("ipsum").value("dolor") + .endArray().endObject(); + assertXContentBuilder(expected, newXContentBuilder("t*", true).startObject().startArray("tags").value("lorem").value("ipsum") + .value("dolor").endArray().endObject()); + assertXContentBuilder(expected, newXContentBuilder("tags", true).startObject().startArray("tags").value("lorem").value("ipsum") + .value("dolor").endArray().endObject()); + assertXContentBuilder(expected, newXContentBuilder("a", false).startObject().startArray("tags").value("lorem").value("ipsum") + .value("dolor").endArray().endObject()); // Test: Array of values (with filtering) - assertXContentBuilder(newXContentBuilder().startObject().endObject(), newXContentBuilder("foo").startObject().startArray("tags").value("lorem").value("ipsum").value("dolor").endArray().endObject()); + assertXContentBuilder(newXContentBuilder().startObject().endObject(), newXContentBuilder("foo", true).startObject() + .startArray("tags").value("lorem").value("ipsum").value("dolor").endArray().endObject()); + assertXContentBuilder(newXContentBuilder().startObject().endObject(), newXContentBuilder("t*", false).startObject() + .startArray("tags").value("lorem").value("ipsum").value("dolor").endArray().endObject()); + assertXContentBuilder(newXContentBuilder().startObject().endObject(), newXContentBuilder("tags", false).startObject() + .startArray("tags").value("lorem").value("ipsum").value("dolor").endArray().endObject()); // Test: Array of objects (no filtering) - expected = newXContentBuilder().startObject().startArray("tags").startObject().field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject(); - assertXContentBuilder(expected, newXContentBuilder("t*").startObject().startArray("tags").startObject().field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject()); - assertXContentBuilder(expected, newXContentBuilder("tags").startObject().startArray("tags").startObject().field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject()); + expected = newXContentBuilder().startObject().startArray("tags").startObject().field("lastname", "lorem").endObject().startObject() + .field("firstname", "ipsum").endObject().endArray().endObject(); + assertXContentBuilder(expected, newXContentBuilder("t*", true).startObject().startArray("tags").startObject() + .field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject()); + assertXContentBuilder(expected, newXContentBuilder("tags", true).startObject().startArray("tags").startObject() + .field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject()); + assertXContentBuilder(expected, newXContentBuilder("a", false).startObject().startArray("tags").startObject() + .field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject()); // Test: Array of objects (with filtering) - assertXContentBuilder(newXContentBuilder().startObject().endObject(), newXContentBuilder("foo").startObject().startArray("tags").startObject().field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject()); + assertXContentBuilder(newXContentBuilder().startObject().endObject(), + newXContentBuilder("foo", true).startObject().startArray("tags").startObject().field("lastname", "lorem").endObject() + .startObject().field("firstname", "ipsum").endObject().endArray().endObject()); + assertXContentBuilder(newXContentBuilder().startObject().endObject(), + newXContentBuilder("t*", false).startObject().startArray("tags").startObject().field("lastname", "lorem").endObject() + .startObject().field("firstname", "ipsum").endObject().endArray().endObject()); + assertXContentBuilder(newXContentBuilder().startObject().endObject(), + newXContentBuilder("tags", false).startObject().startArray("tags").startObject().field("lastname", "lorem").endObject() + .startObject().field("firstname", "ipsum").endObject().endArray().endObject()); // Test: Array of objects (with partial filtering) - expected = newXContentBuilder().startObject().startArray("tags").startObject().field("firstname", "ipsum").endObject().endArray().endObject(); - assertXContentBuilder(expected, newXContentBuilder("t*.firstname").startObject().startArray("tags").startObject().field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject()); + expected = newXContentBuilder().startObject().startArray("tags").startObject().field("firstname", "ipsum").endObject().endArray() + .endObject(); + assertXContentBuilder(expected, newXContentBuilder("t*.firstname", true).startObject().startArray("tags").startObject() + .field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject()); + assertXContentBuilder(expected, newXContentBuilder("t*.lastname", false).startObject().startArray("tags").startObject() + .field("lastname", "lorem").endObject().startObject().field("firstname", "ipsum").endObject().endArray().endObject()); } } diff --git a/core/src/test/java/org/elasticsearch/common/xcontent/support/filtering/FilterPathGeneratorFilteringTests.java b/core/src/test/java/org/elasticsearch/common/xcontent/support/filtering/FilterPathGeneratorFilteringTests.java index 0ca2eafa417..dd2fe42eb8e 100644 --- a/core/src/test/java/org/elasticsearch/common/xcontent/support/filtering/FilterPathGeneratorFilteringTests.java +++ b/core/src/test/java/org/elasticsearch/common/xcontent/support/filtering/FilterPathGeneratorFilteringTests.java @@ -31,59 +31,111 @@ public class FilterPathGeneratorFilteringTests extends ESTestCase { private final JsonFactory JSON_FACTORY = new JsonFactory(); - public void testFilters() throws Exception { + public void testInclusiveFilters() throws Exception { final String SAMPLE = "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"; - assertResult(SAMPLE, "a", "{'a':0}"); - assertResult(SAMPLE, "b", "{'b':true}"); - assertResult(SAMPLE, "c", "{'c':'c_value'}"); - assertResult(SAMPLE, "d", "{'d':[0,1,2]}"); - assertResult(SAMPLE, "e", "{'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); - assertResult(SAMPLE, "h", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "z", ""); + assertResult(SAMPLE, "a", true, "{'a':0}"); + assertResult(SAMPLE, "b", true, "{'b':true}"); + assertResult(SAMPLE, "c", true, "{'c':'c_value'}"); + assertResult(SAMPLE, "d", true, "{'d':[0,1,2]}"); + assertResult(SAMPLE, "e", true, "{'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "z", true, ""); - assertResult(SAMPLE, "e.f1", "{'e':[{'f1':'f1_value'}]}"); - assertResult(SAMPLE, "e.f2", "{'e':[{'f2':'f2_value'}]}"); - assertResult(SAMPLE, "e.f*", "{'e':[{'f1':'f1_value','f2':'f2_value'}]}"); - assertResult(SAMPLE, "e.*2", "{'e':[{'f2':'f2_value'},{'g2':'g2_value'}]}"); + assertResult(SAMPLE, "e.f1", true, "{'e':[{'f1':'f1_value'}]}"); + assertResult(SAMPLE, "e.f2", true, "{'e':[{'f2':'f2_value'}]}"); + assertResult(SAMPLE, "e.f*", true, "{'e':[{'f1':'f1_value','f2':'f2_value'}]}"); + assertResult(SAMPLE, "e.*2", true, "{'e':[{'f2':'f2_value'},{'g2':'g2_value'}]}"); - assertResult(SAMPLE, "h.i", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.i.j", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.i.j.k", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.i.j.k.l", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.i", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.i.j", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.i.j.k", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.i.j.k.l", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.*", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "*.i", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.*", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "*.i", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "*.i.j", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.*.j", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.i.*", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "*.i.j", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.*.j", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.i.*", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "*.i.j.k", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.*.j.k", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.i.*.k", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.i.j.*", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "*.i.j.k", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.*.j.k", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.i.*.k", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.i.j.*", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "*.i.j.k.l", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.*.j.k.l", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.i.*.k.l", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.i.j.*.l", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.i.j.k.*", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "*.i.j.k.l", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.*.j.k.l", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.i.*.k.l", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.i.j.*.l", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.i.j.k.*", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "h.*.j.*.l", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "**.l", "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h.*.j.*.l", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "**.l", true, "{'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); - assertResult(SAMPLE, "**.*2", "{'e':[{'f2':'f2_value'},{'g2':'g2_value'}]}"); + assertResult(SAMPLE, "**.*2", true, "{'e':[{'f2':'f2_value'},{'g2':'g2_value'}]}"); } - public void testFiltersWithDots() throws Exception { - assertResult("{'a':0,'b.c':'value','b':{'c':'c_value'}}", "b.c", "{'b':{'c':'c_value'}}"); - assertResult("{'a':0,'b.c':'value','b':{'c':'c_value'}}", "b\\.c", "{'b.c':'value'}"); + public void testExclusiveFilters() throws Exception { + final String SAMPLE = "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"; + + assertResult(SAMPLE, "a", false, "{'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "b", false, "{'a':0,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "c", false, "{'a':0,'b':true,'d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "d", false, "{'a':0,'b':true,'c':'c_value','e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "e", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "h", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "z", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + + assertResult(SAMPLE, "e.f1", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "e.f2", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value'},{'g1':'g1_value','g2':'g2_value'}],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "e.f*", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'g1':'g1_value','g2':'g2_value'}],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + assertResult(SAMPLE, "e.*2", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value'},{'g1':'g1_value'}],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + + assertResult(SAMPLE, "h.i", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h.i.j", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h.i.j.k", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h.i.j.k.l", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + + assertResult(SAMPLE, "h.*", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "*.i", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + + assertResult(SAMPLE, "*.i.j", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h.*.j", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h.i.*", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + + assertResult(SAMPLE, "*.i.j.k", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h.*.j.k", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h.i.*.k", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h.i.j.*", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + + assertResult(SAMPLE, "*.i.j.k.l", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h.*.j.k.l", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h.i.*.k.l", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h.i.j.*.l", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "h.i.j.k.*", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + + assertResult(SAMPLE, "h.*.j.*.l", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + assertResult(SAMPLE, "**.l", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value','f2':'f2_value'},{'g1':'g1_value','g2':'g2_value'}]}"); + + assertResult(SAMPLE, "**.*2", false, "{'a':0,'b':true,'c':'c_value','d':[0,1,2],'e':[{'f1':'f1_value'},{'g1':'g1_value'}],'h':{'i':{'j':{'k':{'l':'l_value'}}}}}"); + } - private void assertResult(String input, String filter, String expected) throws Exception { + public void testInclusiveFiltersWithDots() throws Exception { + assertResult("{'a':0,'b.c':'value','b':{'c':'c_value'}}", "b.c", true, "{'b':{'c':'c_value'}}"); + assertResult("{'a':0,'b.c':'value','b':{'c':'c_value'}}", "b\\.c", true, "{'b.c':'value'}"); + } + + public void testExclusiveFiltersWithDots() throws Exception { + assertResult("{'a':0,'b.c':'value','b':{'c':'c_value'}}", "b.c", false, "{'a':0,'b.c':'value'}"); + assertResult("{'a':0,'b.c':'value','b':{'c':'c_value'}}", "b\\.c", false, "{'a':0,'b':{'c':'c_value'}}"); + } + + private void assertResult(String input, String filter, boolean inclusive, String expected) throws Exception { try (BytesStreamOutput os = new BytesStreamOutput()) { - try (FilteringGeneratorDelegate generator = new FilteringGeneratorDelegate(JSON_FACTORY.createGenerator(os), new FilterPathBasedFilter(new String[]{filter}), true, true)) { + try (FilteringGeneratorDelegate generator = new FilteringGeneratorDelegate(JSON_FACTORY.createGenerator(os), + new FilterPathBasedFilter(new String[] { filter }, inclusive), true, true)) { try (JsonParser parser = JSON_FACTORY.createParser(replaceQuotes(input))) { while (parser.nextToken() != null) { generator.copyCurrentStructure(parser);