001/* 002 * (C) Copyright 2014-2019 Nuxeo (http://nuxeo.com/) and others. 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 * 016 * Contributors: 017 * Vladimir Pasquier <vpasquier@nuxeo.com> 018 */ 019package org.nuxeo.ecm.restapi.server.jaxrs; 020 021import static javax.servlet.http.HttpServletResponse.SC_BAD_REQUEST; 022 023import java.io.Serializable; 024import java.util.ArrayList; 025import java.util.EnumMap; 026import java.util.HashMap; 027import java.util.List; 028import java.util.Map; 029 030import javax.ws.rs.GET; 031import javax.ws.rs.Path; 032import javax.ws.rs.PathParam; 033import javax.ws.rs.core.Context; 034import javax.ws.rs.core.MultivaluedMap; 035import javax.ws.rs.core.UriInfo; 036 037import org.apache.commons.lang3.StringUtils; 038import org.nuxeo.ecm.automation.core.util.PageProviderHelper; 039import org.nuxeo.ecm.automation.core.util.Properties; 040import org.nuxeo.ecm.automation.jaxrs.io.documents.PaginableDocumentModelListImpl; 041import org.nuxeo.ecm.core.api.CoreSession; 042import org.nuxeo.ecm.core.api.DocumentModel; 043import org.nuxeo.ecm.core.api.DocumentModelList; 044import org.nuxeo.ecm.core.api.NuxeoException; 045import org.nuxeo.ecm.core.api.SortInfo; 046import org.nuxeo.ecm.platform.query.api.PageProvider; 047import org.nuxeo.ecm.platform.query.api.PageProviderDefinition; 048import org.nuxeo.ecm.platform.query.api.PageProviderService; 049import org.nuxeo.ecm.platform.query.api.QuickFilter; 050import org.nuxeo.ecm.platform.query.nxql.CoreQueryDocumentPageProvider; 051import org.nuxeo.ecm.restapi.server.jaxrs.adapters.SearchAdapter; 052import org.nuxeo.ecm.webengine.model.WebObject; 053import org.nuxeo.ecm.webengine.model.impl.AbstractResource; 054import org.nuxeo.ecm.webengine.model.impl.ResourceTypeImpl; 055import org.nuxeo.runtime.api.Framework; 056 057/** 058 * @since 6.0 Search endpoint to perform queries on the repository through rest api. 059 */ 060@WebObject(type = "query") 061public class QueryObject extends AbstractResource<ResourceTypeImpl> { 062 063 public static final String PATH = "query"; 064 065 public static final String NXQL = "NXQL"; 066 067 public static final String QUERY = "query"; 068 069 public static final String PAGE_SIZE = "pageSize"; 070 071 public static final String CURRENT_PAGE_INDEX = "currentPageIndex"; 072 073 public static final String MAX_RESULTS = "maxResults"; 074 075 public static final String SORT_BY = "sortBy"; 076 077 public static final String SORT_ORDER = "sortOrder"; 078 079 public static final String ORDERED_PARAMS = "queryParams"; 080 081 public static final String CURRENT_USERID_PATTERN = "$currentUser"; 082 083 public static final String CURRENT_REPO_PATTERN = "$currentRepository"; 084 085 /** 086 * @since 8.4 087 */ 088 public static final String QUICK_FILTERS = "quickFilters"; 089 090 protected EnumMap<QueryParams, String> queryParametersMap; 091 092 protected EnumMap<LangParams, String> langPathMap; 093 094 protected PageProviderService pageProviderService; 095 096 @Override 097 public void initialize(Object... args) { 098 pageProviderService = Framework.getService(PageProviderService.class); 099 // Query Enum Parameters Map 100 queryParametersMap = new EnumMap<>(QueryParams.class); 101 queryParametersMap.put(QueryParams.PAGE_SIZE, PAGE_SIZE); 102 queryParametersMap.put(QueryParams.CURRENT_PAGE_INDEX, CURRENT_PAGE_INDEX); 103 queryParametersMap.put(QueryParams.MAX_RESULTS, MAX_RESULTS); 104 queryParametersMap.put(QueryParams.SORT_BY, SORT_BY); 105 queryParametersMap.put(QueryParams.SORT_ORDER, SORT_ORDER); 106 queryParametersMap.put(QueryParams.QUERY, QUERY); 107 queryParametersMap.put(QueryParams.ORDERED_PARAMS, ORDERED_PARAMS); 108 queryParametersMap.put(QueryParams.QUICK_FILTERS, QUICK_FILTERS); 109 // Lang Path Enum Map 110 langPathMap = new EnumMap<>(LangParams.class); 111 langPathMap.put(LangParams.NXQL, NXQL); 112 } 113 114 @SuppressWarnings("unchecked") 115 protected DocumentModelList getQuery(UriInfo uriInfo, String langOrProviderName) { 116 // Fetching all parameters 117 MultivaluedMap<String, String> queryParams = uriInfo.getQueryParameters(); 118 // Look if provider name is given 119 String providerName = null; 120 if (!langPathMap.containsValue(langOrProviderName)) { 121 providerName = langOrProviderName; 122 } 123 String query = queryParams.getFirst(QUERY); 124 String pageSize = queryParams.getFirst(PAGE_SIZE); 125 String currentPageIndex = queryParams.getFirst(CURRENT_PAGE_INDEX); 126 String maxResults = queryParams.getFirst(MAX_RESULTS); 127 String sortBy = queryParams.getFirst(SORT_BY); 128 String sortOrder = queryParams.getFirst(SORT_ORDER); 129 List<String> orderedParams = queryParams.get(ORDERED_PARAMS); 130 String quickFilters = queryParams.getFirst(QUICK_FILTERS); 131 132 // If no query or provider name has been found 133 // Execute big select 134 if (query == null && StringUtils.isBlank(providerName)) { 135 // provide a defaut query 136 query = "SELECT * from Document"; 137 } 138 139 // Fetching named parameters (other custom query parameters in the 140 // path) 141 Properties namedParameters = new Properties(); 142 for (String namedParameterKey : queryParams.keySet()) { 143 if (!queryParametersMap.containsValue(namedParameterKey)) { 144 String value = queryParams.getFirst(namedParameterKey); 145 if (value != null) { 146 if (value.equals(CURRENT_USERID_PATTERN)) { 147 value = ctx.getCoreSession().getPrincipal().getName(); 148 } else if (value.equals(CURRENT_REPO_PATTERN)) { 149 value = ctx.getCoreSession().getRepositoryName(); 150 } 151 } 152 namedParameters.put(namedParameterKey, value); 153 } 154 } 155 156 // Target query page 157 Long targetPage = null; 158 if (currentPageIndex != null) { 159 targetPage = Long.valueOf(currentPageIndex); 160 } 161 162 // Target page size 163 Long targetPageSize = null; 164 if (pageSize != null) { 165 targetPageSize = Long.valueOf(pageSize); 166 } 167 168 // Ordered Parameters 169 Object[] parameters = null; 170 if (orderedParams != null && !orderedParams.isEmpty()) { 171 parameters = orderedParams.toArray(new String[0]); 172 // expand specific parameters 173 for (int idx = 0; idx < parameters.length; idx++) { 174 String value = (String) parameters[idx]; 175 if (value.equals(CURRENT_USERID_PATTERN)) { 176 parameters[idx] = ctx.getCoreSession().getPrincipal().getName(); 177 } else if (value.equals(CURRENT_REPO_PATTERN)) { 178 parameters[idx] = ctx.getCoreSession().getRepositoryName(); 179 } 180 } 181 } 182 183 Map<String, Serializable> props = new HashMap<>(); 184 props.put(CoreQueryDocumentPageProvider.CORE_SESSION_PROPERTY, (Serializable) ctx.getCoreSession()); 185 186 DocumentModel searchDocumentModel = PageProviderHelper.getSearchDocumentModel(ctx.getCoreSession(), 187 pageProviderService, providerName, namedParameters); 188 189 // Sort Info Management 190 List<SortInfo> sortInfoList = null; 191 if (!StringUtils.isBlank(sortBy)) { 192 sortInfoList = new ArrayList<>(); 193 String[] sorts = sortBy.split(","); 194 String[] orders = null; 195 if (!StringUtils.isBlank(sortOrder)) { 196 orders = sortOrder.split(","); 197 } 198 for (int i = 0; i < sorts.length; i++) { 199 String sort = sorts[i]; 200 boolean sortAscending = (orders != null && orders.length > i && "asc".equalsIgnoreCase(orders[i])); 201 sortInfoList.add(new SortInfo(sort, sortAscending)); 202 } 203 } 204 205 PaginableDocumentModelListImpl res; 206 if (query != null) { 207 PageProviderDefinition ppdefinition = pageProviderService.getPageProviderDefinition( 208 SearchAdapter.pageProviderName); 209 ppdefinition.setPattern(query); 210 if (maxResults != null && !maxResults.isEmpty() && !maxResults.equals("-1")) { 211 // set the maxResults to avoid slowing down queries 212 ppdefinition.getProperties().put("maxResults", maxResults); 213 } 214 if (StringUtils.isBlank(providerName)) { 215 providerName = SearchAdapter.pageProviderName; 216 } 217 218 res = new PaginableDocumentModelListImpl( 219 (PageProvider<DocumentModel>) pageProviderService.getPageProvider(providerName, ppdefinition, 220 searchDocumentModel, sortInfoList, targetPageSize, targetPage, props, parameters), 221 null); 222 } else { 223 PageProviderDefinition pageProviderDefinition = pageProviderService.getPageProviderDefinition(providerName); 224 // Quick filters management 225 List<QuickFilter> quickFilterList = new ArrayList<>(); 226 if (quickFilters != null && !quickFilters.isEmpty()) { 227 String[] filters = quickFilters.split(","); 228 List<QuickFilter> ppQuickFilters = pageProviderDefinition.getQuickFilters(); 229 for (String filter : filters) { 230 for (QuickFilter quickFilter : ppQuickFilters) { 231 if (quickFilter.getName().equals(filter)) { 232 quickFilterList.add(quickFilter); 233 break; 234 } 235 } 236 } 237 } 238 res = new PaginableDocumentModelListImpl( 239 (PageProvider<DocumentModel>) pageProviderService.getPageProvider(providerName, searchDocumentModel, 240 sortInfoList, targetPageSize, targetPage, props, quickFilterList, parameters), 241 null); 242 } 243 if (res.hasError()) { 244 throw new NuxeoException(res.getErrorMessage(), SC_BAD_REQUEST); 245 } 246 return res; 247 } 248 249 /** 250 * @deprecated since 11.1, use 251 * {@link PageProviderHelper#getSearchDocumentModel(CoreSession, PageProviderService, String, Map)} 252 * instead 253 */ 254 @Deprecated(since = "11.1") 255 protected DocumentModel getSearchDocumentModel(CoreSession session, PageProviderService pps, String providerName, 256 Properties namedParameters) { 257 return PageProviderHelper.getSearchDocumentModel(session, pps, providerName, namedParameters); 258 } 259 260 /** 261 * Perform query on the repository. By default in NXQL. 262 * 263 * @param uriInfo Query parameters 264 * @return Document Listing 265 */ 266 @GET 267 public Object doQuery(@Context UriInfo uriInfo) { 268 return getQuery(uriInfo, NXQL); 269 } 270 271 /** 272 * Perform query on the repository in NXQL or specific pageprovider name 273 * 274 * @param uriInfo Query parameters 275 * @param langOrProviderName NXQL or specific provider name 276 * @return Document Listing 277 */ 278 @GET 279 @Path("{langOrProviderName}") 280 public Object doSpecificQuery(@Context UriInfo uriInfo, 281 @PathParam("langOrProviderName") String langOrProviderName) { 282 return getQuery(uriInfo, langOrProviderName); 283 } 284 285 public enum QueryParams { 286 PAGE_SIZE, CURRENT_PAGE_INDEX, MAX_RESULTS, SORT_BY, SORT_ORDER, ORDERED_PARAMS, QUERY, QUICK_FILTERS 287 } 288 289 public enum LangParams { 290 NXQL 291 } 292 293}