/* * (C) Copyright 2014 Nuxeo SA (http://nuxeo.com/) and others. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * * Contributors: * bdelbosc */ package org.nuxeo.elasticsearch.provider; import java.io.Serializable; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.elasticsearch.index.query.QueryBuilder; import org.nuxeo.ecm.core.api.CoreSession; import org.nuxeo.ecm.core.api.DocumentModel; import org.nuxeo.ecm.core.api.DocumentModelList; import org.nuxeo.ecm.core.api.NuxeoException; import org.nuxeo.ecm.core.api.SortInfo; import org.nuxeo.ecm.core.query.QueryParseException; import org.nuxeo.ecm.platform.query.api.AbstractPageProvider; import org.nuxeo.ecm.platform.query.api.Aggregate; import org.nuxeo.ecm.platform.query.api.AggregateDefinition; import org.nuxeo.ecm.platform.query.api.Bucket; import org.nuxeo.ecm.platform.query.api.PageProviderDefinition; import org.nuxeo.ecm.platform.query.api.QuickFilter; import org.nuxeo.ecm.platform.query.api.WhereClauseDefinition; import org.nuxeo.ecm.platform.query.nxql.NXQLQueryBuilder; import org.nuxeo.elasticsearch.aggregate.AggregateEsBase; import org.nuxeo.elasticsearch.aggregate.AggregateFactory; import org.nuxeo.elasticsearch.api.ElasticSearchService; import org.nuxeo.elasticsearch.api.EsResult; import org.nuxeo.elasticsearch.query.NxQueryBuilder; import org.nuxeo.elasticsearch.query.PageProviderQueryBuilder; import org.nuxeo.runtime.api.Framework; public class ElasticSearchNativePageProvider extends AbstractPageProvider<DocumentModel> { public static final String CORE_SESSION_PROPERTY = "coreSession"; public static final String SEARCH_ON_ALL_REPOSITORIES_PROPERTY = "searchAllRepositories"; protected static final Log log = LogFactory.getLog(ElasticSearchNativePageProvider.class); private static final long serialVersionUID = 1L; protected List<DocumentModel> currentPageDocuments; protected Map<String, Aggregate<? extends Bucket>> currentAggregates; @Override public Map<String, Aggregate<? extends Bucket>> getAggregates() { getCurrentPage(); return currentAggregates; } @Override public List<DocumentModel> getCurrentPage() { long t0 = System.currentTimeMillis(); // use a cache if (currentPageDocuments != null) { return currentPageDocuments; } error = null; errorMessage = null; if (log.isDebugEnabled()) { log.debug(String.format("Perform query for provider '%s': with pageSize=%d, offset=%d", getName(), getMinMaxPageSize(), getCurrentPageOffset())); } currentPageDocuments = new ArrayList<DocumentModel>(); // Build the ES query QueryBuilder query = makeQueryBuilder(); SortInfo[] sortArray = null; if (sortInfos != null) { sortArray = sortInfos.toArray(new SortInfo[sortInfos.size()]); } // Execute the ES query ElasticSearchService ess = Framework.getLocalService(ElasticSearchService.class); try { NxQueryBuilder nxQuery = new NxQueryBuilder(getCoreSession()).esQuery(query) .offset((int) getCurrentPageOffset()) .limit((int) getMinMaxPageSize()) .addSort(sortArray) .addAggregates(buildAggregates()); if (searchOnAllRepositories()) { nxQuery.searchOnAllRepositories(); } List<String> highlightFields = getHighlights(); if (highlightFields != null && !highlightFields.isEmpty()) { nxQuery.highlight(highlightFields); } EsResult ret = ess.queryAndAggregate(nxQuery); DocumentModelList dmList = ret.getDocuments(); currentAggregates = new HashMap<>(ret.getAggregates().size()); for (Aggregate<Bucket> agg : ret.getAggregates()) { currentAggregates.put(agg.getId(), agg); } setResultsCount(dmList.totalSize()); currentPageDocuments = dmList; } catch (QueryParseException e) { error = e; errorMessage = e.getMessage(); log.warn(e.getMessage(), e); } // send event for statistics ! fireSearchEvent(getCoreSession().getPrincipal(), query.toString(), currentPageDocuments, System.currentTimeMillis() - t0); return currentPageDocuments; } private List<AggregateEsBase<? extends Bucket>> buildAggregates() { ArrayList<AggregateEsBase<? extends Bucket>> ret = new ArrayList<>(getAggregateDefinitions().size()); for (AggregateDefinition def : getAggregateDefinitions()) { ret.add(AggregateFactory.create(def, getSearchDocumentModel())); } return ret; } @Override public boolean hasAggregateSupport() { return true; } protected QueryBuilder makeQueryBuilder() { QueryBuilder ret; PageProviderDefinition def = getDefinition(); List<QuickFilter> quickFilters = getQuickFilters(); String quickFiltersClause = ""; if (quickFilters != null && !quickFilters.isEmpty()) { for (QuickFilter quickFilter : quickFilters) { String clause = quickFilter.getClause(); if (!quickFiltersClause.isEmpty() && clause != null) { quickFiltersClause = NXQLQueryBuilder.appendClause(quickFiltersClause, clause); } else { quickFiltersClause = clause != null ? clause : ""; } } } WhereClauseDefinition whereClause = def.getWhereClause(); if (whereClause == null) { String originalPattern = def.getPattern(); String pattern = quickFiltersClause.isEmpty() ? originalPattern : StringUtils.containsIgnoreCase(originalPattern, " WHERE ") ? NXQLQueryBuilder.appendClause(originalPattern, quickFiltersClause) : originalPattern + " WHERE " + quickFiltersClause; ret = PageProviderQueryBuilder.makeQuery(pattern, getParameters(), def.getQuotePatternParameters(), def.getEscapePatternParameters(), isNativeQuery()); } else { DocumentModel searchDocumentModel = getSearchDocumentModel(); if (searchDocumentModel == null) { throw new NuxeoException(String.format( "Cannot build query of provider '%s': " + "no search document model is set", getName())); } ret = PageProviderQueryBuilder.makeQuery(searchDocumentModel, whereClause, quickFiltersClause, getParameters(), isNativeQuery()); } return ret; } @Override protected void pageChanged() { currentPageDocuments = null; currentAggregates = null; super.pageChanged(); } @Override public void refresh() { currentPageDocuments = null; currentAggregates = null; super.refresh(); } protected CoreSession getCoreSession() { Map<String, Serializable> props = getProperties(); CoreSession coreSession = (CoreSession) props.get(CORE_SESSION_PROPERTY); if (coreSession == null) { throw new NuxeoException("cannot find core session"); } return coreSession; } protected boolean searchOnAllRepositories() { String value = (String) getProperties().get(SEARCH_ON_ALL_REPOSITORIES_PROPERTY); if (value == null) { return false; } return Boolean.valueOf(value); } public boolean isNativeQuery() { return true; } }