Skip to content
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import org.elasticsearch.tasks.CancellableTask;

import java.io.IOException;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -214,7 +215,10 @@ static Map<String, IndexFieldCapabilities> retrieveFieldCaps(
null,
Map.of()
);
responseMap.put(parentField, fieldCap);

if (filter == null || Arrays.asList(types).contains(type)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Careful, by calling Arrays.asList in a loop, here we are converting every time the array to a new ArrayList and it is not very efficient.
There are a couple of options here:

  1. We can convert the array to a ArrayList before the for loop (this should be safe since we are not changing the array content).
  2. Use a Set instead of an ArrayList (also placed outside the loop), in this way the .contains call is O(1), while for ArrayList is O(n).
    Side note: I don't expect this array to ever become huge therefore both approach should be safe to use but to be extra safe I'd opt for the Set.
    If we are using Set we can even go for Set.of(types) since it returns an unmodifiable set!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that makes sense. To avoid the duplicated initialization of Set.of(types), I changed some other places. Also, now I have to check for the parent in the filters, so I used the same Set strategy.

responseMap.put(parentField, fieldCap);
}
}
dotIndex = parentField.lastIndexOf('.');
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -249,6 +249,111 @@ public void testFieldTypeFiltering() throws IOException {
assertNull(response.get("_index"));
}

public void testExcludeParentWhenFieldTypeFilteringDoNotIncludeItsType() throws IOException {
MapperService mapperService = createMapperService("""
{ "_doc" : {
"properties" : {
"field1" : { "type" : "keyword" },
"field2" : { "type" : "long" },
"field3" : { "type" : "text" },
"field4" : {
"type" : "object",
"properties" : {
"field5" : { "type" : "keyword" }
}
}
}
} }
""");
SearchExecutionContext sec = createSearchExecutionContext(mapperService);

Map<String, IndexFieldCapabilities> response = FieldCapabilitiesFetcher.retrieveFieldCaps(
sec,
s -> true,
Strings.EMPTY_ARRAY,
new String[] { "text", "keyword" },
FieldPredicate.ACCEPT_ALL,
getMockIndexShard(),
true
);
assertNotNull(response.get("field1"));
assertNull(response.get("field2"));
assertNotNull(response.get("field3"));
assertNull(response.get("field4"));
assertNotNull(response.get("field4.field5"));
assertNull(response.get("_index"));
}

public void testIncludeParentWhenFieldTypeFilteringIncludeItsType() throws IOException {
MapperService mapperService = createMapperService("""
{ "_doc" : {
"properties" : {
"field1" : { "type" : "keyword" },
"field2" : { "type" : "long" },
"field3" : { "type" : "text" },
"field4" : {
"type" : "object",
"properties" : {
"field5" : { "type" : "keyword" }
}
}
}
} }
""");
SearchExecutionContext sec = createSearchExecutionContext(mapperService);

Map<String, IndexFieldCapabilities> response = FieldCapabilitiesFetcher.retrieveFieldCaps(
sec,
s -> true,
Strings.EMPTY_ARRAY,
new String[] { "text", "keyword", "object" },
FieldPredicate.ACCEPT_ALL,
getMockIndexShard(),
true
);
assertNotNull(response.get("field1"));
assertNull(response.get("field2"));
assertNotNull(response.get("field3"));
assertNotNull(response.get("field4"));
assertNotNull(response.get("field4.field5"));
assertNull(response.get("_index"));
}

public void testIncludeAllFieldsWhenNoFieldTypeFiltering() throws IOException {
MapperService mapperService = createMapperService("""
{ "_doc" : {
"properties" : {
"field1" : { "type" : "keyword" },
"field2" : { "type" : "long" },
"field3" : { "type" : "text" },
"field4" : {
"type" : "object",
"properties" : {
"field5" : { "type" : "keyword" }
}
}
}
} }
""");
SearchExecutionContext sec = createSearchExecutionContext(mapperService);

Map<String, IndexFieldCapabilities> response = FieldCapabilitiesFetcher.retrieveFieldCaps(
sec,
s -> true,
Strings.EMPTY_ARRAY,
Strings.EMPTY_ARRAY,
FieldPredicate.ACCEPT_ALL,
getMockIndexShard(),
true
);
assertNotNull(response.get("field1"));
assertNotNull(response.get("field2"));
assertNotNull(response.get("field3"));
assertNotNull(response.get("field4"));
assertNotNull(response.get("field4.field5"));
assertNotNull(response.get("_index"));
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How should this work if we use the filters -parent or +parent? (ref).
Maybe we can add some more test coverage to make sure we have the expected behaviour here!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added two extra tests to cover that. What surprised me is that +parent doesn't exist as I would expect. Instead, parent is the affirmative form to retrieve the parent fields.

private IndexShard getMockIndexShard() {
IndexShard indexShard = mock(IndexShard.class);
when(indexShard.getFieldInfos()).thenReturn(FieldInfos.EMPTY);
Expand Down