Skip to content

Commit 2c03af3

Browse files
authored
FilterPathBasedFilter support match fieldname with dot (#83178)
Current `FilterPathBasedFilter` does not support match fieldName with dot. This PR merges the changes of #83148 and #83152 together to add the ability of `matching fieldName with dot` to the `FilterPathBasedFilter`.
1 parent 5f98acc commit 2c03af3

File tree

10 files changed

+515
-74
lines changed

10 files changed

+515
-74
lines changed

benchmarks/src/main/java/org/elasticsearch/benchmark/search/fetch/subphase/FetchSourcePhaseBenchmark.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public void setup() throws IOException {
6666
);
6767
includesSet = Set.of(fetchContext.includes());
6868
excludesSet = Set.of(fetchContext.excludes());
69-
parserConfig = XContentParserConfiguration.EMPTY.withFiltering(includesSet, excludesSet);
69+
parserConfig = XContentParserConfiguration.EMPTY.withFiltering(includesSet, excludesSet, false);
7070
}
7171

7272
private BytesReference read300BytesExample() throws IOException {

benchmarks/src/main/java/org/elasticsearch/benchmark/xcontent/FilterContentBenchmark.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,7 @@ public class FilterContentBenchmark {
6161
private BytesReference source;
6262
private XContentParserConfiguration parserConfig;
6363
private Set<String> filters;
64+
private XContentParserConfiguration parserConfigMatchDotsInFieldNames;
6465

6566
@Setup
6667
public void setup() throws IOException {
@@ -72,7 +73,8 @@ public void setup() throws IOException {
7273
};
7374
source = readSource(sourceFile);
7475
filters = buildFilters();
75-
parserConfig = buildParseConfig();
76+
parserConfig = buildParseConfig(false);
77+
parserConfigMatchDotsInFieldNames = buildParseConfig(true);
7678
}
7779

7880
private Set<String> buildFilters() {
@@ -105,9 +107,14 @@ public BytesReference filterWithParserConfigCreated() throws IOException {
105107
return filter(this.parserConfig);
106108
}
107109

110+
@Benchmark
111+
public BytesReference filterWithParserConfigCreatedMatchDotsInFieldNames() throws IOException {
112+
return filter(this.parserConfigMatchDotsInFieldNames);
113+
}
114+
108115
@Benchmark
109116
public BytesReference filterWithNewParserConfig() throws IOException {
110-
XContentParserConfiguration contentParserConfiguration = buildParseConfig();
117+
XContentParserConfiguration contentParserConfiguration = buildParseConfig(false);
111118
return filter(contentParserConfiguration);
112119
}
113120

@@ -152,7 +159,7 @@ public BytesReference filterWithBuilder() throws IOException {
152159
}
153160
}
154161

155-
private XContentParserConfiguration buildParseConfig() {
162+
private XContentParserConfiguration buildParseConfig(boolean matchDotsInFieldNames) {
156163
Set<String> includes;
157164
Set<String> excludes;
158165
if (inclusive) {
@@ -162,7 +169,7 @@ private XContentParserConfiguration buildParseConfig() {
162169
includes = null;
163170
excludes = filters;
164171
}
165-
return XContentParserConfiguration.EMPTY.withFiltering(includes, excludes);
172+
return XContentParserConfiguration.EMPTY.withFiltering(includes, excludes, matchDotsInFieldNames);
166173
}
167174

168175
private BytesReference filter(XContentParserConfiguration contentParserConfiguration) throws IOException {

libs/x-content/src/main/java/org/elasticsearch/xcontent/XContentParserConfiguration.java

Lines changed: 49 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,34 +32,45 @@ public class XContentParserConfiguration {
3232
DeprecationHandler.THROW_UNSUPPORTED_OPERATION,
3333
RestApiVersion.current(),
3434
null,
35-
null
35+
null,
36+
false
3637
);
3738

3839
final NamedXContentRegistry registry;
3940
final DeprecationHandler deprecationHandler;
4041
final RestApiVersion restApiVersion;
4142
final FilterPath[] includes;
4243
final FilterPath[] excludes;
44+
final boolean filtersMatchFieldNamesWithDots;
4345

4446
private XContentParserConfiguration(
4547
NamedXContentRegistry registry,
4648
DeprecationHandler deprecationHandler,
4749
RestApiVersion restApiVersion,
4850
FilterPath[] includes,
49-
FilterPath[] excludes
51+
FilterPath[] excludes,
52+
boolean filtersMatchFieldNamesWithDots
5053
) {
5154
this.registry = registry;
5255
this.deprecationHandler = deprecationHandler;
5356
this.restApiVersion = restApiVersion;
5457
this.includes = includes;
5558
this.excludes = excludes;
59+
this.filtersMatchFieldNamesWithDots = filtersMatchFieldNamesWithDots;
5660
}
5761

5862
/**
5963
* Replace the registry backing {@link XContentParser#namedObject}.
6064
*/
6165
public XContentParserConfiguration withRegistry(NamedXContentRegistry registry) {
62-
return new XContentParserConfiguration(registry, deprecationHandler, restApiVersion, includes, excludes);
66+
return new XContentParserConfiguration(
67+
registry,
68+
deprecationHandler,
69+
restApiVersion,
70+
includes,
71+
excludes,
72+
filtersMatchFieldNamesWithDots
73+
);
6374
}
6475

6576
public NamedXContentRegistry registry() {
@@ -71,7 +82,14 @@ public NamedXContentRegistry registry() {
7182
* a deprecated field.
7283
*/
7384
public XContentParserConfiguration withDeprecationHandler(DeprecationHandler deprecationHandler) {
74-
return new XContentParserConfiguration(registry, deprecationHandler, restApiVersion, includes, excludes);
85+
return new XContentParserConfiguration(
86+
registry,
87+
deprecationHandler,
88+
restApiVersion,
89+
includes,
90+
excludes,
91+
filtersMatchFieldNamesWithDots
92+
);
7593
}
7694

7795
public DeprecationHandler deprecationHandler() {
@@ -83,7 +101,14 @@ public DeprecationHandler deprecationHandler() {
83101
* {@link RestApiVersion}.
84102
*/
85103
public XContentParserConfiguration withRestApiVersion(RestApiVersion restApiVersion) {
86-
return new XContentParserConfiguration(registry, deprecationHandler, restApiVersion, includes, excludes);
104+
return new XContentParserConfiguration(
105+
registry,
106+
deprecationHandler,
107+
restApiVersion,
108+
includes,
109+
excludes,
110+
filtersMatchFieldNamesWithDots
111+
);
87112
}
88113

89114
public RestApiVersion restApiVersion() {
@@ -93,13 +118,18 @@ public RestApiVersion restApiVersion() {
93118
/**
94119
* Replace the configured filtering.
95120
*/
96-
public XContentParserConfiguration withFiltering(Set<String> includeStrings, Set<String> excludeStrings) {
121+
public XContentParserConfiguration withFiltering(
122+
Set<String> includeStrings,
123+
Set<String> excludeStrings,
124+
boolean filtersMatchFieldNamesWithDots
125+
) {
97126
return new XContentParserConfiguration(
98127
registry,
99128
deprecationHandler,
100129
restApiVersion,
101130
FilterPath.compile(includeStrings),
102-
FilterPath.compile(excludeStrings)
131+
FilterPath.compile(excludeStrings),
132+
filtersMatchFieldNamesWithDots
103133
);
104134
}
105135

@@ -112,10 +142,20 @@ public JsonParser filter(JsonParser parser) {
112142
throw new UnsupportedOperationException("double wildcards are not supported in filtered excludes");
113143
}
114144
}
115-
filtered = new FilteringParserDelegate(filtered, new FilterPathBasedFilter(excludes, false), true, true);
145+
filtered = new FilteringParserDelegate(
146+
filtered,
147+
new FilterPathBasedFilter(excludes, false, filtersMatchFieldNamesWithDots),
148+
true,
149+
true
150+
);
116151
}
117152
if (includes != null) {
118-
filtered = new FilteringParserDelegate(filtered, new FilterPathBasedFilter(includes, true), true, true);
153+
filtered = new FilteringParserDelegate(
154+
filtered,
155+
new FilterPathBasedFilter(includes, true, filtersMatchFieldNamesWithDots),
156+
true,
157+
true
158+
);
119159
}
120160
return filtered;
121161
}

libs/x-content/src/main/java/org/elasticsearch/xcontent/support/filtering/FilterPath.java

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -68,13 +68,22 @@ private boolean isFinalNode() {
6868
* if current node is a double wildcard node, the node will also add to nextFilters.
6969
* @param name the xcontent property name
7070
* @param nextFilters nextFilters is a List, used to check the inner property of name
71+
* @param matchFieldNamesWithDots support dot in field name or not
7172
* @return true if the name equal a final node, otherwise return false
7273
*/
73-
boolean matches(String name, List<FilterPath> nextFilters) {
74+
boolean matches(String name, List<FilterPath> nextFilters, boolean matchFieldNamesWithDots) {
7475
if (nextFilters == null) {
7576
return false;
7677
}
7778

79+
// match dot first
80+
if (matchFieldNamesWithDots) {
81+
// contains dot and not the first or last char
82+
int dotIndex = name.indexOf('.');
83+
if ((dotIndex != -1) && (dotIndex != 0) && (dotIndex != name.length() - 1)) {
84+
return matchFieldNamesWithDots(name, dotIndex, nextFilters);
85+
}
86+
}
7887
FilterPath termNode = termsChildren.get(name);
7988
if (termNode != null) {
8089
if (termNode.isFinalNode()) {
@@ -102,6 +111,25 @@ boolean matches(String name, List<FilterPath> nextFilters) {
102111
return false;
103112
}
104113

114+
private boolean matchFieldNamesWithDots(String name, int dotIndex, List<FilterPath> nextFilters) {
115+
String prefixName = name.substring(0, dotIndex);
116+
String suffixName = name.substring(dotIndex + 1);
117+
List<FilterPath> prefixFilterPath = new ArrayList<>();
118+
boolean prefixMatch = matches(prefixName, prefixFilterPath, true);
119+
// if prefixMatch return true(because prefix is a final FilterPath node)
120+
if (prefixMatch) {
121+
return true;
122+
}
123+
// if has prefixNextFilter, use them to match suffix
124+
for (FilterPath filter : prefixFilterPath) {
125+
boolean matches = filter.matches(suffixName, nextFilters, true);
126+
if (matches) {
127+
return true;
128+
}
129+
}
130+
return false;
131+
}
132+
105133
private static class FilterPathBuilder {
106134
private class BuildNode {
107135
private final Map<String, BuildNode> children;

libs/x-content/src/main/java/org/elasticsearch/xcontent/support/filtering/FilterPathBasedFilter.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -42,16 +42,19 @@ public String toString() {
4242

4343
private final boolean inclusive;
4444

45-
public FilterPathBasedFilter(FilterPath[] filters, boolean inclusive) {
45+
private final boolean matchFieldNamesWithDots;
46+
47+
public FilterPathBasedFilter(FilterPath[] filters, boolean inclusive, boolean matchFieldNamesWithDots) {
4648
if (filters == null || filters.length == 0) {
4749
throw new IllegalArgumentException("filters cannot be null or empty");
4850
}
4951
this.inclusive = inclusive;
5052
this.filters = filters;
53+
this.matchFieldNamesWithDots = matchFieldNamesWithDots;
5154
}
5255

5356
public FilterPathBasedFilter(Set<String> filters, boolean inclusive) {
54-
this(FilterPath.compile(filters), inclusive);
57+
this(FilterPath.compile(filters), inclusive, false);
5558
}
5659

5760
/**
@@ -61,14 +64,18 @@ private TokenFilter evaluate(String name, FilterPath[] filterPaths) {
6164
if (filterPaths != null) {
6265
List<FilterPath> nextFilters = new ArrayList<>();
6366
for (FilterPath filter : filterPaths) {
64-
boolean matches = filter.matches(name, nextFilters);
67+
boolean matches = filter.matches(name, nextFilters, matchFieldNamesWithDots);
6568
if (matches) {
6669
return MATCHING;
6770
}
6871
}
6972

7073
if (nextFilters.isEmpty() == false) {
71-
return new FilterPathBasedFilter(nextFilters.toArray(new FilterPath[nextFilters.size()]), inclusive);
74+
return new FilterPathBasedFilter(
75+
nextFilters.toArray(new FilterPath[nextFilters.size()]),
76+
inclusive,
77+
matchFieldNamesWithDots
78+
);
7279
}
7380
}
7481
return NO_MATCHING;

0 commit comments

Comments
 (0)