Skip to content

Commit c47d284

Browse files
committed
Fix source filter setup in multiget requests.
Original Pull Request #1664 Closes #1659 (cherry picked from commit 1a02c1e)
1 parent f5d651b commit c47d284

File tree

3 files changed

+301
-36
lines changed

3 files changed

+301
-36
lines changed

src/main/java/org/springframework/data/elasticsearch/core/RequestFactory.java

Lines changed: 60 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2019-2020 the original author or authors.
2+
* Copyright 2019-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -54,6 +54,7 @@
5454
import org.elasticsearch.action.search.SearchRequest;
5555
import org.elasticsearch.action.search.SearchRequestBuilder;
5656
import org.elasticsearch.action.support.ActiveShardCount;
57+
import org.elasticsearch.action.support.WriteRequest;
5758
import org.elasticsearch.action.update.UpdateRequest;
5859
import org.elasticsearch.action.update.UpdateRequestBuilder;
5960
import org.elasticsearch.client.Client;
@@ -85,6 +86,7 @@
8586
import org.elasticsearch.script.ScriptType;
8687
import org.elasticsearch.search.aggregations.AbstractAggregationBuilder;
8788
import org.elasticsearch.search.builder.SearchSourceBuilder;
89+
import org.elasticsearch.search.fetch.subphase.FetchSourceContext;
8890
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
8991
import org.elasticsearch.search.sort.FieldSortBuilder;
9092
import org.elasticsearch.search.sort.GeoDistanceSortBuilder;
@@ -860,9 +862,7 @@ private List<MultiGetRequest.Item> getMultiRequestItems(Query searchQuery, Class
860862
elasticsearchConverter.updateQuery(searchQuery, clazz);
861863
List<MultiGetRequest.Item> items = new ArrayList<>();
862864

863-
if (!isEmpty(searchQuery.getFields())) {
864-
searchQuery.addSourceFilter(new FetchSourceFilter(toArray(searchQuery.getFields()), null));
865-
}
865+
FetchSourceContext fetchSourceContext = getFetchSourceContext(searchQuery);
866866

867867
if (!isEmpty(searchQuery.getIds())) {
868868
String indexName = index.getIndexName();
@@ -872,6 +872,11 @@ private List<MultiGetRequest.Item> getMultiRequestItems(Query searchQuery, Class
872872
if (searchQuery.getRoute() != null) {
873873
item = item.routing(searchQuery.getRoute());
874874
}
875+
876+
if (fetchSourceContext != null) {
877+
item.fetchSourceContext(fetchSourceContext);
878+
}
879+
875880
items.add(item);
876881
}
877882
}
@@ -1553,38 +1558,6 @@ private QueryBuilder getFilter(Query query) {
15531558
return elasticsearchFilter;
15541559
}
15551560

1556-
// region response stuff
1557-
1558-
/**
1559-
* extract the index settings information for a given index
1560-
*
1561-
* @param response the Elasticsearch response
1562-
* @param indexName the index name
1563-
* @return settings as {@link Document}
1564-
*/
1565-
public Document fromSettingsResponse(GetSettingsResponse response, String indexName) {
1566-
1567-
Document settings = Document.create();
1568-
1569-
if (!response.getIndexToDefaultSettings().isEmpty()) {
1570-
Settings defaultSettings = response.getIndexToDefaultSettings().get(indexName);
1571-
for (String key : defaultSettings.keySet()) {
1572-
settings.put(key, defaultSettings.get(key));
1573-
}
1574-
}
1575-
1576-
if (!response.getIndexToSettings().isEmpty()) {
1577-
Settings customSettings = response.getIndexToSettings().get(indexName);
1578-
for (String key : customSettings.keySet()) {
1579-
settings.put(key, customSettings.get(key));
1580-
}
1581-
}
1582-
1583-
return settings;
1584-
}
1585-
// endregion
1586-
1587-
// region helper functions
15881561
@Nullable
15891562
private ElasticsearchPersistentEntity<?> getPersistentEntity(@Nullable Class<?> clazz) {
15901563
return clazz != null ? elasticsearchConverter.getMappingContext().getPersistentEntity(clazz) : null;
@@ -1633,6 +1606,57 @@ private boolean hasSeqNoPrimaryTermProperty(@Nullable Class<?> entityClass) {
16331606
return entity.hasSeqNoPrimaryTermProperty();
16341607
}
16351608

1609+
private FetchSourceContext getFetchSourceContext(Query searchQuery) {
1610+
FetchSourceContext fetchSourceContext = null;
1611+
SourceFilter sourceFilter = searchQuery.getSourceFilter();
1612+
1613+
if (!isEmpty(searchQuery.getFields())) {
1614+
if (sourceFilter == null) {
1615+
sourceFilter = new FetchSourceFilter(toArray(searchQuery.getFields()), null);
1616+
} else {
1617+
ArrayList<String> arrayList = new ArrayList<>();
1618+
Collections.addAll(arrayList, sourceFilter.getIncludes());
1619+
sourceFilter = new FetchSourceFilter(toArray(arrayList), null);
1620+
}
1621+
1622+
fetchSourceContext = new FetchSourceContext(true, sourceFilter.getIncludes(), sourceFilter.getExcludes());
1623+
} else if (sourceFilter != null) {
1624+
fetchSourceContext = new FetchSourceContext(true, sourceFilter.getIncludes(), sourceFilter.getExcludes());
1625+
}
1626+
return fetchSourceContext;
1627+
}
1628+
16361629
// endregion
16371630

1631+
// region response stuff
1632+
1633+
/**
1634+
* extract the index settings information for a given index
1635+
*
1636+
* @param response the Elasticsearch response
1637+
* @param indexName the index name
1638+
* @return settings as {@link Document}
1639+
*/
1640+
public Document fromSettingsResponse(GetSettingsResponse response, String indexName) {
1641+
1642+
Document settings = Document.create();
1643+
1644+
if (!response.getIndexToDefaultSettings().isEmpty()) {
1645+
Settings defaultSettings = response.getIndexToDefaultSettings().get(indexName);
1646+
for (String key : defaultSettings.keySet()) {
1647+
settings.put(key, defaultSettings.get(key));
1648+
}
1649+
}
1650+
1651+
if (!response.getIndexToSettings().isEmpty()) {
1652+
Settings customSettings = response.getIndexToSettings().get(indexName);
1653+
for (String key : customSettings.keySet()) {
1654+
settings.put(key, customSettings.get(key));
1655+
}
1656+
}
1657+
1658+
return settings;
1659+
}
1660+
1661+
// endregion
16381662
}
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.core;
17+
18+
import static org.assertj.core.api.Assertions.*;
19+
20+
import lombok.AllArgsConstructor;
21+
import lombok.Builder;
22+
import lombok.Data;
23+
import lombok.NoArgsConstructor;
24+
25+
import java.util.Collections;
26+
import java.util.List;
27+
28+
import org.junit.jupiter.api.AfterEach;
29+
import org.junit.jupiter.api.BeforeEach;
30+
import org.junit.jupiter.api.DisplayName;
31+
import org.junit.jupiter.api.Test;
32+
import org.springframework.beans.factory.annotation.Autowired;
33+
import org.springframework.data.annotation.Id;
34+
import org.springframework.data.elasticsearch.annotations.Document;
35+
import org.springframework.data.elasticsearch.annotations.Field;
36+
import org.springframework.data.elasticsearch.annotations.FieldType;
37+
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder;
38+
import org.springframework.data.elasticsearch.core.query.Query;
39+
import org.springframework.data.elasticsearch.core.query.SourceFilter;
40+
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchRestTemplateConfiguration;
41+
import org.springframework.data.elasticsearch.junit.jupiter.SpringIntegrationTest;
42+
import org.springframework.test.context.ContextConfiguration;
43+
44+
/**
45+
* @author Peter-Josef Meisch
46+
*/
47+
@SpringIntegrationTest
48+
@ContextConfiguration(classes = { ElasticsearchRestTemplateConfiguration.class })
49+
public class SourceFilterIntegrationTests {
50+
51+
@Autowired private ElasticsearchOperations operations;
52+
private IndexOperations indexOps;
53+
54+
@BeforeEach
55+
void setUp() {
56+
indexOps = operations.indexOps(Entity.class);
57+
indexOps.create();
58+
indexOps.putMapping();
59+
60+
operations.save(Entity.builder().id("42").field1("one").field2("two").field3("three").build());
61+
indexOps.refresh();
62+
}
63+
64+
@AfterEach
65+
void tearDown() {
66+
indexOps.delete();
67+
}
68+
69+
@Test // #1659
70+
@DisplayName("should only return requested fields on search")
71+
void shouldOnlyReturnRequestedFieldsOnSearch() {
72+
73+
Query query = Query.findAll();
74+
query.addFields("field2");
75+
76+
SearchHits<Entity> searchHits = operations.search(query, Entity.class);
77+
78+
assertThat(searchHits).hasSize(1);
79+
Entity entity = searchHits.getSearchHit(0).getContent();
80+
assertThat(entity.getField1()).isNull();
81+
assertThat(entity.getField2()).isEqualTo("two");
82+
assertThat(entity.getField3()).isNull();
83+
}
84+
85+
@Test // #1659
86+
@DisplayName("should only return requested fields on multiget")
87+
void shouldOnlyReturnRequestedFieldsOnGMultiGet() {
88+
89+
Query query = new NativeSearchQueryBuilder().withIds(Collections.singleton("42")).build();
90+
query.addFields("field2");
91+
92+
List<Entity> entities = operations.multiGet(query, Entity.class);
93+
94+
assertThat(entities).hasSize(1);
95+
Entity entity = entities.get(0);
96+
assertThat(entity.getField1()).isNull();
97+
assertThat(entity.getField2()).isEqualTo("two");
98+
assertThat(entity.getField3()).isNull();
99+
}
100+
101+
@Test // #1659
102+
@DisplayName("should not return excluded fields from SourceFilter on search")
103+
void shouldNotReturnExcludedFieldsFromSourceFilterOnSearch() {
104+
105+
Query query = Query.findAll();
106+
query.addSourceFilter(new SourceFilter() {
107+
@Override
108+
public String[] getIncludes() {
109+
return new String[] {};
110+
}
111+
112+
@Override
113+
public String[] getExcludes() {
114+
return new String[] { "field2" };
115+
}
116+
});
117+
118+
SearchHits<Entity> entities = operations.search(query, Entity.class);
119+
120+
assertThat(entities).hasSize(1);
121+
Entity entity = entities.getSearchHit(0).getContent();
122+
assertThat(entity.getField1()).isNotNull();
123+
assertThat(entity.getField2()).isNull();
124+
assertThat(entity.getField3()).isNotNull();
125+
}
126+
127+
@Test // #1659
128+
@DisplayName("should not return excluded fields from SourceFilter on multiget")
129+
void shouldNotReturnExcludedFieldsFromSourceFilterOnMultiGet() {
130+
131+
Query query = new NativeSearchQueryBuilder().withIds(Collections.singleton("42")).build();
132+
query.addSourceFilter(new SourceFilter() {
133+
@Override
134+
public String[] getIncludes() {
135+
return new String[] {};
136+
}
137+
138+
@Override
139+
public String[] getExcludes() {
140+
return new String[] { "field2" };
141+
}
142+
});
143+
144+
List<Entity> entities = operations.multiGet(query, Entity.class);
145+
146+
assertThat(entities).hasSize(1);
147+
Entity entity = entities.get(0);
148+
assertThat(entity.getField1()).isNotNull();
149+
assertThat(entity.getField2()).isNull();
150+
assertThat(entity.getField3()).isNotNull();
151+
}
152+
153+
@Test // #1659
154+
@DisplayName("should only return included fields from SourceFilter on search")
155+
void shouldOnlyReturnIncludedFieldsFromSourceFilterOnSearch() {
156+
157+
Query query = Query.findAll();
158+
query.addSourceFilter(new SourceFilter() {
159+
@Override
160+
public String[] getIncludes() {
161+
return new String[] { "field2" };
162+
}
163+
164+
@Override
165+
public String[] getExcludes() {
166+
return new String[] {};
167+
}
168+
});
169+
170+
SearchHits<Entity> entities = operations.search(query, Entity.class);
171+
172+
assertThat(entities).hasSize(1);
173+
Entity entity = entities.getSearchHit(0).getContent();
174+
assertThat(entity.getField1()).isNull();
175+
assertThat(entity.getField2()).isNotNull();
176+
assertThat(entity.getField3()).isNull();
177+
}
178+
179+
@Test // #1659
180+
@DisplayName("should only return included fields from SourceFilter on multiget")
181+
void shouldOnlyReturnIncludedFieldsFromSourceFilterOnMultiGet() {
182+
183+
Query query = new NativeSearchQueryBuilder().withIds(Collections.singleton("42")).build();
184+
query.addSourceFilter(new SourceFilter() {
185+
@Override
186+
public String[] getIncludes() {
187+
return new String[] { "field2" };
188+
}
189+
190+
@Override
191+
public String[] getExcludes() {
192+
return new String[] {};
193+
}
194+
});
195+
196+
List<Entity> entities = operations.multiGet(query, Entity.class);
197+
198+
assertThat(entities).hasSize(1);
199+
Entity entity = entities.get(0);
200+
assertThat(entity.getField1()).isNull();
201+
assertThat(entity.getField2()).isNotNull();
202+
assertThat(entity.getField3()).isNull();
203+
}
204+
205+
@Data
206+
@Builder
207+
@NoArgsConstructor
208+
@AllArgsConstructor
209+
@Document(indexName = "sourcefilter-tests")
210+
public static class Entity {
211+
@Id private String id;
212+
@Field(type = FieldType.Text) private String field1;
213+
@Field(type = FieldType.Text) private String field2;
214+
@Field(type = FieldType.Text) private String field3;
215+
}
216+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright 2021 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.springframework.data.elasticsearch.core;
17+
18+
import org.springframework.data.elasticsearch.junit.jupiter.ElasticsearchTemplateConfiguration;
19+
import org.springframework.test.context.ContextConfiguration;
20+
21+
/**
22+
* @author Peter-Josef Meisch
23+
*/
24+
@ContextConfiguration(classes = { ElasticsearchTemplateConfiguration.class })
25+
public class SourceFilterIntegrationTransportTests extends SourceFilterIntegrationTests {}

0 commit comments

Comments
 (0)