1
1
package org .springframework .data .elasticsearch .core ;
2
2
3
+ import static org .springframework .util .StringUtils .*;
4
+
5
+ import java .util .ArrayList ;
6
+ import java .util .HashMap ;
7
+ import java .util .Iterator ;
8
+ import java .util .List ;
9
+ import java .util .Map ;
10
+
11
+ import org .elasticsearch .action .bulk .BulkItemResponse ;
12
+ import org .elasticsearch .action .bulk .BulkResponse ;
13
+ import org .elasticsearch .action .search .MultiSearchRequest ;
14
+ import org .elasticsearch .action .search .MultiSearchResponse ;
15
+ import org .elasticsearch .action .search .SearchRequest ;
16
+ import org .elasticsearch .common .collect .MapBuilder ;
17
+ import org .elasticsearch .index .query .MoreLikeThisQueryBuilder ;
3
18
import org .slf4j .Logger ;
4
19
import org .slf4j .LoggerFactory ;
20
+ import org .springframework .beans .BeansException ;
21
+ import org .springframework .context .ApplicationContext ;
22
+ import org .springframework .context .ApplicationContextAware ;
23
+ import org .springframework .data .domain .Page ;
5
24
import org .springframework .data .elasticsearch .ElasticsearchException ;
25
+ import org .springframework .data .elasticsearch .annotations .Document ;
6
26
import org .springframework .data .elasticsearch .annotations .Mapping ;
27
+ import org .springframework .data .elasticsearch .annotations .Setting ;
7
28
import org .springframework .data .elasticsearch .core .convert .ElasticsearchConverter ;
8
29
import org .springframework .data .elasticsearch .core .convert .MappingElasticsearchConverter ;
30
+ import org .springframework .data .elasticsearch .core .document .SearchDocumentResponse ;
9
31
import org .springframework .data .elasticsearch .core .index .MappingBuilder ;
32
+ import org .springframework .data .elasticsearch .core .mapping .ElasticsearchPersistentEntity ;
33
+ import org .springframework .data .elasticsearch .core .mapping .ElasticsearchPersistentProperty ;
10
34
import org .springframework .data .elasticsearch .core .mapping .SimpleElasticsearchMappingContext ;
35
+ import org .springframework .data .elasticsearch .core .query .DeleteQuery ;
36
+ import org .springframework .data .elasticsearch .core .query .MoreLikeThisQuery ;
37
+ import org .springframework .data .elasticsearch .core .query .NativeSearchQuery ;
38
+ import org .springframework .data .elasticsearch .core .query .NativeSearchQueryBuilder ;
39
+ import org .springframework .data .elasticsearch .core .query .Query ;
40
+ import org .springframework .util .Assert ;
11
41
import org .springframework .util .StringUtils ;
12
42
13
43
/**
16
46
* @author Sascha Woo
17
47
* @author Peter-Josef Meisch
18
48
*/
19
- public abstract class AbstractElasticsearchTemplate implements ElasticsearchOperations {
49
+ public abstract class AbstractElasticsearchTemplate implements ElasticsearchOperations , ApplicationContextAware {
20
50
21
51
private static final Logger LOGGER = LoggerFactory .getLogger (AbstractElasticsearchTemplate .class );
22
52
23
53
protected ElasticsearchConverter elasticsearchConverter ;
54
+ protected RequestFactory requestFactory ;
55
+
56
+ public RequestFactory getRequestFactory () {
57
+ return requestFactory ;
58
+ }
59
+
60
+ protected void initialize (ElasticsearchConverter elasticsearchConverter ) {
61
+ Assert .notNull (elasticsearchConverter , "elasticsearchConverter must not be null." );
62
+ this .elasticsearchConverter = elasticsearchConverter ;
63
+ this .requestFactory = new RequestFactory (elasticsearchConverter );
64
+ }
24
65
25
66
protected ElasticsearchConverter createElasticsearchConverter () {
26
67
MappingElasticsearchConverter mappingElasticsearchConverter = new MappingElasticsearchConverter (
@@ -29,6 +70,13 @@ protected ElasticsearchConverter createElasticsearchConverter() {
29
70
return mappingElasticsearchConverter ;
30
71
}
31
72
73
+ @ Override
74
+ public void setApplicationContext (ApplicationContext context ) throws BeansException {
75
+ if (elasticsearchConverter instanceof ApplicationContextAware ) {
76
+ ((ApplicationContextAware ) elasticsearchConverter ).setApplicationContext (context );
77
+ }
78
+ }
79
+
32
80
protected String buildMapping (Class <?> clazz ) {
33
81
34
82
// load mapping specified in Mapping annotation if present
@@ -53,8 +101,193 @@ protected String buildMapping(Class<?> clazz) {
53
101
}
54
102
}
55
103
104
+ @ Override
105
+ public boolean createIndex (String indexName ) {
106
+ return createIndexIfNotCreated (indexName );
107
+ }
108
+
109
+ private <T > boolean createIndexIfNotCreated (String indexName ) {
110
+ return indexExists (indexName ) || createIndex (indexName , null );
111
+ }
112
+
113
+ @ Override
114
+ public <T > boolean createIndex (Class <T > clazz ) {
115
+ return createIndexIfNotCreated (clazz );
116
+ }
117
+
118
+ private <T > boolean createIndexIfNotCreated (Class <T > clazz ) {
119
+ return indexExists (getPersistentEntityFor (clazz ).getIndexName ()) || createIndexWithSettings (clazz );
120
+ }
121
+
122
+ private <T > boolean createIndexWithSettings (Class <T > clazz ) {
123
+ if (clazz .isAnnotationPresent (Setting .class )) {
124
+ String settingPath = clazz .getAnnotation (Setting .class ).settingPath ();
125
+ if (hasText (settingPath )) {
126
+ String settings = ResourceUtil .readFileFromClasspath (settingPath );
127
+ if (hasText (settings )) {
128
+ return createIndex (getPersistentEntityFor (clazz ).getIndexName (), settings );
129
+ }
130
+ } else {
131
+ LOGGER .info ("settingPath in @Setting has to be defined. Using default instead." );
132
+ }
133
+ }
134
+ return createIndex (getPersistentEntityFor (clazz ).getIndexName (), getDefaultSettings (getPersistentEntityFor (clazz )));
135
+ }
136
+
137
+ @ Override
138
+ public <T > boolean createIndex (Class <T > clazz , Object settings ) {
139
+ return createIndex (getPersistentEntityFor (clazz ).getIndexName (), settings );
140
+ }
141
+
142
+ @ Override
143
+ public void delete (Query query , Class <?> clazz , IndexCoordinates index ) {
144
+ Assert .notNull (query , "Query must not be null." );
145
+ SearchRequest searchRequest = requestFactory .searchRequest (query , clazz , index );
146
+ DeleteQuery deleteQuery = new DeleteQuery ();
147
+ deleteQuery .setQuery (searchRequest .source ().query ());
148
+ delete (deleteQuery , index );
149
+ }
150
+
151
+ @ Override
152
+ public <T > Page <T > moreLikeThis (MoreLikeThisQuery query , Class <T > clazz , IndexCoordinates index ) {
153
+ Assert .notNull (query .getId (), "No document id defined for MoreLikeThisQuery" );
154
+ MoreLikeThisQueryBuilder moreLikeThisQueryBuilder = requestFactory .moreLikeThisQueryBuilder (query , index );
155
+ return queryForPage (new NativeSearchQueryBuilder ().withQuery (moreLikeThisQueryBuilder ).build (), clazz , index );
156
+ }
157
+
158
+ protected static String [] toArray (List <String > values ) {
159
+ String [] valuesAsArray = new String [values .size ()];
160
+ return values .toArray (valuesAsArray );
161
+ }
162
+
56
163
@ Override
57
164
public ElasticsearchConverter getElasticsearchConverter () {
58
165
return elasticsearchConverter ;
59
166
}
167
+
168
+ @ Override
169
+ public ElasticsearchPersistentEntity getPersistentEntityFor (Class clazz ) {
170
+ Assert .isTrue (clazz .isAnnotationPresent (Document .class ), "Unable to identify index name. " + clazz .getSimpleName ()
171
+ + " is not a Document. Make sure the document class is annotated with @Document(indexName=\" foo\" )" );
172
+ return elasticsearchConverter .getMappingContext ().getRequiredPersistentEntity (clazz );
173
+ }
174
+
175
+ private <T > Map getDefaultSettings (ElasticsearchPersistentEntity <T > persistentEntity ) {
176
+
177
+ if (persistentEntity .isUseServerConfiguration ())
178
+ return new HashMap ();
179
+
180
+ return new MapBuilder <String , String >().put ("index.number_of_shards" , String .valueOf (persistentEntity .getShards ()))
181
+ .put ("index.number_of_replicas" , String .valueOf (persistentEntity .getReplicas ()))
182
+ .put ("index.refresh_interval" , persistentEntity .getRefreshInterval ())
183
+ .put ("index.store.type" , persistentEntity .getIndexStoreType ()).map ();
184
+ }
185
+
186
+ protected void checkForBulkOperationFailure (BulkResponse bulkResponse ) {
187
+ if (bulkResponse .hasFailures ()) {
188
+ Map <String , String > failedDocuments = new HashMap <>();
189
+ for (BulkItemResponse item : bulkResponse .getItems ()) {
190
+ if (item .isFailed ())
191
+ failedDocuments .put (item .getId (), item .getFailureMessage ());
192
+ }
193
+ throw new ElasticsearchException (
194
+ "Bulk operation has failures. Use ElasticsearchException.getFailedDocuments() for detailed messages ["
195
+ + failedDocuments + "]" ,
196
+ failedDocuments );
197
+ }
198
+ }
199
+
200
+ /**
201
+ * @param query
202
+ * @param clazz
203
+ * @deprecated index names and types should not be set in query
204
+ */
205
+ @ Deprecated
206
+ protected void setPersistentEntityIndexAndType (Query query , Class clazz ) {
207
+ if (query .getIndices ().isEmpty ()) {
208
+ String [] indices = retrieveIndexNameFromPersistentEntity (clazz );
209
+
210
+ if (indices != null ) {
211
+ query .addIndices (indices );
212
+ }
213
+ }
214
+ if (query .getTypes ().isEmpty ()) {
215
+ String [] types = retrieveTypeFromPersistentEntity (clazz );
216
+
217
+ if (types != null ) {
218
+ query .addTypes (types );
219
+ }
220
+ }
221
+ }
222
+
223
+ private String [] retrieveIndexNameFromPersistentEntity (Class clazz ) {
224
+ if (clazz != null ) {
225
+ return new String [] { getPersistentEntityFor (clazz ).getIndexName () };
226
+ }
227
+ return null ;
228
+ }
229
+
230
+ private String [] retrieveTypeFromPersistentEntity (Class clazz ) {
231
+ if (clazz != null ) {
232
+ return new String [] { getPersistentEntityFor (clazz ).getIndexType () };
233
+ }
234
+ return null ;
235
+ }
236
+
237
+ @ Override
238
+ public <T > List <Page <T >> queryForPage (List <? extends Query > queries , Class <T > clazz , IndexCoordinates index ) {
239
+ MultiSearchRequest request = new MultiSearchRequest ();
240
+ for (Query query : queries ) {
241
+ request .add (requestFactory .searchRequest (query , clazz , index ));
242
+ }
243
+ return doMultiSearch (queries , clazz , request );
244
+ }
245
+
246
+ @ Override
247
+ public List <Page <?>> queryForPage (List <? extends Query > queries , List <Class <?>> classes , IndexCoordinates index ) {
248
+ MultiSearchRequest request = new MultiSearchRequest ();
249
+ Iterator <Class <?>> it = classes .iterator ();
250
+ for (Query query : queries ) {
251
+ request .add (requestFactory .searchRequest (query , it .next (), index ));
252
+ }
253
+ return doMultiSearch (queries , classes , request );
254
+ }
255
+
256
+ private <T > List <Page <T >> doMultiSearch (List <? extends Query > queries , Class <T > clazz , MultiSearchRequest request ) {
257
+ MultiSearchResponse .Item [] items = getMultiSearchResult (request );
258
+ List <Page <T >> res = new ArrayList <>(queries .size ());
259
+ int c = 0 ;
260
+ for (Query query : queries ) {
261
+ res .add (elasticsearchConverter .mapResults (SearchDocumentResponse .from (items [c ++].getResponse ()), clazz ,
262
+ query .getPageable ()));
263
+ }
264
+ return res ;
265
+ }
266
+
267
+ private List <Page <?>> doMultiSearch (List <? extends Query > queries , List <Class <?>> classes ,
268
+ MultiSearchRequest request ) {
269
+ MultiSearchResponse .Item [] items = getMultiSearchResult (request );
270
+ List <Page <?>> res = new ArrayList <>(queries .size ());
271
+ int c = 0 ;
272
+ Iterator <Class <?>> it = classes .iterator ();
273
+ for (Query query : queries ) {
274
+ res .add (elasticsearchConverter .mapResults (SearchDocumentResponse .from (items [c ++].getResponse ()), it .next (),
275
+ query .getPageable ()));
276
+ }
277
+ return res ;
278
+ }
279
+
280
+ abstract protected MultiSearchResponse .Item [] getMultiSearchResult (MultiSearchRequest request );
281
+
282
+ protected void setPersistentEntityId (Object entity , String id ) {
283
+
284
+ ElasticsearchPersistentEntity <?> persistentEntity = getPersistentEntityFor (entity .getClass ());
285
+ ElasticsearchPersistentProperty idProperty = persistentEntity .getIdProperty ();
286
+
287
+ // Only deal with text because ES generated Ids are strings !
288
+
289
+ if (idProperty != null && idProperty .getType ().isAssignableFrom (String .class )) {
290
+ persistentEntity .getPropertyAccessor (entity ).setProperty (idProperty , id );
291
+ }
292
+ }
60
293
}
0 commit comments