1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.helper;
17
18 import java.util.ArrayList;
19 import java.util.List;
20 import java.util.Map;
21
22 import org.apache.logging.log4j.LogManager;
23 import org.apache.logging.log4j.Logger;
24 import org.apache.lucene.search.TotalHits;
25 import org.codelibs.fess.mylasta.direction.FessConfig;
26 import org.codelibs.fess.opensearch.client.SearchEngineClient;
27 import org.codelibs.fess.opensearch.client.SearchEngineClientException;
28 import org.codelibs.fess.thumbnail.ThumbnailManager;
29 import org.codelibs.fess.util.ComponentUtil;
30 import org.codelibs.fess.util.DocList;
31 import org.codelibs.fess.util.MemoryUtil;
32 import org.opensearch.action.admin.indices.refresh.RefreshResponse;
33 import org.opensearch.action.bulk.BulkItemResponse;
34 import org.opensearch.action.bulk.BulkItemResponse.Failure;
35 import org.opensearch.action.bulk.BulkResponse;
36 import org.opensearch.action.search.SearchResponse;
37 import org.opensearch.index.query.QueryBuilder;
38 import org.opensearch.index.query.QueryBuilders;
39
40
41
42
43
44
45
46
47
48
49
50
51 public class IndexingHelper {
52
53 private static final Logger logger = LogManager.getLogger(IndexingHelper.class);
54
55
56 protected int maxRetryCount = 5;
57
58
59 protected int defaultRowSize = 100;
60
61
62
63
64
65 public IndexingHelper() {
66
67 }
68
69
70 protected long requestInterval = 500;
71
72
73
74
75
76
77
78
79
80
81 public void sendDocuments(final SearchEngineClient searchEngineClient, final DocList docList) {
82 if (docList.isEmpty()) {
83 return;
84 }
85 final FessConfig fessConfig = ComponentUtil.getFessConfig();
86 final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
87 final long execTime = systemHelper.getCurrentTimeAsLong();
88 if (logger.isDebugEnabled()) {
89 logger.debug("Sending {} documents to a server.", docList.size());
90 }
91 try {
92 if (fessConfig.isThumbnailCrawlerEnabled()) {
93 final ThumbnailManager thumbnailManager = ComponentUtil.getThumbnailManager();
94 final String thumbnailField = fessConfig.getIndexFieldThumbnail();
95 docList.stream().forEach(doc -> {
96 if (!thumbnailManager.offer(doc)) {
97 if (logger.isDebugEnabled()) {
98 logger.debug("Removing {}={} from doc[{}]", thumbnailField, doc.get(thumbnailField),
99 doc.get(fessConfig.getIndexFieldUrl()));
100 }
101 doc.remove(thumbnailField);
102 }
103 });
104 }
105 final CrawlingConfigHelper crawlingConfigHelper = ComponentUtil.getCrawlingConfigHelper();
106 synchronized (searchEngineClient) {
107 final long deletedDocCount = deleteOldDocuments(searchEngineClient, docList);
108 if (logger.isDebugEnabled()) {
109 logger.debug("Deleted {} stale documents", deletedDocCount);
110 }
111 final BulkResponse response =
112 searchEngineClient.addAll(fessConfig.getIndexDocumentUpdateIndex(), docList, (doc, builder) -> {
113 final String configId = (String) doc.get(fessConfig.getIndexFieldConfigId());
114 crawlingConfigHelper.getPipeline(configId).ifPresent(s -> builder.setPipeline(s));
115 });
116 if (response.hasFailures()) {
117 if (logger.isDebugEnabled()) {
118 final BulkItemResponse[] items = response.getItems();
119 if (docList.size() == items.length) {
120 for (int i = 0; i < docList.size(); i++) {
121 final BulkItemResponse resp = items[i];
122 if (resp.isFailed() && resp.getFailure() != null) {
123 final Map<String, Object> req = docList.get(i);
124 final Failure failure = resp.getFailure();
125 logger.debug("Failed Request: {}\n=>{}", req, failure.getMessage());
126 }
127 }
128 }
129 }
130 throw new SearchEngineClientException(response.buildFailureMessage());
131 }
132 }
133 if (logger.isInfoEnabled()) {
134 if (docList.getContentSize() > 0) {
135 logger.info("Sent {} documents (process={}ms, send={}ms, size={}, {})", docList.size(), docList.getProcessingTime(),
136 systemHelper.getCurrentTimeAsLong() - execTime, MemoryUtil.byteCountToDisplaySize(docList.getContentSize()),
137 MemoryUtil.getMemoryUsageLog());
138 } else {
139 logger.info("Sent {} documents (send={}ms, {})", docList.size(), systemHelper.getCurrentTimeAsLong() - execTime,
140 MemoryUtil.getMemoryUsageLog());
141 }
142 }
143 } finally {
144 docList.clear();
145 }
146 }
147
148
149
150
151
152
153
154
155
156
157 protected long deleteOldDocuments(final SearchEngineClient searchEngineClient, final DocList docList) {
158 final FessConfig fessConfig = ComponentUtil.getFessConfig();
159
160 final List<String> docIdList = new ArrayList<>();
161 for (final Map<String, Object> inputDoc : docList) {
162 final Object idValue = inputDoc.get(fessConfig.getIndexFieldId());
163 if (idValue == null) {
164 continue;
165 }
166
167 final Object configIdValue = inputDoc.get(fessConfig.getIndexFieldConfigId());
168 if (configIdValue == null) {
169 continue;
170 }
171
172 final QueryBuilder queryBuilder = QueryBuilders.boolQuery()
173 .must(QueryBuilders.termQuery(fessConfig.getIndexFieldUrl(), inputDoc.get(fessConfig.getIndexFieldUrl())))
174 .filter(QueryBuilders.termQuery(fessConfig.getIndexFieldConfigId(), configIdValue));
175
176 final List<Map<String, Object>> docs = getDocumentListByQuery(searchEngineClient, queryBuilder,
177 new String[] { fessConfig.getIndexFieldId(), fessConfig.getIndexFieldDocId() });
178 for (final Map<String, Object> doc : docs) {
179 final Object oldIdValue = doc.get(fessConfig.getIndexFieldId());
180 if (oldIdValue != null && !idValue.equals(oldIdValue)) {
181 final Object oldDocIdValue = doc.get(fessConfig.getIndexFieldDocId());
182 if (oldDocIdValue != null) {
183 docIdList.add(oldDocIdValue.toString());
184 }
185 }
186 }
187 if (logger.isDebugEnabled()) {
188 logger.debug("{} => {}", queryBuilder, docs);
189 }
190 }
191 if (!docIdList.isEmpty()) {
192 return deleteDocumentByQuery(searchEngineClient, fessConfig.getIndexDocumentUpdateIndex(),
193 QueryBuilders.termsQuery(fessConfig.getIndexFieldDocId(), docIdList.stream().toArray(n -> new String[n])));
194 }
195 return 0L;
196 }
197
198
199
200
201
202
203
204
205
206
207 public boolean updateDocument(final SearchEngineClient searchEngineClient, final String id, final String field, final Object value) {
208 final FessConfig fessConfig = ComponentUtil.getFessConfig();
209 return searchEngineClient.update(fessConfig.getIndexDocumentUpdateIndex(), id, field, value);
210 }
211
212
213
214
215
216
217
218
219 public boolean deleteDocument(final SearchEngineClient searchEngineClient, final String id) {
220 final FessConfig fessConfig = ComponentUtil.getFessConfig();
221 return searchEngineClient.delete(fessConfig.getIndexDocumentUpdateIndex(), id);
222 }
223
224
225
226
227
228
229
230
231 public long deleteDocumentByUrl(final SearchEngineClient searchEngineClient, final String url) {
232 final FessConfig fessConfig = ComponentUtil.getFessConfig();
233 return deleteDocumentByQuery(searchEngineClient, fessConfig.getIndexDocumentUpdateIndex(),
234 QueryBuilders.termQuery(fessConfig.getIndexFieldUrl(), url));
235 }
236
237
238
239
240
241
242
243
244 public long deleteDocumentsByDocId(final SearchEngineClient searchEngineClient, final List<String> docIdList) {
245 final FessConfig fessConfig = ComponentUtil.getFessConfig();
246 return deleteDocumentByQuery(searchEngineClient, fessConfig.getIndexDocumentUpdateIndex(),
247 QueryBuilders.termsQuery(fessConfig.getIndexFieldDocId(), docIdList.stream().toArray(n -> new String[n])));
248 }
249
250
251
252
253
254
255
256
257 public long deleteDocumentByQuery(final SearchEngineClient searchEngineClient, final QueryBuilder queryBuilder) {
258 final FessConfig fessConfig = ComponentUtil.getFessConfig();
259 return deleteDocumentByQuery(searchEngineClient, fessConfig.getIndexDocumentUpdateIndex(), queryBuilder);
260 }
261
262
263
264
265
266
267
268
269
270 protected long deleteDocumentByQuery(final SearchEngineClient searchEngineClient, final String index, final QueryBuilder queryBuilder) {
271 return searchEngineClient.deleteByQuery(index, queryBuilder);
272 }
273
274
275
276
277
278
279
280
281
282 public Map<String, Object> getDocument(final SearchEngineClient searchEngineClient, final String id, final String[] fields) {
283 final FessConfig fessConfig = ComponentUtil.getFessConfig();
284 return searchEngineClient.getDocument(fessConfig.getIndexDocumentUpdateIndex(), builder -> {
285 builder.setQuery(QueryBuilders.idsQuery().addIds(id));
286 builder.setFetchSource(fields, null);
287 return true;
288 }).orElse(null);
289 }
290
291
292
293
294
295
296
297
298
299 public List<Map<String, Object>> getDocumentListByPrefixId(final SearchEngineClient searchEngineClient, final String id,
300 final String[] fields) {
301 final FessConfig fessConfig = ComponentUtil.getFessConfig();
302 final QueryBuilder queryBuilder = QueryBuilders.prefixQuery(fessConfig.getIndexFieldId(), id);
303 return getDocumentListByQuery(searchEngineClient, queryBuilder, fields);
304 }
305
306
307
308
309
310
311
312
313 public long deleteChildDocument(final SearchEngineClient searchEngineClient, final String id) {
314 final FessConfig fessConfig = ComponentUtil.getFessConfig();
315 return searchEngineClient.deleteByQuery(fessConfig.getIndexDocumentUpdateIndex(),
316 QueryBuilders.termQuery(fessConfig.getIndexFieldParentId(), id));
317 }
318
319
320
321
322
323
324
325
326
327 public List<Map<String, Object>> getChildDocumentList(final SearchEngineClient searchEngineClient, final String id,
328 final String[] fields) {
329 final FessConfig fessConfig = ComponentUtil.getFessConfig();
330 final QueryBuilder queryBuilder = QueryBuilders.termQuery(fessConfig.getIndexFieldParentId(), id);
331 return getDocumentListByQuery(searchEngineClient, queryBuilder, fields);
332 }
333
334
335
336
337
338
339
340
341
342
343
344 protected List<Map<String, Object>> getDocumentListByQuery(final SearchEngineClient searchEngineClient, final QueryBuilder queryBuilder,
345 final String[] fields) {
346 final FessConfig fessConfig = ComponentUtil.getFessConfig();
347
348 final long numFound = getDocumentSizeByQuery(searchEngineClient, queryBuilder, fessConfig);
349 final long maxSearchDocSize = fessConfig.getIndexerMaxSearchDocSizeAsInteger().longValue();
350 final boolean exceeded = numFound > maxSearchDocSize;
351 if (exceeded) {
352 logger.warn("Max search document size exceeded: found={}, limit={}. query={}", numFound,
353 fessConfig.getIndexerMaxSearchDocSize(), queryBuilder);
354 }
355
356 if (numFound > fessConfig.getIndexerMaxResultWindowSizeAsInteger().longValue()) {
357 final List<Map<String, Object>> entityList = new ArrayList<>(Long.valueOf(numFound).intValue());
358 searchEngineClient.scrollSearch(fessConfig.getIndexDocumentUpdateIndex(), requestBuilder -> {
359 requestBuilder.setQuery(queryBuilder).setSize((int) numFound);
360 if (fields != null) {
361 requestBuilder.setFetchSource(fields, null);
362 }
363 return true;
364 }, entity -> {
365 entityList.add(entity);
366 return entityList.size() <= (exceeded ? maxSearchDocSize : numFound);
367 });
368 return entityList;
369 }
370 return searchEngineClient.getDocumentList(fessConfig.getIndexDocumentUpdateIndex(), requestBuilder -> {
371 requestBuilder.setQuery(queryBuilder).setSize((int) numFound);
372 if (fields != null) {
373 requestBuilder.setFetchSource(fields, null);
374 }
375 return true;
376 });
377 }
378
379
380
381
382
383
384
385
386
387 protected long getDocumentSizeByQuery(final SearchEngineClient searchEngineClient, final QueryBuilder queryBuilder,
388 final FessConfig fessConfig) {
389 final SearchResponse countResponse = searchEngineClient.prepareSearch(fessConfig.getIndexDocumentUpdateIndex())
390 .setQuery(queryBuilder)
391 .setSize(0)
392 .setTrackTotalHits(true)
393 .execute()
394 .actionGet(fessConfig.getIndexSearchTimeout());
395 final TotalHits totalHits = countResponse.getHits().getTotalHits();
396 if (totalHits != null) {
397 return totalHits.value();
398 }
399 return 0;
400 }
401
402
403
404
405
406
407
408 public long deleteBySessionId(final String sessionId) {
409 final SearchEngineClient searchEngineClient = ComponentUtil.getSearchEngineClient();
410 final FessConfig fessConfig = ComponentUtil.getFessConfig();
411 final String index = fessConfig.getIndexDocumentUpdateIndex();
412 return deleteBySessionId(searchEngineClient, index, sessionId);
413 }
414
415
416
417
418
419
420
421
422
423 public long deleteBySessionId(final SearchEngineClient searchEngineClient, final String index, final String sessionId) {
424 final FessConfig fessConfig = ComponentUtil.getFessConfig();
425 final QueryBuilder queryBuilder = QueryBuilders.termQuery(fessConfig.getIndexFieldSegment(), sessionId);
426 return deleteByQueryBuilder(searchEngineClient, index, queryBuilder);
427 }
428
429
430
431
432
433
434
435 public long deleteByConfigId(final String configId) {
436 final SearchEngineClient searchEngineClient = ComponentUtil.getSearchEngineClient();
437 final FessConfig fessConfig = ComponentUtil.getFessConfig();
438 final String index = fessConfig.getIndexDocumentUpdateIndex();
439 return deleteByConfigId(searchEngineClient, index, configId);
440 }
441
442
443
444
445
446
447
448
449
450 public long deleteByConfigId(final SearchEngineClient searchEngineClient, final String index, final String configId) {
451 final FessConfig fessConfig = ComponentUtil.getFessConfig();
452 final QueryBuilder queryBuilder = QueryBuilders.termQuery(fessConfig.getIndexFieldConfigId(), configId);
453 return deleteByQueryBuilder(searchEngineClient, index, queryBuilder);
454 }
455
456
457
458
459
460
461
462 public long deleteByVirtualHost(final String virtualHost) {
463 final SearchEngineClient searchEngineClient = ComponentUtil.getSearchEngineClient();
464 final FessConfig fessConfig = ComponentUtil.getFessConfig();
465 final String index = fessConfig.getIndexDocumentUpdateIndex();
466 return deleteByVirtualHost(searchEngineClient, index, virtualHost);
467 }
468
469
470
471
472
473
474
475
476
477 public long deleteByVirtualHost(final SearchEngineClient searchEngineClient, final String index, final String virtualHost) {
478 final FessConfig fessConfig = ComponentUtil.getFessConfig();
479 final QueryBuilder queryBuilder = QueryBuilders.termQuery(fessConfig.getIndexFieldVirtualHost(), virtualHost);
480 return deleteByQueryBuilder(searchEngineClient, index, queryBuilder);
481 }
482
483
484
485
486
487
488
489
490
491
492 protected long deleteByQueryBuilder(final SearchEngineClient searchEngineClient, final String index, final QueryBuilder queryBuilder) {
493 refreshIndex(searchEngineClient, index);
494 final long numOfDeleted = searchEngineClient.deleteByQuery(index, queryBuilder);
495 if (logger.isDebugEnabled()) {
496 logger.debug("Deleted {} stale documents.", numOfDeleted);
497 }
498 return numOfDeleted;
499 }
500
501
502
503
504
505
506
507
508
509 protected int refreshIndex(final SearchEngineClient searchEngineClient, final String index) {
510 final RefreshResponse response = searchEngineClient.admin().indices().prepareRefresh(index).execute().actionGet();
511 if (logger.isDebugEnabled()) {
512 logger.debug("[{}] refresh status: {} ({}/{}/{})", index, response.getStatus(), response.getTotalShards(),
513 response.getSuccessfulShards(), response.getFailedShards());
514 }
515 return response.getStatus().getStatus();
516 }
517
518
519
520
521
522
523
524
525 public long calculateDocumentSize(final Map<String, Object> dataMap) {
526 return MemoryUtil.sizeOf(dataMap);
527 }
528
529
530
531
532
533
534 public void setMaxRetryCount(final int maxRetryCount) {
535 this.maxRetryCount = maxRetryCount;
536 }
537
538
539
540
541
542
543 public void setDefaultRowSize(final int defaultRowSize) {
544 this.defaultRowSize = defaultRowSize;
545 }
546
547
548
549
550
551
552 public void setRequestInterval(final long requestInterval) {
553 this.requestInterval = requestInterval;
554 }
555
556 }