1 /*
2 * Copyright 2012-2025 CodeLibs Project and the Others.
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 * http://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,
13 * either express or implied. See the License for the specific language
14 * governing permissions and limitations under the License.
15 */
16 package org.codelibs.fess.util;
17
18 import java.io.ByteArrayOutputStream;
19 import java.io.IOException;
20 import java.io.OutputStream;
21 import java.util.function.Function;
22
23 import org.apache.logging.log4j.LogManager;
24 import org.apache.logging.log4j.Logger;
25 import org.codelibs.fess.opensearch.client.SearchEngineClient;
26 import org.lastaflute.di.exception.IORuntimeException;
27 import org.opensearch.core.xcontent.MediaType;
28 import org.opensearch.core.xcontent.ToXContent;
29 import org.opensearch.core.xcontent.XContentBuilder;
30 import org.opensearch.core.xcontent.XContentHelper;
31 import org.opensearch.search.SearchHit;
32
33 /**
34 * Utility class for search engine operations and content formatting.
35 * Provides helper methods for working with XContent builders, scrolling through search results,
36 * and converting XContent objects to different output formats.
37 */
38 public final class SearchEngineUtil {
39
40 private static final Logger logger = LogManager.getLogger(SearchEngineUtil.class);
41
42 /**
43 * Private constructor to prevent instantiation of this utility class.
44 */
45 private SearchEngineUtil() {
46 }
47
48 /**
49 * Creates an OutputStream from an XContentBuilder using the provided callback function.
50 *
51 * @param func the callback function to build XContent
52 * @param mediaType the media type for the content builder
53 * @return an OutputStream containing the built content, or an empty ByteArrayOutputStream if an error occurs
54 */
55 public static OutputStream getXContentBuilderOutputStream(final XContentBuilderCallback func, final MediaType mediaType) {
56 try (final XContentBuilder builder = func.apply(mediaType.contentBuilder(), ToXContent.EMPTY_PARAMS)) {
57 builder.flush();
58 return builder.getOutputStream();
59 } catch (final IOException e) {
60 if (logger.isDebugEnabled()) {
61 logger.debug("Failed to print the output.", e);
62 }
63 return new ByteArrayOutputStream();
64 }
65 }
66
67 /**
68 * Creates an OutputStream from a ToXContent object with the specified media type.
69 *
70 * @param xContent the content object to convert
71 * @param mediaType the media type for the output
72 * @return an OutputStream containing the converted content
73 */
74 public static OutputStream getXContentOutputStream(final ToXContent xContent, final MediaType mediaType) {
75 return getXContentBuilderOutputStream((builder, params) -> xContent.toXContent(builder, params), mediaType);
76 }
77
78 /**
79 * Scrolls through all documents in the specified index and applies the callback function to each hit.
80 *
81 * @param index the name of the index to scroll through
82 * @param callback the function to apply to each search hit, returning true to continue or false to stop
83 * @return the number of documents processed
84 */
85 public static long scroll(final String index, final Function<SearchHit, Boolean> callback) {
86 final SearchEngineClient client = ComponentUtil.getSearchEngineClient();
87 return client.<SearchHit> scrollSearch(index, searchRequestBuilder -> true, (searchResponse, hit) -> hit,
88 hit -> callback.apply(hit));
89 }
90
91 /**
92 * Converts a ToXContent object to its string representation using the specified media type.
93 *
94 * @param xContent the content object to convert
95 * @param mediaType the media type for the conversion
96 * @return the string representation of the content
97 * @throws IORuntimeException if an IO error occurs during conversion
98 */
99 public static String getXContentString(final ToXContent xContent, final MediaType mediaType) {
100 try {
101 return XContentHelper.toXContent(xContent, mediaType, ToXContent.EMPTY_PARAMS, false).utf8ToString();
102 } catch (final IOException e) {
103 throw new IORuntimeException(e);
104 }
105 }
106
107 /**
108 * Functional interface for building XContent with custom logic.
109 * Allows clients to provide custom content building implementations.
110 */
111 public interface XContentBuilderCallback {
112 /**
113 * Applies custom logic to build XContent using the provided builder and parameters.
114 *
115 * @param builder the XContentBuilder to use for building content
116 * @param params the parameters to use during content building
117 * @return the modified XContentBuilder
118 * @throws IOException if an IO error occurs during building
119 */
120 XContentBuilder apply(XContentBuilder builder, ToXContent.Params params) throws IOException;
121 }
122
123 }