View Javadoc
1   /*
2    * Copyright 2012-2020 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.app.web.api.admin.searchlist;
17  
18  import static org.codelibs.fess.app.web.admin.searchlist.AdminSearchlistAction.getDoc;
19  import static org.codelibs.fess.app.web.admin.searchlist.AdminSearchlistAction.validateFields;
20  
21  import java.util.Map;
22  
23  import javax.annotation.Resource;
24  
25  import org.apache.logging.log4j.LogManager;
26  import org.apache.logging.log4j.Logger;
27  import org.codelibs.core.lang.StringUtil;
28  import org.codelibs.fess.Constants;
29  import org.codelibs.fess.app.web.CrudMode;
30  import org.codelibs.fess.app.web.api.ApiResult;
31  import org.codelibs.fess.app.web.api.ApiResult.ApiDeleteResponse;
32  import org.codelibs.fess.app.web.api.ApiResult.ApiDocResponse;
33  import org.codelibs.fess.app.web.api.ApiResult.ApiDocsResponse;
34  import org.codelibs.fess.app.web.api.ApiResult.ApiResponse;
35  import org.codelibs.fess.app.web.api.ApiResult.ApiUpdateResponse;
36  import org.codelibs.fess.app.web.api.ApiResult.Status;
37  import org.codelibs.fess.app.web.api.admin.FessApiAdminAction;
38  import org.codelibs.fess.entity.SearchRenderData;
39  import org.codelibs.fess.es.client.FessEsClient;
40  import org.codelibs.fess.exception.InvalidQueryException;
41  import org.codelibs.fess.exception.ResultOffsetExceededException;
42  import org.codelibs.fess.helper.SearchHelper;
43  import org.codelibs.fess.util.ComponentUtil;
44  import org.elasticsearch.index.query.QueryBuilder;
45  import org.elasticsearch.index.query.QueryBuilders;
46  import org.lastaflute.web.Execute;
47  import org.lastaflute.web.response.JsonResponse;
48  
49  /**
50   * @author shinsuke
51   */
52  public class ApiAdminSearchlistAction extends FessApiAdminAction {
53  
54      // ===================================================================================
55      // Constant
56      //
57      private static final Logger logger = LogManager.getLogger(ApiAdminSearchlistAction.class);
58  
59      // ===================================================================================
60      // Attribute
61      // =========
62      @Resource
63      protected SearchHelper searchHelper;
64  
65      @Resource
66      protected FessEsClient fessEsClient;
67  
68      // ===================================================================================
69      //                                                                      Search Execute
70      //                                                                      ==============
71  
72      // GET /api/admin/searchlist/docs
73      // POST /api/admin/searchlist/docs
74      @Execute
75      public JsonResponse<ApiResult> docs(final SearchBody body) {
76          validateApi(body, messages -> {});
77  
78          if (StringUtil.isBlank(body.q)) {
79              // query matches on all documents.
80              body.q = Constants.MATCHES_ALL_QUERY;
81          }
82  
83          final SearchRenderDatahRenderData.html#SearchRenderData">SearchRenderData renderData = new SearchRenderData();
84          body.initialize();
85          try {
86              searchHelper.search(body, renderData, getUserBean());
87              return asJson(new ApiDocsResponse().renderData(renderData).status(Status.OK).result());
88          } catch (final InvalidQueryException e) {
89              if (logger.isDebugEnabled()) {
90                  logger.debug(e.getMessage(), e);
91              }
92              throwValidationErrorApi(e.getMessageCode());
93          } catch (final ResultOffsetExceededException e) {
94              if (logger.isDebugEnabled()) {
95                  logger.debug(e.getMessage(), e);
96              }
97              throwValidationErrorApi(messages -> messages.addErrorsResultSizeExceeded(GLOBAL));
98          }
99  
100         throwValidationErrorApi(messages -> messages.addErrorsInvalidQueryUnknown(GLOBAL));
101         return null; // ignore
102     }
103 
104     // GET /api/admin/searchlist/doc/{doc_id}
105     @Execute
106     public JsonResponse<ApiResult> get$doc(final String id) {
107         return asJson(new ApiDocResponse().doc(fessEsClient.getDocument(fessConfig.getIndexDocumentUpdateIndex(), builder -> {
108             builder.setQuery(QueryBuilders.termQuery(fessConfig.getIndexFieldDocId(), id));
109             return true;
110         }).orElseGet(() -> {
111             throwValidationErrorApi(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, id));
112             return null;
113         })).status(Status.OK).result());
114     }
115 
116     // PUT /api/admin/searchlist/doc
117     @Execute
118     public JsonResponse<ApiResult> put$doc(final CreateBody body) {
119         validateApi(body, messages -> {});
120         if (body.doc == null) {
121             throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, "doc is required"));
122         }
123         validateFields(body, this::throwValidationErrorApi);
124         body.crudMode = CrudMode.CREATE;
125         final Map<String, Object> doc = getDoc(body).map(entity -> {
126             try {
127                 entity.putAll(fessConfig.convertToStorableDoc(body.doc));
128 
129                 final String newId = ComponentUtil.getCrawlingInfoHelper().generateId(entity);
130                 entity.put(fessConfig.getIndexFieldId(), newId);
131 
132                 final String index = fessConfig.getIndexDocumentUpdateIndex();
133                 fessEsClient.store(index, entity);
134                 saveInfo(messages -> messages.addSuccessCrudCreateCrudTable(GLOBAL));
135             } catch (final Exception e) {
136                 logger.error("Failed to add " + entity, e);
137                 throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, buildThrowableMessage(e)));
138             }
139             return entity;
140         }).orElseGet(() -> {
141             throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToCreateInstance(GLOBAL));
142             return null;
143         });
144         return asJson(new ApiUpdateResponse().id(doc.get(fessConfig.getIndexFieldDocId()).toString()).created(true).status(Status.OK)
145                 .result());
146     }
147 
148     // POST /api/admin/searchlist/doc
149     @Execute
150     public JsonResponse<ApiResult> post$doc(final EditBody body) {
151         validateApi(body, messages -> {});
152         if (body.doc == null) {
153             throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToCreateCrudTable(GLOBAL, "doc is required"));
154         }
155         validateFields(body, this::throwValidationErrorApi);
156         body.crudMode = CrudMode.EDIT;
157         final Map<String, Object> doc = getDoc(body).map(entity -> {
158             final String index = fessConfig.getIndexDocumentUpdateIndex();
159             try {
160                 entity.putAll(fessConfig.convertToStorableDoc(body.doc));
161 
162                 final String newId = ComponentUtil.getCrawlingInfoHelper().generateId(entity);
163                 final String oldId = (String) entity.get(fessConfig.getIndexFieldId());
164                 if (!newId.equals(oldId)) {
165                     entity.put(fessConfig.getIndexFieldId(), newId);
166                     entity.remove(fessConfig.getIndexFieldVersion());
167                     final Number seqNo = (Number) entity.remove(fessConfig.getIndexFieldSeqNo());
168                     final Number primaryTerm = (Number) entity.remove(fessConfig.getIndexFieldPrimaryTerm());
169                     if (seqNo != null && primaryTerm != null && oldId != null) {
170                         fessEsClient.delete(index, oldId, seqNo, primaryTerm);
171                     }
172                 }
173 
174                 fessEsClient.store(index, entity);
175                 saveInfo(messages -> messages.addSuccessCrudUpdateCrudTable(GLOBAL));
176             } catch (final Exception e) {
177                 logger.error("Failed to update " + entity, e);
178                 throwValidationErrorApi(messages -> messages.addErrorsCrudFailedToUpdateCrudTable(GLOBAL, buildThrowableMessage(e)));
179             }
180             return entity;
181         }).orElseGet(() -> {
182             throwValidationErrorApi(messages -> messages.addErrorsCrudCouldNotFindCrudTable(GLOBAL, body.doc.toString()));
183             return null;
184         });
185         return asJson(new ApiUpdateResponse().id(doc.get(fessConfig.getIndexFieldDocId()).toString()).created(false).status(Status.OK)
186                 .result());
187     }
188 
189     // DELETE /api/admin/searchlist/doc/{doc_id}
190     @Execute
191     public JsonResponse<ApiResult> delete$doc(final String id) {
192         try {
193             final QueryBuilder query = QueryBuilders.termQuery(fessConfig.getIndexFieldDocId(), id);
194             fessEsClient.deleteByQuery(fessConfig.getIndexDocumentUpdateIndex(), query);
195             saveInfo(messages -> messages.addSuccessDeleteDocFromIndex(GLOBAL));
196         } catch (final Exception e) {
197             throwValidationErrorApi(messages -> messages.addErrorsFailedToDeleteDocInAdmin(GLOBAL));
198         }
199         return asJson(new ApiResponse().status(Status.OK).result());
200     }
201 
202     // DELETE /api/admin/searchlist/query
203     @Execute
204     public JsonResponse<ApiResult> delete$query(final SearchBody body) {
205         validateApi(body, messages -> {});
206 
207         if (StringUtil.isBlank(body.q)) {
208             throwValidationErrorApi(messages -> messages.addErrorsInvalidQueryUnknown(GLOBAL));
209         }
210         try {
211             final long count = searchHelper.deleteByQuery(request, body);
212             return asJson(new ApiDeleteResponse().count(count).status(Status.OK).result());
213         } catch (final InvalidQueryException e) {
214             if (logger.isDebugEnabled()) {
215                 logger.debug(e.getMessage(), e);
216             }
217             throwValidationErrorApi(e.getMessageCode());
218         } catch (final ResultOffsetExceededException e) {
219             if (logger.isDebugEnabled()) {
220                 logger.debug(e.getMessage(), e);
221             }
222             throwValidationErrorApi(messages -> messages.addErrorsResultSizeExceeded(GLOBAL));
223         }
224 
225         throwValidationErrorApi(messages -> messages.addErrorsInvalidQueryUnknown(GLOBAL));
226         return null; // ignore
227     }
228 }