1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.ds.callback;
17
18 import java.util.HashSet;
19 import java.util.Map;
20 import java.util.Set;
21 import java.util.concurrent.atomic.AtomicLong;
22
23 import org.apache.logging.log4j.LogManager;
24 import org.apache.logging.log4j.Logger;
25 import org.codelibs.core.stream.StreamUtil;
26 import org.codelibs.fess.entity.DataStoreParams;
27 import org.codelibs.fess.exception.DataStoreException;
28 import org.codelibs.fess.helper.CrawlingInfoHelper;
29 import org.codelibs.fess.helper.IndexingHelper;
30 import org.codelibs.fess.helper.SearchLogHelper;
31 import org.codelibs.fess.helper.SystemHelper;
32 import org.codelibs.fess.ingest.IngestFactory;
33 import org.codelibs.fess.ingest.Ingester;
34 import org.codelibs.fess.mylasta.direction.FessConfig;
35 import org.codelibs.fess.opensearch.client.SearchEngineClient;
36 import org.codelibs.fess.util.ComponentUtil;
37 import org.codelibs.fess.util.DocList;
38 import org.codelibs.fess.util.DocumentUtil;
39 import org.codelibs.fess.util.MemoryUtil;
40
41 import jakarta.annotation.PostConstruct;
42
43
44
45
46
47
48 public class IndexUpdateCallbackImpl implements IndexUpdateCallback {
49 private static final Logger logger = LogManager.getLogger(IndexUpdateCallbackImpl.class);
50
51
52
53
54
55 public IndexUpdateCallbackImpl() {
56
57 }
58
59
60 protected AtomicLong documentSize = new AtomicLong(0);
61
62
63 protected volatile long executeTime = 0;
64
65
66 protected final DocList docList = new DocList();
67
68
69 protected long maxDocumentRequestSize;
70
71
72 protected int maxDocumentCacheSize;
73
74
75 private IngestFactory ingestFactory = null;
76
77
78
79
80
81 @PostConstruct
82 public void init() {
83 if (logger.isDebugEnabled()) {
84 logger.debug("Initializing {}", this.getClass().getSimpleName());
85 }
86 maxDocumentRequestSize = Long.parseLong(ComponentUtil.getFessConfig().getIndexerDataMaxDocumentRequestSize());
87 maxDocumentCacheSize = ComponentUtil.getFessConfig().getIndexerDataMaxDocumentCacheSizeAsInteger();
88 if (ComponentUtil.hasIngestFactory()) {
89 ingestFactory = ComponentUtil.getIngestFactory();
90 }
91 }
92
93
94
95
96
97
98
99
100
101 @Override
102 public void store(final DataStoreParams paramMap, final Map<String, Object> dataMap) {
103 final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
104 systemHelper.calibrateCpuLoad();
105
106 final long startTime = systemHelper.getCurrentTimeAsLong();
107 final FessConfig fessConfig = ComponentUtil.getFessConfig();
108 final SearchEngineClient searchEngineClient = ComponentUtil.getSearchEngineClient();
109
110 if (logger.isDebugEnabled()) {
111 logger.debug("Adding document: url={}", dataMap.get(fessConfig.getIndexFieldUrl()));
112 }
113
114
115 final Object urlObj = dataMap.get(fessConfig.getIndexFieldUrl());
116 if (urlObj == null) {
117 final Object configId = dataMap.get(fessConfig.getIndexFieldConfigId());
118 throw new DataStoreException("URL field is null in dataMap. Cannot index document without a URL. configId: " + configId);
119 }
120
121 final IndexingHelper indexingHelper = ComponentUtil.getIndexingHelper();
122 final CrawlingInfoHelper crawlingInfoHelper = ComponentUtil.getCrawlingInfoHelper();
123 dataMap.put(fessConfig.getIndexFieldId(), crawlingInfoHelper.generateId(dataMap));
124
125 final String url = dataMap.get(fessConfig.getIndexFieldUrl()).toString();
126
127 if (fessConfig.getIndexerClickCountEnabledAsBoolean()) {
128 addClickCountField(dataMap, url, fessConfig.getIndexFieldClickCount());
129 }
130
131 if (fessConfig.getIndexerFavoriteCountEnabledAsBoolean()) {
132 addFavoriteCountField(dataMap, url, fessConfig.getIndexFieldFavoriteCount());
133 }
134
135 final Set<String> matchedLabelSet = ComponentUtil.getLabelTypeHelper().getMatchedLabelValueSet(url);
136 if (!matchedLabelSet.isEmpty()) {
137 final Set<String> newLabelSet = new HashSet<>();
138 final String[] oldLabels = DocumentUtil.getValue(dataMap, fessConfig.getIndexFieldLabel(), String[].class);
139 StreamUtil.stream(oldLabels).of(stream -> stream.forEach(newLabelSet::add));
140 matchedLabelSet.stream().forEach(newLabelSet::add);
141 dataMap.put(fessConfig.getIndexFieldLabel(), newLabelSet.toArray(new String[newLabelSet.size()]));
142 }
143
144 if (!dataMap.containsKey(fessConfig.getIndexFieldDocId())) {
145 dataMap.put(fessConfig.getIndexFieldDocId(), systemHelper.generateDocId(dataMap));
146 }
147
148 ComponentUtil.getLanguageHelper().updateDocument(dataMap);
149
150 synchronized (docList) {
151 docList.add(ingest(paramMap, dataMap));
152 final long contentSize = indexingHelper.calculateDocumentSize(dataMap);
153 docList.addContentSize(contentSize);
154 final long processingTime = systemHelper.getCurrentTimeAsLong() - startTime;
155 docList.addProcessingTime(processingTime);
156 if (logger.isDebugEnabled()) {
157 logger.debug("Added document (size={}, time={}ms). Document cache count: {}",
158 MemoryUtil.byteCountToDisplaySize(contentSize), processingTime, docList.size());
159 }
160
161 if (docList.getContentSize() >= maxDocumentRequestSize || docList.size() >= maxDocumentCacheSize) {
162 indexingHelper.sendDocuments(searchEngineClient, docList);
163 }
164 executeTime += processingTime;
165 }
166
167 documentSize.getAndIncrement();
168
169 if (logger.isDebugEnabled()) {
170 logger.debug("Total documents added: {}", documentSize.get());
171 }
172
173 }
174
175
176
177
178
179
180
181
182
183 protected Map<String, Object> ingest(final DataStoreParams paramMap, final Map<String, Object> dataMap) {
184 if (ingestFactory == null) {
185 return dataMap;
186 }
187 Map<String, Object> target = dataMap;
188 for (final Ingester ingester : ingestFactory.getIngesters()) {
189 try {
190 target = ingester.process(target, paramMap);
191 } catch (final Exception e) {
192 logger.warn("[{}] Failed to process ingester", ingester.getClass().getSimpleName(), e);
193 }
194 }
195 return target;
196 }
197
198
199
200
201
202 @Override
203 public void commit() {
204 synchronized (docList) {
205 if (!docList.isEmpty()) {
206 final IndexingHelper indexingHelper = ComponentUtil.getIndexingHelper();
207 final SearchEngineClient searchEngineClient = ComponentUtil.getSearchEngineClient();
208 indexingHelper.sendDocuments(searchEngineClient, docList);
209 }
210 }
211 }
212
213
214
215
216
217
218
219
220 protected void addClickCountField(final Map<String, Object> doc, final String url, final String clickCountField) {
221 final SearchLogHelper searchLogHelper = ComponentUtil.getSearchLogHelper();
222 final int count = searchLogHelper.getClickCount(url);
223 doc.put(clickCountField, count);
224 if (logger.isDebugEnabled()) {
225 logger.debug("Updated click count: count={}, url={}", count, url);
226 }
227 }
228
229
230
231
232
233
234
235
236 protected void addFavoriteCountField(final Map<String, Object> doc, final String url, final String favoriteCountField) {
237 final SearchLogHelper searchLogHelper = ComponentUtil.getSearchLogHelper();
238 final long count = searchLogHelper.getFavoriteCount(url);
239 doc.put(favoriteCountField, count);
240 if (logger.isDebugEnabled()) {
241 logger.debug("Updated favorite count: count={}, url={}", count, url);
242 }
243 }
244
245
246
247
248
249
250 @Override
251 public long getDocumentSize() {
252 return documentSize.get();
253 }
254
255
256
257
258
259
260 @Override
261 public long getExecuteTime() {
262 return executeTime;
263 }
264
265 }