FessEsClient.java 44 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069
  1. package org.codelibs.fess.client;
  2. import static org.codelibs.elasticsearch.runner.ElasticsearchClusterRunner.newConfigs;
  3. import java.util.ArrayList;
  4. import java.util.List;
  5. import java.util.Map;
  6. import java.util.Map.Entry;
  7. import java.util.Optional;
  8. import java.util.Set;
  9. import org.apache.commons.codec.Charsets;
  10. import org.codelibs.core.beans.util.BeanUtil;
  11. import org.codelibs.core.io.FileUtil;
  12. import org.codelibs.core.lang.StringUtil;
  13. import org.codelibs.elasticsearch.runner.ElasticsearchClusterRunner;
  14. import org.codelibs.elasticsearch.runner.ElasticsearchClusterRunner.Configs;
  15. import org.codelibs.fess.Constants;
  16. import org.codelibs.fess.ResultOffsetExceededException;
  17. import org.codelibs.fess.entity.FacetInfo;
  18. import org.codelibs.fess.entity.GeoInfo;
  19. import org.codelibs.fess.entity.PingResponse;
  20. import org.codelibs.fess.entity.SearchQuery;
  21. import org.codelibs.fess.entity.SearchQuery.SortField;
  22. import org.codelibs.fess.solr.FessSolrQueryException;
  23. import org.codelibs.fess.util.ComponentUtil;
  24. import org.elasticsearch.ElasticsearchException;
  25. import org.elasticsearch.action.Action;
  26. import org.elasticsearch.action.ActionFuture;
  27. import org.elasticsearch.action.ActionListener;
  28. import org.elasticsearch.action.ActionRequest;
  29. import org.elasticsearch.action.ActionRequestBuilder;
  30. import org.elasticsearch.action.ActionResponse;
  31. import org.elasticsearch.action.ShardOperationFailedException;
  32. import org.elasticsearch.action.admin.cluster.health.ClusterHealthResponse;
  33. import org.elasticsearch.action.admin.indices.create.CreateIndexResponse;
  34. import org.elasticsearch.action.admin.indices.flush.FlushResponse;
  35. import org.elasticsearch.action.admin.indices.mapping.get.GetMappingsResponse;
  36. import org.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
  37. import org.elasticsearch.action.admin.indices.optimize.OptimizeResponse;
  38. import org.elasticsearch.action.admin.indices.refresh.RefreshResponse;
  39. import org.elasticsearch.action.bulk.BulkRequest;
  40. import org.elasticsearch.action.bulk.BulkRequestBuilder;
  41. import org.elasticsearch.action.bulk.BulkResponse;
  42. import org.elasticsearch.action.count.CountRequest;
  43. import org.elasticsearch.action.count.CountRequestBuilder;
  44. import org.elasticsearch.action.count.CountResponse;
  45. import org.elasticsearch.action.delete.DeleteRequest;
  46. import org.elasticsearch.action.delete.DeleteRequestBuilder;
  47. import org.elasticsearch.action.delete.DeleteResponse;
  48. import org.elasticsearch.action.deletebyquery.DeleteByQueryRequest;
  49. import org.elasticsearch.action.deletebyquery.DeleteByQueryRequestBuilder;
  50. import org.elasticsearch.action.deletebyquery.DeleteByQueryResponse;
  51. import org.elasticsearch.action.exists.ExistsRequest;
  52. import org.elasticsearch.action.exists.ExistsRequestBuilder;
  53. import org.elasticsearch.action.exists.ExistsResponse;
  54. import org.elasticsearch.action.explain.ExplainRequest;
  55. import org.elasticsearch.action.explain.ExplainRequestBuilder;
  56. import org.elasticsearch.action.explain.ExplainResponse;
  57. import org.elasticsearch.action.fieldstats.FieldStatsRequest;
  58. import org.elasticsearch.action.fieldstats.FieldStatsRequestBuilder;
  59. import org.elasticsearch.action.fieldstats.FieldStatsResponse;
  60. import org.elasticsearch.action.get.GetRequest;
  61. import org.elasticsearch.action.get.GetRequestBuilder;
  62. import org.elasticsearch.action.get.GetResponse;
  63. import org.elasticsearch.action.get.MultiGetRequest;
  64. import org.elasticsearch.action.get.MultiGetRequestBuilder;
  65. import org.elasticsearch.action.get.MultiGetResponse;
  66. import org.elasticsearch.action.index.IndexRequest;
  67. import org.elasticsearch.action.index.IndexRequest.OpType;
  68. import org.elasticsearch.action.index.IndexRequestBuilder;
  69. import org.elasticsearch.action.index.IndexResponse;
  70. import org.elasticsearch.action.indexedscripts.delete.DeleteIndexedScriptRequest;
  71. import org.elasticsearch.action.indexedscripts.delete.DeleteIndexedScriptRequestBuilder;
  72. import org.elasticsearch.action.indexedscripts.delete.DeleteIndexedScriptResponse;
  73. import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptRequest;
  74. import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptRequestBuilder;
  75. import org.elasticsearch.action.indexedscripts.get.GetIndexedScriptResponse;
  76. import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptRequest;
  77. import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptRequestBuilder;
  78. import org.elasticsearch.action.indexedscripts.put.PutIndexedScriptResponse;
  79. import org.elasticsearch.action.mlt.MoreLikeThisRequest;
  80. import org.elasticsearch.action.mlt.MoreLikeThisRequestBuilder;
  81. import org.elasticsearch.action.percolate.MultiPercolateRequest;
  82. import org.elasticsearch.action.percolate.MultiPercolateRequestBuilder;
  83. import org.elasticsearch.action.percolate.MultiPercolateResponse;
  84. import org.elasticsearch.action.percolate.PercolateRequest;
  85. import org.elasticsearch.action.percolate.PercolateRequestBuilder;
  86. import org.elasticsearch.action.percolate.PercolateResponse;
  87. import org.elasticsearch.action.search.ClearScrollRequest;
  88. import org.elasticsearch.action.search.ClearScrollRequestBuilder;
  89. import org.elasticsearch.action.search.ClearScrollResponse;
  90. import org.elasticsearch.action.search.MultiSearchRequest;
  91. import org.elasticsearch.action.search.MultiSearchRequestBuilder;
  92. import org.elasticsearch.action.search.MultiSearchResponse;
  93. import org.elasticsearch.action.search.SearchRequest;
  94. import org.elasticsearch.action.search.SearchRequestBuilder;
  95. import org.elasticsearch.action.search.SearchResponse;
  96. import org.elasticsearch.action.search.SearchScrollRequest;
  97. import org.elasticsearch.action.search.SearchScrollRequestBuilder;
  98. import org.elasticsearch.action.suggest.SuggestRequest;
  99. import org.elasticsearch.action.suggest.SuggestRequestBuilder;
  100. import org.elasticsearch.action.suggest.SuggestResponse;
  101. import org.elasticsearch.action.termvector.MultiTermVectorsRequest;
  102. import org.elasticsearch.action.termvector.MultiTermVectorsRequestBuilder;
  103. import org.elasticsearch.action.termvector.MultiTermVectorsResponse;
  104. import org.elasticsearch.action.termvector.TermVectorRequest;
  105. import org.elasticsearch.action.termvector.TermVectorRequestBuilder;
  106. import org.elasticsearch.action.termvector.TermVectorResponse;
  107. import org.elasticsearch.action.update.UpdateRequest;
  108. import org.elasticsearch.action.update.UpdateRequestBuilder;
  109. import org.elasticsearch.action.update.UpdateResponse;
  110. import org.elasticsearch.client.AdminClient;
  111. import org.elasticsearch.client.Client;
  112. import org.elasticsearch.client.transport.TransportClient;
  113. import org.elasticsearch.cluster.metadata.MappingMetaData;
  114. import org.elasticsearch.common.collect.ImmutableOpenMap;
  115. import org.elasticsearch.common.settings.ImmutableSettings;
  116. import org.elasticsearch.common.settings.ImmutableSettings.Builder;
  117. import org.elasticsearch.common.settings.Settings;
  118. import org.elasticsearch.common.transport.InetSocketTransportAddress;
  119. import org.elasticsearch.common.transport.TransportAddress;
  120. import org.elasticsearch.common.unit.TimeValue;
  121. import org.elasticsearch.index.query.BoolFilterBuilder;
  122. import org.elasticsearch.index.query.FilterBuilders;
  123. import org.elasticsearch.index.query.QueryBuilder;
  124. import org.elasticsearch.index.query.QueryBuilders;
  125. import org.elasticsearch.indices.IndexAlreadyExistsException;
  126. import org.elasticsearch.indices.IndexMissingException;
  127. import org.elasticsearch.search.SearchHit;
  128. import org.elasticsearch.search.aggregations.AggregationBuilders;
  129. import org.elasticsearch.search.aggregations.bucket.filter.FilterAggregationBuilder;
  130. import org.elasticsearch.search.aggregations.bucket.terms.TermsBuilder;
  131. import org.elasticsearch.search.sort.FieldSortBuilder;
  132. import org.elasticsearch.search.sort.SortBuilders;
  133. import org.elasticsearch.search.sort.SortOrder;
  134. import org.elasticsearch.threadpool.ThreadPool;
  135. import org.seasar.framework.container.annotation.tiger.DestroyMethod;
  136. import org.seasar.framework.container.annotation.tiger.InitMethod;
  137. import org.slf4j.Logger;
  138. import org.slf4j.LoggerFactory;
  139. import com.google.common.io.BaseEncoding;
  140. public class FessEsClient implements Client {
  141. private static final Logger logger = LoggerFactory.getLogger(FessEsClient.class);
  142. protected ElasticsearchClusterRunner runner;
  143. protected List<TransportAddress> transportAddressList = new ArrayList<>();
  144. protected Client client;
  145. protected String clusterName = "elasticsearch";
  146. protected Map<String, String> settings;
  147. protected String indexConfigPath = "fess_indices";
  148. protected List<String> indexConfigList = new ArrayList<>();
  149. public void addIndexConfig(String path) {
  150. indexConfigList.add(path);
  151. }
  152. public void setSettings(Map<String, String> settings) {
  153. this.settings = settings;
  154. }
  155. public String getClusterName() {
  156. return clusterName;
  157. }
  158. public void setClusterName(String clusterName) {
  159. this.clusterName = clusterName;
  160. }
  161. public void setRunner(ElasticsearchClusterRunner runner) {
  162. this.runner = runner;
  163. }
  164. public void addTransportAddress(String host, int port) {
  165. transportAddressList.add(new InetSocketTransportAddress(host, port));
  166. }
  167. @InitMethod
  168. public void open() {
  169. final String transportAddressesValue = System.getProperty(Constants.FESS_ES_TRANSPORT_ADDRESSES);
  170. if (StringUtil.isNotBlank(transportAddressesValue)) {
  171. for (final String transportAddressValue : transportAddressesValue.split(",")) {
  172. final String[] addressPair = transportAddressValue.trim().split(":");
  173. if (addressPair.length < 3) {
  174. final String host = addressPair[0];
  175. int port = 9300;
  176. if (addressPair.length == 2) {
  177. port = Integer.parseInt(addressPair[1]);
  178. }
  179. addTransportAddress(host, port);
  180. } else {
  181. logger.warn("Invalid address format: " + transportAddressValue);
  182. }
  183. }
  184. }
  185. if (transportAddressList.isEmpty()) {
  186. if (runner == null) {
  187. runner = new ElasticsearchClusterRunner();
  188. final Configs config = newConfigs().clusterName(clusterName).numOfNode(1);
  189. final String esDir = System.getProperty("fess.es.dir");
  190. if (esDir != null) {
  191. config.basePath(esDir);
  192. }
  193. runner.onBuild((number, settingsBuilder) -> {
  194. if (settings != null) {
  195. settingsBuilder.put(settings);
  196. }
  197. });
  198. runner.build(config);
  199. }
  200. client = runner.client();
  201. addTransportAddress("localhost", runner.node().settings().getAsInt("transport.tcp.port", 9300));
  202. } else {
  203. final Builder settingsBuilder = ImmutableSettings.settingsBuilder();
  204. settingsBuilder.put("cluster.name", clusterName);
  205. final Settings settings = settingsBuilder.build();
  206. final TransportClient transportClient = new TransportClient(settings);
  207. for (final TransportAddress address : transportAddressList) {
  208. transportClient.addTransportAddress(address);
  209. }
  210. client = transportClient;
  211. }
  212. if (StringUtil.isBlank(transportAddressesValue)) {
  213. final StringBuilder buf = new StringBuilder();
  214. for (final TransportAddress transportAddress : transportAddressList) {
  215. if (transportAddress instanceof InetSocketTransportAddress) {
  216. if (buf.length() > 0) {
  217. buf.append(',');
  218. }
  219. final InetSocketTransportAddress inetTransportAddress = (InetSocketTransportAddress) transportAddress;
  220. buf.append(inetTransportAddress.address().getHostName());
  221. buf.append(':');
  222. buf.append(inetTransportAddress.address().getPort());
  223. }
  224. }
  225. if (buf.length() > 0) {
  226. System.setProperty(Constants.FESS_ES_TRANSPORT_ADDRESSES, buf.toString());
  227. }
  228. }
  229. waitForYellowStatus();
  230. indexConfigList.forEach(configName -> {
  231. final String[] values = configName.split("/");
  232. if (values.length == 2) {
  233. final String configIndex = values[0];
  234. final String configType = values[1];
  235. boolean exists = false;
  236. try {
  237. client.prepareExists(configIndex).execute().actionGet();
  238. exists = true;
  239. } catch (final IndexMissingException e) {
  240. // ignore
  241. }
  242. if (!exists) {
  243. try {
  244. String source = null;
  245. final String indexConfigFile = indexConfigPath + "/" + configIndex + ".json";
  246. try {
  247. source = FileUtil.readUTF8(indexConfigFile);
  248. } catch (final Exception e) {
  249. logger.warn(indexConfigFile + " is not found.", e);
  250. }
  251. final CreateIndexResponse indexResponse =
  252. client.admin().indices().prepareCreate(configIndex).setSource(source).execute().actionGet();
  253. if (indexResponse.isAcknowledged()) {
  254. logger.info("Created " + configIndex + " index.");
  255. } else if (logger.isDebugEnabled()) {
  256. logger.debug("Failed to create " + configIndex + " index.");
  257. }
  258. } catch (final IndexAlreadyExistsException e) {
  259. // ignore
  260. }
  261. }
  262. final GetMappingsResponse getMappingsResponse =
  263. client.admin().indices().prepareGetMappings(configIndex).setTypes(configType).execute().actionGet();
  264. final ImmutableOpenMap<String, MappingMetaData> indexMappings = getMappingsResponse.mappings().get(configIndex);
  265. if (indexMappings == null || !indexMappings.containsKey(configType)) {
  266. String source = null;
  267. final String mappingFile = indexConfigPath + "/" + configIndex + "/" + configType + ".json";
  268. try {
  269. source = FileUtil.readUTF8(mappingFile);
  270. } catch (final Exception e) {
  271. logger.warn(mappingFile + " is not found.", e);
  272. }
  273. final PutMappingResponse putMappingResponse =
  274. client.admin().indices().preparePutMapping(configIndex).setType(configType).setSource(source).execute().actionGet();
  275. if (putMappingResponse.isAcknowledged()) {
  276. logger.info("Created " + configIndex + "/" + configType + " mapping.");
  277. } else {
  278. logger.warn("Failed to create " + configIndex + "/" + configType + " mapping.");
  279. }
  280. } else if (logger.isDebugEnabled()) {
  281. logger.debug(configIndex + "/" + configType + " mapping exists.");
  282. }
  283. } else {
  284. logger.warn("Invalid index config name: " + configName);
  285. }
  286. }) ;
  287. }
  288. private void waitForYellowStatus() {
  289. final ClusterHealthResponse response = client.admin().cluster().prepareHealth().setWaitForYellowStatus().execute().actionGet();
  290. if (logger.isDebugEnabled()) {
  291. logger.debug("Elasticsearch Cluster Status: {0}", response.getStatus());
  292. }
  293. }
  294. @DestroyMethod
  295. public void close() {
  296. try {
  297. client.close();
  298. } catch (final ElasticsearchException e) {
  299. logger.warn("Failed to close Client: " + client, e);
  300. }
  301. }
  302. public void deleteByQuery(String index, String type, QueryBuilder queryBuilder) {
  303. try {
  304. // TODO replace with deleting bulk ids with scroll/scan
  305. client.prepareDeleteByQuery(index).setQuery(queryBuilder).setTypes(type).execute().actionGet().forEach(res -> {
  306. final ShardOperationFailedException[] failures = res.getFailures();
  307. if (failures.length > 0) {
  308. final StringBuilder buf = new StringBuilder(200);
  309. buf.append("Failed to delete documents in some shards.");
  310. for (final ShardOperationFailedException failure : failures) {
  311. buf.append('\n').append(failure.toString());
  312. }
  313. throw new FessEsClientException(buf.toString());
  314. }
  315. });
  316. } catch (final ElasticsearchException e) {
  317. throw new FessEsClientException("Failed to delete documents.", e);
  318. }
  319. }
  320. public <T> T search(String index, String type, SearchCondition condition, SearchResult<T> searchResult) {
  321. final long startTime = System.currentTimeMillis();
  322. SearchResponse searchResponse = null;
  323. final SearchRequestBuilder searchRequestBuilder = client.prepareSearch(index).setTypes(type);
  324. if (condition.build(searchRequestBuilder)) {
  325. if (ComponentUtil.getQueryHelper().getTimeAllowed() >= 0) {
  326. searchRequestBuilder.setTimeout(TimeValue.timeValueMillis(ComponentUtil.getQueryHelper().getTimeAllowed()));
  327. }
  328. for (final Map.Entry<String, String[]> entry : ComponentUtil.getQueryHelper().getQueryParamMap().entrySet()) {
  329. searchRequestBuilder.putHeader(entry.getKey(), entry.getValue());
  330. }
  331. final Set<Entry<String, String[]>> paramSet = ComponentUtil.getQueryHelper().getRequestParameterSet();
  332. if (!paramSet.isEmpty()) {
  333. for (final Map.Entry<String, String[]> entry : paramSet) {
  334. searchRequestBuilder.putHeader(entry.getKey(), entry.getValue());
  335. }
  336. }
  337. searchResponse = searchRequestBuilder.execute().actionGet();
  338. }
  339. final long execTime = System.currentTimeMillis() - startTime;
  340. return searchResult.build(searchRequestBuilder, execTime, Optional.ofNullable(searchResponse));
  341. }
  342. public Optional<Map<String, Object>> getDocument(String index, String type, final SearchCondition condition) {
  343. return getDocument(index, type, condition, (response, hit) -> {
  344. return hit.getSource();
  345. });
  346. }
  347. public <T> Optional<T> getDocument(String index, String type, final SearchCondition condition, EntityCreator<T> creator) {
  348. return search(index, type, condition, (queryBuilder, execTime, searchResponse) -> {
  349. return searchResponse.map(response -> {
  350. final SearchHit[] hits = response.getHits().hits();
  351. if (hits.length > 0) {
  352. return creator.build(response, hits[0]);
  353. }
  354. return null;
  355. });
  356. });
  357. }
  358. public List<Map<String, Object>> getDocumentList(String index, String type, final SearchCondition condition) {
  359. return getDocumentList(index, type, condition, (response, hit) -> {
  360. return hit.getSource();
  361. });
  362. }
  363. public <T> List<T> getDocumentList(String index, String type, final SearchCondition condition, EntityCreator<T> creator) {
  364. return search(index, type, condition, (searchRequestBuilder, execTime, searchResponse) -> {
  365. final List<T> list = new ArrayList<>();
  366. searchResponse.ifPresent(response -> {
  367. response.getHits().forEach(hit -> {
  368. list.add(creator.build(response, hit));
  369. });
  370. });
  371. return list;
  372. });
  373. }
  374. public boolean update(String index, String type, String id, String field, Object value) {
  375. try {
  376. return client.prepareUpdate(index, type, id).setDoc(field, value).execute().actionGet().isCreated();
  377. } catch (final ElasticsearchException e) {
  378. throw new FessEsClientException("Failed to set " + value + " to " + field + " for doc " + id, e);
  379. }
  380. }
  381. public void refresh(String index) {
  382. client.admin().indices().prepareRefresh(index).execute(new ActionListener<RefreshResponse>() {
  383. @Override
  384. public void onResponse(RefreshResponse response) {
  385. if (logger.isDebugEnabled()) {
  386. logger.debug("Refreshed " + index + ".");
  387. }
  388. }
  389. @Override
  390. public void onFailure(Throwable e) {
  391. logger.error("Failed to refresh " + index + ".", e);
  392. }
  393. });
  394. }
  395. public void flush(String index) {
  396. client.admin().indices().prepareFlush(index).execute(new ActionListener<FlushResponse>() {
  397. @Override
  398. public void onResponse(FlushResponse response) {
  399. if (logger.isDebugEnabled()) {
  400. logger.debug("Flushed " + index + ".");
  401. }
  402. }
  403. @Override
  404. public void onFailure(Throwable e) {
  405. logger.error("Failed to flush " + index + ".", e);
  406. }
  407. });
  408. }
  409. public void optimize(String index) {
  410. client.admin().indices().prepareOptimize(index).execute(new ActionListener<OptimizeResponse>() {
  411. @Override
  412. public void onResponse(OptimizeResponse response) {
  413. if (logger.isDebugEnabled()) {
  414. logger.debug("Optimzed " + index + ".");
  415. }
  416. }
  417. @Override
  418. public void onFailure(Throwable e) {
  419. logger.error("Failed to optimze " + index + ".", e);
  420. }
  421. });
  422. }
  423. public PingResponse ping() {
  424. try {
  425. final ClusterHealthResponse response = client.admin().cluster().prepareHealth().execute().actionGet();
  426. return new PingResponse(response);
  427. } catch (final ElasticsearchException e) {
  428. throw new FessEsClientException("Failed to process a ping request.", e);
  429. }
  430. }
  431. public void addAll(String index, String type, List<Map<String, Object>> docList) {
  432. final BulkRequestBuilder bulkRequestBuilder = client.prepareBulk();
  433. for (final Map<String, Object> doc : docList) {
  434. bulkRequestBuilder.add(client.prepareIndex(index, type).setSource(doc));
  435. }
  436. final BulkResponse response = bulkRequestBuilder.execute().actionGet();
  437. final String failureMessage = response.buildFailureMessage();
  438. if (StringUtil.isNotBlank(failureMessage)) {
  439. throw new FessEsClientException(failureMessage);
  440. }
  441. }
  442. public static class SearchConditionBuilder {
  443. private final SearchRequestBuilder searchRequestBuilder;
  444. private String query;
  445. private boolean administrativeAccess = false;
  446. private String[] responseFields;
  447. private int offset = Constants.DEFAULT_START_COUNT;
  448. private int size = Constants.DEFAULT_PAGE_SIZE;
  449. private GeoInfo geoInfo;
  450. private FacetInfo facetInfo;
  451. public static SearchConditionBuilder builder(SearchRequestBuilder searchRequestBuilder) {
  452. return new SearchConditionBuilder(searchRequestBuilder);
  453. }
  454. SearchConditionBuilder(SearchRequestBuilder searchRequestBuilder) {
  455. this.searchRequestBuilder = searchRequestBuilder;
  456. }
  457. public SearchConditionBuilder query(String query) {
  458. this.query = query;
  459. return this;
  460. }
  461. public SearchConditionBuilder administrativeAccess() {
  462. this.administrativeAccess = true;
  463. return this;
  464. }
  465. public SearchConditionBuilder responseFields(String[] responseFields) {
  466. this.responseFields = responseFields;
  467. return this;
  468. }
  469. public SearchConditionBuilder offset(int offset) {
  470. this.offset = offset;
  471. return this;
  472. }
  473. public SearchConditionBuilder size(int size) {
  474. this.size = size;
  475. return this;
  476. }
  477. public SearchConditionBuilder geoInfo(GeoInfo geoInfo) {
  478. this.geoInfo = geoInfo;
  479. return this;
  480. }
  481. public SearchConditionBuilder facetInfo(FacetInfo facetInfo) {
  482. this.facetInfo = facetInfo;
  483. return this;
  484. }
  485. public boolean build() {
  486. if (offset > ComponentUtil.getQueryHelper().getMaxSearchResultOffset()) {
  487. throw new ResultOffsetExceededException("The number of result size is exceeded.");
  488. }
  489. final SearchQuery searchQuery = ComponentUtil.getQueryHelper().build(query, administrativeAccess);
  490. final String q = searchQuery.getQuery();
  491. if (StringUtil.isBlank(q)) {
  492. return false;
  493. }
  494. searchRequestBuilder.setFrom(offset).setSize(size);
  495. if (responseFields != null) {
  496. searchRequestBuilder.addFields(responseFields);
  497. }
  498. // sort
  499. final SortField[] sortFields = searchQuery.getSortFields();
  500. if (sortFields.length != 0) {
  501. for (final SortField sortField : sortFields) {
  502. final FieldSortBuilder fieldSort = SortBuilders.fieldSort(sortField.getField());
  503. if (Constants.DESC.equals(sortField.getOrder())) {
  504. fieldSort.order(SortOrder.DESC);
  505. } else {
  506. fieldSort.order(SortOrder.ASC);
  507. }
  508. searchRequestBuilder.addSort(fieldSort);
  509. }
  510. } else if (ComponentUtil.getQueryHelper().hasDefaultSortFields()) {
  511. for (final SortField sortField : ComponentUtil.getQueryHelper().getDefaultSortFields()) {
  512. final FieldSortBuilder fieldSort = SortBuilders.fieldSort(sortField.getField());
  513. if (Constants.DESC.equals(sortField.getOrder())) {
  514. fieldSort.order(SortOrder.DESC);
  515. } else {
  516. fieldSort.order(SortOrder.ASC);
  517. }
  518. searchRequestBuilder.addSort(fieldSort);
  519. }
  520. }
  521. // highlighting
  522. if (ComponentUtil.getQueryHelper().getHighlightingFields() != null
  523. && ComponentUtil.getQueryHelper().getHighlightingFields().length != 0) {
  524. for (final String hf : ComponentUtil.getQueryHelper().getHighlightingFields()) {
  525. searchRequestBuilder.addHighlightedField(hf, ComponentUtil.getQueryHelper().getHighlightSnippetSize());
  526. }
  527. }
  528. // facets
  529. if (facetInfo != null) {
  530. if (facetInfo.field != null) {
  531. for (final String f : facetInfo.field) {
  532. if (ComponentUtil.getQueryHelper().isFacetField(f)) {
  533. final String encodedField = BaseEncoding.base64().encode(f.getBytes(Charsets.UTF_8));
  534. final TermsBuilder termsBuilder =
  535. AggregationBuilders.terms(Constants.FACET_FIELD_PREFIX + encodedField).field(f);
  536. // TODO order
  537. if (facetInfo.limit != null) {
  538. // TODO
  539. termsBuilder.size(Integer.parseInt(facetInfo.limit));
  540. }
  541. searchRequestBuilder.addAggregation(termsBuilder);
  542. } else {
  543. throw new FessSolrQueryException("Invalid facet field: " + f);
  544. }
  545. }
  546. }
  547. if (facetInfo.query != null) {
  548. for (final String fq : facetInfo.query) {
  549. final String facetQuery = ComponentUtil.getQueryHelper().buildFacetQuery(fq);
  550. if (StringUtil.isNotBlank(facetQuery)) {
  551. final String encodedFacetQuery = BaseEncoding.base64().encode(facetQuery.getBytes(Charsets.UTF_8));
  552. final FilterAggregationBuilder filterBuilder =
  553. AggregationBuilders.filter(Constants.FACET_QUERY_PREFIX + encodedFacetQuery).filter(
  554. FilterBuilders.queryFilter(QueryBuilders.queryStringQuery(facetQuery)));
  555. // TODO order
  556. if (facetInfo.limit != null) {
  557. // TODO
  558. // filterBuilder.size(Integer.parseInt(facetInfo .limit));
  559. }
  560. searchRequestBuilder.addAggregation(filterBuilder);
  561. } else {
  562. throw new FessSolrQueryException("Invalid facet query: " + facetQuery);
  563. }
  564. }
  565. }
  566. }
  567. BoolFilterBuilder boolFilterBuilder = null;
  568. // query
  569. QueryBuilder queryBuilder = QueryBuilders.queryStringQuery(q);
  570. // filter query
  571. if (searchQuery.hasFilterQueries()) {
  572. if (boolFilterBuilder == null) {
  573. boolFilterBuilder = FilterBuilders.boolFilter();
  574. }
  575. for (final String filterQuery : searchQuery.getFilterQueries()) {
  576. boolFilterBuilder.must(FilterBuilders.queryFilter(QueryBuilders.queryStringQuery(filterQuery)));
  577. }
  578. }
  579. // geo
  580. if (geoInfo != null && geoInfo.isAvailable()) {
  581. if (boolFilterBuilder == null) {
  582. boolFilterBuilder = FilterBuilders.boolFilter();
  583. }
  584. boolFilterBuilder.must(geoInfo.toFilterBuilder());
  585. }
  586. if (boolFilterBuilder != null) {
  587. queryBuilder = QueryBuilders.filteredQuery(queryBuilder, boolFilterBuilder);
  588. }
  589. searchRequestBuilder.setQuery(queryBuilder);
  590. return true;
  591. }
  592. }
  593. public boolean store(String index, String type, Object obj) {
  594. final Map<String, Object> source = BeanUtil.copyBeanToNewMap(obj);
  595. final String id = (String) source.remove("id");
  596. final Long version = (Long) source.remove("version");
  597. IndexResponse response;
  598. try {
  599. if (id == null) {
  600. // create
  601. response =
  602. client.prepareIndex(index, type).setSource(source).setRefresh(true).setOpType(OpType.CREATE).execute().actionGet();
  603. } else {
  604. // update
  605. response =
  606. client.prepareIndex(index, type, id).setSource(source).setRefresh(true).setOpType(OpType.INDEX).setVersion(version)
  607. .execute().actionGet();
  608. }
  609. return response.isCreated();
  610. } catch (final ElasticsearchException e) {
  611. throw new FessEsClientException("Failed to store: " + obj, e);
  612. }
  613. }
  614. public boolean delete(String index, String type, String id, long version) {
  615. try {
  616. final DeleteResponse response =
  617. client.prepareDelete(index, type, id).setVersion(version).setRefresh(true).execute().actionGet();
  618. return response.isFound();
  619. } catch (final ElasticsearchException e) {
  620. throw new FessEsClientException("Failed to delete: " + index + "/" + type + "/" + id + "/" + version, e);
  621. }
  622. }
  623. public void setIndexConfigPath(String indexConfigPath) {
  624. this.indexConfigPath = indexConfigPath;
  625. }
  626. public interface SearchCondition {
  627. boolean build(SearchRequestBuilder searchRequestBuilder);
  628. }
  629. public interface SearchResult<T> {
  630. T build(SearchRequestBuilder searchRequestBuilder, long execTime, Optional<SearchResponse> searchResponse);
  631. }
  632. public interface EntityCreator<T> {
  633. T build(SearchResponse response, SearchHit hit);
  634. }
  635. //
  636. // Elasticsearch Client
  637. //
  638. public <Request extends ActionRequest, Response extends ActionResponse, RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder, Client>> ActionFuture<Response> execute(
  639. Action<Request, Response, RequestBuilder, Client> action, Request request) {
  640. return client.execute(action, request);
  641. }
  642. public <Request extends ActionRequest, Response extends ActionResponse, RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder, Client>> void execute(
  643. Action<Request, Response, RequestBuilder, Client> action, Request request, ActionListener<Response> listener) {
  644. client.execute(action, request, listener);
  645. }
  646. public <Request extends ActionRequest, Response extends ActionResponse, RequestBuilder extends ActionRequestBuilder<Request, Response, RequestBuilder, Client>> RequestBuilder prepareExecute(
  647. Action<Request, Response, RequestBuilder, Client> action) {
  648. return client.prepareExecute(action);
  649. }
  650. public ThreadPool threadPool() {
  651. return client.threadPool();
  652. }
  653. public AdminClient admin() {
  654. return client.admin();
  655. }
  656. public ActionFuture<IndexResponse> index(IndexRequest request) {
  657. return client.index(request);
  658. }
  659. public void index(IndexRequest request, ActionListener<IndexResponse> listener) {
  660. client.index(request, listener);
  661. }
  662. public IndexRequestBuilder prepareIndex() {
  663. return client.prepareIndex();
  664. }
  665. public ActionFuture<UpdateResponse> update(UpdateRequest request) {
  666. return client.update(request);
  667. }
  668. public void update(UpdateRequest request, ActionListener<UpdateResponse> listener) {
  669. client.update(request, listener);
  670. }
  671. public UpdateRequestBuilder prepareUpdate() {
  672. return client.prepareUpdate();
  673. }
  674. public UpdateRequestBuilder prepareUpdate(String index, String type, String id) {
  675. return client.prepareUpdate(index, type, id);
  676. }
  677. public IndexRequestBuilder prepareIndex(String index, String type) {
  678. return client.prepareIndex(index, type);
  679. }
  680. public IndexRequestBuilder prepareIndex(String index, String type, String id) {
  681. return client.prepareIndex(index, type, id);
  682. }
  683. public ActionFuture<DeleteResponse> delete(DeleteRequest request) {
  684. return client.delete(request);
  685. }
  686. public void delete(DeleteRequest request, ActionListener<DeleteResponse> listener) {
  687. client.delete(request, listener);
  688. }
  689. public DeleteRequestBuilder prepareDelete() {
  690. return client.prepareDelete();
  691. }
  692. public DeleteRequestBuilder prepareDelete(String index, String type, String id) {
  693. return client.prepareDelete(index, type, id);
  694. }
  695. public ActionFuture<BulkResponse> bulk(BulkRequest request) {
  696. return client.bulk(request);
  697. }
  698. public void bulk(BulkRequest request, ActionListener<BulkResponse> listener) {
  699. client.bulk(request, listener);
  700. }
  701. public BulkRequestBuilder prepareBulk() {
  702. return client.prepareBulk();
  703. }
  704. public ActionFuture<DeleteByQueryResponse> deleteByQuery(DeleteByQueryRequest request) {
  705. return client.deleteByQuery(request);
  706. }
  707. public void deleteByQuery(DeleteByQueryRequest request, ActionListener<DeleteByQueryResponse> listener) {
  708. client.deleteByQuery(request, listener);
  709. }
  710. public DeleteByQueryRequestBuilder prepareDeleteByQuery(String... indices) {
  711. return client.prepareDeleteByQuery(indices);
  712. }
  713. public ActionFuture<GetResponse> get(GetRequest request) {
  714. return client.get(request);
  715. }
  716. public void get(GetRequest request, ActionListener<GetResponse> listener) {
  717. client.get(request, listener);
  718. }
  719. public GetRequestBuilder prepareGet() {
  720. return client.prepareGet();
  721. }
  722. public GetRequestBuilder prepareGet(String index, String type, String id) {
  723. return client.prepareGet(index, type, id);
  724. }
  725. public PutIndexedScriptRequestBuilder preparePutIndexedScript() {
  726. return client.preparePutIndexedScript();
  727. }
  728. public PutIndexedScriptRequestBuilder preparePutIndexedScript(String scriptLang, String id, String source) {
  729. return client.preparePutIndexedScript(scriptLang, id, source);
  730. }
  731. public void deleteIndexedScript(DeleteIndexedScriptRequest request, ActionListener<DeleteIndexedScriptResponse> listener) {
  732. client.deleteIndexedScript(request, listener);
  733. }
  734. public ActionFuture<DeleteIndexedScriptResponse> deleteIndexedScript(DeleteIndexedScriptRequest request) {
  735. return client.deleteIndexedScript(request);
  736. }
  737. public DeleteIndexedScriptRequestBuilder prepareDeleteIndexedScript() {
  738. return client.prepareDeleteIndexedScript();
  739. }
  740. public DeleteIndexedScriptRequestBuilder prepareDeleteIndexedScript(String scriptLang, String id) {
  741. return client.prepareDeleteIndexedScript(scriptLang, id);
  742. }
  743. public void putIndexedScript(PutIndexedScriptRequest request, ActionListener<PutIndexedScriptResponse> listener) {
  744. client.putIndexedScript(request, listener);
  745. }
  746. public ActionFuture<PutIndexedScriptResponse> putIndexedScript(PutIndexedScriptRequest request) {
  747. return client.putIndexedScript(request);
  748. }
  749. public GetIndexedScriptRequestBuilder prepareGetIndexedScript() {
  750. return client.prepareGetIndexedScript();
  751. }
  752. public GetIndexedScriptRequestBuilder prepareGetIndexedScript(String scriptLang, String id) {
  753. return client.prepareGetIndexedScript(scriptLang, id);
  754. }
  755. public void getIndexedScript(GetIndexedScriptRequest request, ActionListener<GetIndexedScriptResponse> listener) {
  756. client.getIndexedScript(request, listener);
  757. }
  758. public ActionFuture<GetIndexedScriptResponse> getIndexedScript(GetIndexedScriptRequest request) {
  759. return client.getIndexedScript(request);
  760. }
  761. public ActionFuture<MultiGetResponse> multiGet(MultiGetRequest request) {
  762. return client.multiGet(request);
  763. }
  764. public void multiGet(MultiGetRequest request, ActionListener<MultiGetResponse> listener) {
  765. client.multiGet(request, listener);
  766. }
  767. public MultiGetRequestBuilder prepareMultiGet() {
  768. return client.prepareMultiGet();
  769. }
  770. public ActionFuture<CountResponse> count(CountRequest request) {
  771. return client.count(request);
  772. }
  773. public void count(CountRequest request, ActionListener<CountResponse> listener) {
  774. client.count(request, listener);
  775. }
  776. public CountRequestBuilder prepareCount(String... indices) {
  777. return client.prepareCount(indices);
  778. }
  779. public ActionFuture<ExistsResponse> exists(ExistsRequest request) {
  780. return client.exists(request);
  781. }
  782. public void exists(ExistsRequest request, ActionListener<ExistsResponse> listener) {
  783. client.exists(request, listener);
  784. }
  785. public ExistsRequestBuilder prepareExists(String... indices) {
  786. return client.prepareExists(indices);
  787. }
  788. public ActionFuture<SuggestResponse> suggest(SuggestRequest request) {
  789. return client.suggest(request);
  790. }
  791. public void suggest(SuggestRequest request, ActionListener<SuggestResponse> listener) {
  792. client.suggest(request, listener);
  793. }
  794. public SuggestRequestBuilder prepareSuggest(String... indices) {
  795. return client.prepareSuggest(indices);
  796. }
  797. public ActionFuture<SearchResponse> search(SearchRequest request) {
  798. return client.search(request);
  799. }
  800. public void search(SearchRequest request, ActionListener<SearchResponse> listener) {
  801. client.search(request, listener);
  802. }
  803. public SearchRequestBuilder prepareSearch(String... indices) {
  804. return client.prepareSearch(indices);
  805. }
  806. public ActionFuture<SearchResponse> searchScroll(SearchScrollRequest request) {
  807. return client.searchScroll(request);
  808. }
  809. public void searchScroll(SearchScrollRequest request, ActionListener<SearchResponse> listener) {
  810. client.searchScroll(request, listener);
  811. }
  812. public SearchScrollRequestBuilder prepareSearchScroll(String scrollId) {
  813. return client.prepareSearchScroll(scrollId);
  814. }
  815. public ActionFuture<MultiSearchResponse> multiSearch(MultiSearchRequest request) {
  816. return client.multiSearch(request);
  817. }
  818. public void multiSearch(MultiSearchRequest request, ActionListener<MultiSearchResponse> listener) {
  819. client.multiSearch(request, listener);
  820. }
  821. public MultiSearchRequestBuilder prepareMultiSearch() {
  822. return client.prepareMultiSearch();
  823. }
  824. public ActionFuture<SearchResponse> moreLikeThis(MoreLikeThisRequest request) {
  825. return client.moreLikeThis(request);
  826. }
  827. public void moreLikeThis(MoreLikeThisRequest request, ActionListener<SearchResponse> listener) {
  828. client.moreLikeThis(request, listener);
  829. }
  830. public MoreLikeThisRequestBuilder prepareMoreLikeThis(String index, String type, String id) {
  831. return client.prepareMoreLikeThis(index, type, id);
  832. }
  833. public ActionFuture<TermVectorResponse> termVector(TermVectorRequest request) {
  834. return client.termVector(request);
  835. }
  836. public void termVector(TermVectorRequest request, ActionListener<TermVectorResponse> listener) {
  837. client.termVector(request, listener);
  838. }
  839. public TermVectorRequestBuilder prepareTermVector() {
  840. return client.prepareTermVector();
  841. }
  842. public TermVectorRequestBuilder prepareTermVector(String index, String type, String id) {
  843. return client.prepareTermVector(index, type, id);
  844. }
  845. public ActionFuture<MultiTermVectorsResponse> multiTermVectors(MultiTermVectorsRequest request) {
  846. return client.multiTermVectors(request);
  847. }
  848. public void multiTermVectors(MultiTermVectorsRequest request, ActionListener<MultiTermVectorsResponse> listener) {
  849. client.multiTermVectors(request, listener);
  850. }
  851. public MultiTermVectorsRequestBuilder prepareMultiTermVectors() {
  852. return client.prepareMultiTermVectors();
  853. }
  854. public ActionFuture<PercolateResponse> percolate(PercolateRequest request) {
  855. return client.percolate(request);
  856. }
  857. public void percolate(PercolateRequest request, ActionListener<PercolateResponse> listener) {
  858. client.percolate(request, listener);
  859. }
  860. public PercolateRequestBuilder preparePercolate() {
  861. return client.preparePercolate();
  862. }
  863. public ActionFuture<MultiPercolateResponse> multiPercolate(MultiPercolateRequest request) {
  864. return client.multiPercolate(request);
  865. }
  866. public void multiPercolate(MultiPercolateRequest request, ActionListener<MultiPercolateResponse> listener) {
  867. client.multiPercolate(request, listener);
  868. }
  869. public MultiPercolateRequestBuilder prepareMultiPercolate() {
  870. return client.prepareMultiPercolate();
  871. }
  872. public ExplainRequestBuilder prepareExplain(String index, String type, String id) {
  873. return client.prepareExplain(index, type, id);
  874. }
  875. public ActionFuture<ExplainResponse> explain(ExplainRequest request) {
  876. return client.explain(request);
  877. }
  878. public void explain(ExplainRequest request, ActionListener<ExplainResponse> listener) {
  879. client.explain(request, listener);
  880. }
  881. public ClearScrollRequestBuilder prepareClearScroll() {
  882. return client.prepareClearScroll();
  883. }
  884. public ActionFuture<ClearScrollResponse> clearScroll(ClearScrollRequest request) {
  885. return client.clearScroll(request);
  886. }
  887. public void clearScroll(ClearScrollRequest request, ActionListener<ClearScrollResponse> listener) {
  888. client.clearScroll(request, listener);
  889. }
  890. public FieldStatsRequestBuilder prepareFieldStats() {
  891. return client.prepareFieldStats();
  892. }
  893. public ActionFuture<FieldStatsResponse> fieldStats(FieldStatsRequest request) {
  894. return client.fieldStats(request);
  895. }
  896. public void fieldStats(FieldStatsRequest request, ActionListener<FieldStatsResponse> listener) {
  897. client.fieldStats(request, listener);
  898. }
  899. public Settings settings() {
  900. return client.settings();
  901. }
  902. }