1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package org.codelibs.fess.util;
17
18 import java.util.ArrayList;
19 import java.util.Collection;
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.ListIterator;
24 import java.util.Map;
25
26 import org.apache.logging.log4j.LogManager;
27 import org.apache.logging.log4j.Logger;
28 import org.codelibs.core.stream.StreamUtil;
29 import org.codelibs.fesen.action.search.SearchResponse;
30 import org.codelibs.fesen.common.document.DocumentField;
31 import org.codelibs.fesen.search.SearchHit;
32 import org.codelibs.fesen.search.SearchHits;
33 import org.codelibs.fesen.search.aggregations.Aggregations;
34 import org.codelibs.fesen.search.fetch.subphase.highlight.HighlightField;
35 import org.codelibs.fess.Constants;
36 import org.codelibs.fess.helper.QueryHelper;
37 import org.codelibs.fess.helper.ViewHelper;
38 import org.codelibs.fess.mylasta.direction.FessConfig;
39 import org.dbflute.optional.OptionalEntity;
40
41 public class QueryResponseList implements List<Map<String, Object>> {
42
43 private static final Logger logger = LogManager.getLogger(QueryResponseList.class);
44
45 protected final List<Map<String, Object>> parent;
46
47
48 protected int pageSize;
49
50
51 protected int currentPageNumber;
52
53 protected long allRecordCount;
54
55 protected String allRecordCountRelation;
56
57 protected int allPageCount;
58
59 protected boolean existNextPage;
60
61 protected boolean existPrevPage;
62
63 protected long currentStartRecordNumber;
64
65 protected long currentEndRecordNumber;
66
67 protected List<String> pageNumberList;
68
69 protected String searchQuery;
70
71 protected long execTime;
72
73 protected FacetResponse facetResponse;
74
75 protected boolean partialResults = false;
76
77 protected long queryTime;
78
79 public QueryResponseList() {
80 parent = new ArrayList<>();
81 }
82
83
84 protected QueryResponseList(final List<Map<String, Object>> parent) {
85 this.parent = parent;
86 }
87
88 public void init(final OptionalEntity<SearchResponse> searchResponseOpt, final int start, final int pageSize) {
89 searchResponseOpt.ifPresent(searchResponse -> {
90 final FessConfig fessConfig = ComponentUtil.getFessConfig();
91 final SearchHits searchHits = searchResponse.getHits();
92 allRecordCount = searchHits.getTotalHits().value;
93 allRecordCountRelation = searchHits.getTotalHits().relation.toString();
94 queryTime = searchResponse.getTook().millis();
95
96 if (searchResponse.getTotalShards() != searchResponse.getSuccessfulShards()) {
97 partialResults = true;
98 }
99
100
101 final QueryHelper queryHelper = ComponentUtil.getQueryHelper();
102 final String hlPrefix = queryHelper.getHighlightPrefix();
103 for (final SearchHit searchHit : searchHits.getHits()) {
104 final Map<String, Object> docMap = parseSearchHit(fessConfig, hlPrefix, searchHit);
105
106 if (fessConfig.isResultCollapsed()) {
107 final Map<String, SearchHits> innerHits = searchHit.getInnerHits();
108 if (innerHits != null) {
109 final SearchHits innerSearchHits = innerHits.get(fessConfig.getQueryCollapseInnerHitsName());
110 if (innerSearchHits != null) {
111 final long totalHits = innerSearchHits.getTotalHits().value;
112 if (totalHits > 1) {
113 docMap.put(fessConfig.getQueryCollapseInnerHitsName() + "_count", totalHits);
114 final DocumentField bitsField = searchHit.getFields().get(fessConfig.getIndexFieldContentMinhashBits());
115 if (bitsField != null && !bitsField.getValues().isEmpty()) {
116 docMap.put(fessConfig.getQueryCollapseInnerHitsName() + "_hash", bitsField.getValues().get(0));
117 }
118 docMap.put(fessConfig.getQueryCollapseInnerHitsName(), StreamUtil.stream(innerSearchHits.getHits())
119 .get(stream -> stream.map(v -> parseSearchHit(fessConfig, hlPrefix, v)).toArray(n -> new Map[n])));
120 }
121 }
122 }
123 }
124
125 parent.add(docMap);
126 }
127
128
129 final Aggregations aggregations = searchResponse.getAggregations();
130 if (aggregations != null) {
131 facetResponse = new FacetResponse(aggregations);
132 }
133
134 });
135
136 if (pageSize > 0) {
137 calculatePageInfo(start, pageSize);
138 }
139 }
140
141 protected Map<String, Object> parseSearchHit(final FessConfig fessConfig, final String hlPrefix, final SearchHit searchHit) {
142 final Map<String, Object> docMap = new HashMap<>(32);
143 if (searchHit.getSourceAsMap() == null) {
144 searchHit.getFields().forEach((key, value) -> {
145 docMap.put(key, value.getValue());
146 });
147 } else {
148 docMap.putAll(searchHit.getSourceAsMap());
149 }
150
151 final ViewHelper viewHelper = ComponentUtil.getViewHelper();
152
153 final Map<String, HighlightField> highlightFields = searchHit.getHighlightFields();
154 try {
155 if (highlightFields != null) {
156 highlightFields.values().stream().forEach(highlightField -> {
157 final String text = viewHelper.createHighlightText(highlightField);
158 if (text != null) {
159 docMap.put(hlPrefix + highlightField.getName(), text);
160 }
161 });
162 if (Constants.TEXT_FRAGMENT_TYPE_HIGHLIGHT.equals(fessConfig.getQueryHighlightTextFragmentType())) {
163 docMap.put(Constants.TEXT_FRAGMENTS,
164 viewHelper.createTextFragmentsByHighlight(highlightFields.values().toArray(n -> new HighlightField[n])));
165 }
166 }
167 } catch (final Exception e) {
168 if (logger.isDebugEnabled()) {
169 logger.debug("Could not create a highlighting value: {}", docMap, e);
170 }
171 }
172
173 if (Constants.TEXT_FRAGMENT_TYPE_QUERY.equals(fessConfig.getQueryHighlightTextFragmentType())) {
174 docMap.put(Constants.TEXT_FRAGMENTS, viewHelper.createTextFragmentsByQuery());
175 }
176
177
178 if (viewHelper != null) {
179 docMap.put(fessConfig.getResponseFieldContentTitle(), viewHelper.getContentTitle(docMap));
180 docMap.put(fessConfig.getResponseFieldContentDescription(), viewHelper.getContentDescription(docMap));
181 docMap.put(fessConfig.getResponseFieldUrlLink(), viewHelper.getUrlLink(docMap));
182 docMap.put(fessConfig.getResponseFieldSitePath(), viewHelper.getSitePath(docMap));
183 }
184
185 if (!docMap.containsKey(Constants.SCORE)) {
186 docMap.put(Constants.SCORE, searchHit.getScore());
187 }
188
189 if (!docMap.containsKey(fessConfig.getIndexFieldId())) {
190 docMap.put(fessConfig.getIndexFieldId(), searchHit.getId());
191 }
192 return docMap;
193 }
194
195 protected void calculatePageInfo(final int start, final int size) {
196 pageSize = size;
197 allPageCount = (int) ((allRecordCount - 1) / pageSize) + 1;
198 existPrevPage = start > 0;
199 existNextPage = start < (long) (allPageCount - 1) * (long) pageSize;
200 currentPageNumber = start / pageSize + 1;
201 if (existNextPage && size() < pageSize) {
202
203 existNextPage = false;
204 allPageCount = currentPageNumber;
205 }
206 currentStartRecordNumber = allRecordCount != 0 ? start + 1 : 0;
207 currentEndRecordNumber = currentStartRecordNumber + pageSize - 1;
208 currentEndRecordNumber = allRecordCount < currentEndRecordNumber ? allRecordCount : currentEndRecordNumber;
209
210 final int pageRangeSize = 5;
211 int startPageRangeSize = currentPageNumber - pageRangeSize;
212 if (startPageRangeSize < 1) {
213 startPageRangeSize = 1;
214 }
215 int endPageRangeSize = currentPageNumber + pageRangeSize;
216 if (endPageRangeSize > allPageCount) {
217 endPageRangeSize = allPageCount;
218 }
219 pageNumberList = new ArrayList<>();
220 for (int i = startPageRangeSize; i <= endPageRangeSize; i++) {
221 pageNumberList.add(String.valueOf(i));
222 }
223 }
224
225 @Override
226 public boolean add(final Map<String, Object> e) {
227 return parent.add(e);
228 }
229
230 @Override
231 public void add(final int index, final Map<String, Object> element) {
232 parent.add(index, element);
233 }
234
235 @Override
236 public boolean addAll(final Collection<? extends Map<String, Object>> c) {
237 return parent.addAll(c);
238 }
239
240 @Override
241 public boolean addAll(final int index, final Collection<? extends Map<String, Object>> c) {
242 return parent.addAll(index, c);
243 }
244
245 @Override
246 public void clear() {
247 parent.clear();
248 }
249
250 @Override
251 public boolean contains(final Object o) {
252 return parent.contains(o);
253 }
254
255 @Override
256 public boolean containsAll(final Collection<?> c) {
257 return parent.containsAll(c);
258 }
259
260 @Override
261 public boolean equals(final Object o) {
262 return parent.equals(o);
263 }
264
265 @Override
266 public Map<String, Object> get(final int index) {
267 return parent.get(index);
268 }
269
270 @Override
271 public int hashCode() {
272 return parent.hashCode();
273 }
274
275 @Override
276 public int indexOf(final Object o) {
277 return parent.indexOf(o);
278 }
279
280 @Override
281 public boolean isEmpty() {
282 return parent.isEmpty();
283 }
284
285 @Override
286 public Iterator<Map<String, Object>> iterator() {
287 return parent.iterator();
288 }
289
290 @Override
291 public int lastIndexOf(final Object o) {
292 return parent.lastIndexOf(o);
293 }
294
295 @Override
296 public ListIterator<Map<String, Object>> listIterator() {
297 return parent.listIterator();
298 }
299
300 @Override
301 public ListIterator<Map<String, Object>> listIterator(final int index) {
302 return parent.listIterator(index);
303 }
304
305 @Override
306 public Map<String, Object> remove(final int index) {
307 return parent.remove(index);
308 }
309
310 @Override
311 public boolean remove(final Object o) {
312 return parent.remove(o);
313 }
314
315 @Override
316 public boolean removeAll(final Collection<?> c) {
317 return parent.removeAll(c);
318 }
319
320 @Override
321 public boolean retainAll(final Collection<?> c) {
322 return parent.retainAll(c);
323 }
324
325 @Override
326 public Map<String, Object> set(final int index, final Map<String, Object> element) {
327 return parent.set(index, element);
328 }
329
330 @Override
331 public int size() {
332 return parent.size();
333 }
334
335 @Override
336 public List<Map<String, Object>> subList(final int fromIndex, final int toIndex) {
337 return parent.subList(fromIndex, toIndex);
338 }
339
340 @Override
341 public Object[] toArray() {
342 return parent.toArray();
343 }
344
345 @Override
346 public <T> T[] toArray(final T[] a) {
347 return parent.toArray(a);
348 }
349
350 public int getPageSize() {
351 return pageSize;
352 }
353
354 public int getCurrentPageNumber() {
355 return currentPageNumber;
356 }
357
358 public long getAllRecordCount() {
359 return allRecordCount;
360 }
361
362 public String getAllRecordCountRelation() {
363 return allRecordCountRelation;
364 }
365
366 public int getAllPageCount() {
367 return allPageCount;
368 }
369
370 public boolean isExistNextPage() {
371 return existNextPage;
372 }
373
374 public boolean isExistPrevPage() {
375 return existPrevPage;
376 }
377
378 public long getCurrentStartRecordNumber() {
379 return currentStartRecordNumber;
380 }
381
382 public long getCurrentEndRecordNumber() {
383 return currentEndRecordNumber;
384 }
385
386 public List<String> getPageNumberList() {
387 return pageNumberList;
388 }
389
390 public String getSearchQuery() {
391 return searchQuery;
392 }
393
394 public void setSearchQuery(final String searchQuery) {
395 this.searchQuery = searchQuery;
396 }
397
398 public long getExecTime() {
399 return execTime;
400 }
401
402 public void setExecTime(final long execTime) {
403 this.execTime = execTime;
404 }
405
406 public FacetResponse getFacetResponse() {
407 return facetResponse;
408 }
409
410 public boolean isPartialResults() {
411 return partialResults;
412 }
413
414 public long getQueryTime() {
415 return queryTime;
416 }
417
418 }