Java tutorial
/* * #%L * Alfresco Repository * %% * Copyright (C) 2005 - 2016 Alfresco Software Limited * %% * This file is part of the Alfresco software. * If the software was purchased under a paid Alfresco license, the terms of * the paid license agreement will prevail. Otherwise, the software is * provided under the following open source license terms: * * Alfresco is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Alfresco is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. * #L% */ package org.alfresco.repo.search.impl.solr; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.alfresco.repo.domain.node.Node; import org.alfresco.repo.domain.solr.SOLRDAO; import org.alfresco.repo.search.impl.lucene.ADMLuceneSearcherImpl; import org.alfresco.repo.search.impl.lucene.AbstractLuceneQueryLanguage; import org.alfresco.repo.search.impl.lucene.LuceneQueryLanguageSPI; import org.alfresco.repo.search.impl.lucene.SolrJSONResultSet; import org.alfresco.repo.search.impl.querymodel.QueryModelException; import org.alfresco.repo.search.results.ChildAssocRefResultSet; import org.alfresco.repo.solr.NodeParameters; import org.alfresco.service.cmr.repository.ChildAssociationRef; import org.alfresco.service.cmr.repository.NodeRef; import org.alfresco.service.cmr.repository.NodeService; import org.alfresco.service.cmr.search.QueryConsistency; import org.alfresco.service.cmr.search.ResultSet; import org.alfresco.service.cmr.search.SearchParameters; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.springframework.util.StopWatch; /** * @author Andy * @author Matt Ward */ public class DbOrIndexSwitchingQueryLanguage extends AbstractLuceneQueryLanguage { protected static final Log logger = LogFactory.getLog(DbOrIndexSwitchingQueryLanguage.class); LuceneQueryLanguageSPI dbQueryLanguage; LuceneQueryLanguageSPI indexQueryLanguage; QueryConsistency queryConsistency = QueryConsistency.DEFAULT; private NodeService nodeService; private SOLRDAO solrDao; private boolean hybridEnabled; /** * @param dbQueryLanguage the dbQueryLanguage to set */ public void setDbQueryLanguage(LuceneQueryLanguageSPI dbQueryLanguage) { this.dbQueryLanguage = dbQueryLanguage; } /** * @param indexQueryLanguage the indexQueryLanguage to set */ public void setIndexQueryLanguage(LuceneQueryLanguageSPI indexQueryLanguage) { this.indexQueryLanguage = indexQueryLanguage; } /** * @param queryConsistency the queryConsistency to set */ public void setQueryConsistency(QueryConsistency queryConsistency) { this.queryConsistency = queryConsistency; } /** * @param nodeService the nodeService to set */ public void setNodeService(NodeService nodeService) { this.nodeService = nodeService; } public void setSolrDao(SOLRDAO solrDao) { this.solrDao = solrDao; } public void setHybridEnabled(boolean hybridEnabled) { this.hybridEnabled = hybridEnabled; } public ResultSet executeQuery(SearchParameters searchParameters, ADMLuceneSearcherImpl admLuceneSearcher) { QueryConsistency consistency = searchParameters.getQueryConsistency(); if (consistency == QueryConsistency.DEFAULT) { consistency = queryConsistency; } switch (consistency) { case EVENTUAL: if (indexQueryLanguage != null) { if (logger.isDebugEnabled()) { logger.debug("Using SOLR query: " + dbQueryLanguage.getName() + " for " + searchParameters); } StopWatch stopWatch = new StopWatch("index only"); stopWatch.start(); ResultSet results = indexQueryLanguage.executeQuery(searchParameters, admLuceneSearcher); stopWatch.stop(); if (logger.isDebugEnabled()) { logger.debug("SOLR returned " + results.length() + " results in " + stopWatch.getLastTaskTimeMillis() + "ms"); } return results; } else { throw new QueryModelException("No query language available"); } case TRANSACTIONAL: if (dbQueryLanguage != null) { if (logger.isDebugEnabled()) { logger.debug("Trying db query for " + dbQueryLanguage.getName() + " for " + searchParameters); } StopWatch stopWatch = new StopWatch("database only"); stopWatch.start(); ResultSet results = dbQueryLanguage.executeQuery(flattenDBQuery(searchParameters), admLuceneSearcher); stopWatch.stop(); if (logger.isDebugEnabled()) { logger.debug("DB returned " + results.length() + " results in " + stopWatch.getLastTaskTimeMillis() + "ms"); } return results; } else { throw new QueryModelException("No query language available"); } case HYBRID: if (!hybridEnabled) { throw new DisabledFeatureException("Hybrid query is disabled."); } return executeHybridQuery(searchParameters, admLuceneSearcher); case DEFAULT: case TRANSACTIONAL_IF_POSSIBLE: default: StopWatch stopWatch = new StopWatch("DB if possible"); if (dbQueryLanguage != null) { try { if (logger.isDebugEnabled()) { logger.debug( "Trying db query for " + dbQueryLanguage.getName() + " for " + searchParameters); } stopWatch.start(); ResultSet results = dbQueryLanguage.executeQuery(flattenDBQuery(searchParameters), admLuceneSearcher); stopWatch.stop(); if (logger.isDebugEnabled()) { logger.debug("DB returned " + results.length() + " results in " + stopWatch.getLastTaskTimeMillis() + "ms"); } return results; } catch (QueryModelException qme) { if (stopWatch.isRunning()) { stopWatch.stop(); } // MNT-10323: Logging configuration on JBoss leads to clogging of the log with a lot of these errors because of INFO level when WQS module is installed if (logger.isDebugEnabled()) { logger.debug( "DB query failed for " + dbQueryLanguage.getName() + " for " + searchParameters, qme); } if (indexQueryLanguage != null) { if (logger.isDebugEnabled()) { logger.debug( "Using SOLR query: " + dbQueryLanguage.getName() + " for " + searchParameters); } stopWatch.start(); ResultSet results = indexQueryLanguage.executeQuery(searchParameters, admLuceneSearcher); stopWatch.stop(); if (logger.isDebugEnabled()) { logger.debug("SOLR returned " + results.length() + " results in " + stopWatch.getLastTaskTimeMillis() + "ms"); } return results; } } } else { if (indexQueryLanguage != null) { if (logger.isDebugEnabled()) { logger.debug("(No DB QL) Using SOLR query: " + "dbQueryLanguage==null" + " for " + searchParameters); } stopWatch.start(); ResultSet results = indexQueryLanguage.executeQuery(searchParameters, admLuceneSearcher); stopWatch.stop(); if (logger.isDebugEnabled()) { logger.debug("SOLR returned " + results.length() + " results in " + stopWatch.getLastTaskTimeMillis() + "ms"); } return results; } } throw new QueryModelException("No query language available"); } } private SearchParameters flattenDBQuery(SearchParameters sp) { if (sp.getFilterQueries().size() == 0) { return sp; } else { SearchParameters flatten = sp.copy(); StringBuilder queryBuilder = new StringBuilder(); queryBuilder.append("( ").append(sp.getQuery()).append(" )"); for (String filter : sp.getFilterQueries()) { queryBuilder.append("AND ( ").append(filter).append(" )"); } flatten.setQuery(queryBuilder.toString()); // the filter can be left and will be ignored by the DB query return flatten; } } private ResultSet executeHybridQuery(SearchParameters searchParameters, ADMLuceneSearcherImpl admLuceneSearcher) { if (indexQueryLanguage == null || dbQueryLanguage == null) { throw new QueryModelException("Both index and DB query language required for hybrid search [index=" + indexQueryLanguage + ", DB=" + dbQueryLanguage + "]"); } StopWatch stopWatch = new StopWatch("hybrid search"); if (logger.isDebugEnabled()) { logger.debug( "Hybrid search, using SOLR query: " + dbQueryLanguage.getName() + " for " + searchParameters); } stopWatch.start("index query"); ResultSet indexResults = indexQueryLanguage.executeQuery(searchParameters, admLuceneSearcher); stopWatch.stop(); if (logger.isDebugEnabled()) { logger.debug("SOLR query returned " + indexResults.length() + " results in " + stopWatch.getLastTaskTimeMillis() + "ms"); } // TODO: if the results are up-to-date, then nothing more to do - return the results. if (!(indexResults instanceof SolrJSONResultSet)) { if (logger.isWarnEnabled()) { logger.warn("Hybrid search can only use database when SOLR is also in use. " + "Skipping DB search, returning results from index."); } return indexResults; } long lastTxId = ((SolrJSONResultSet) indexResults).getLastIndexedTxId(); searchParameters.setSinceTxId(lastTxId); if (logger.isDebugEnabled()) { logger.debug( "Hybrid search, using DB query: " + dbQueryLanguage.getName() + " for " + searchParameters); } stopWatch.start("database query"); ResultSet dbResults = dbQueryLanguage.executeQuery(searchParameters, admLuceneSearcher); stopWatch.stop(); if (logger.isDebugEnabled()) { logger.debug("DB query returned " + dbResults.length() + " results in " + stopWatch.getLastTaskTimeMillis() + "ms"); } // Merge result sets List<ChildAssociationRef> childAssocs = new ArrayList<>(); NodeParameters nodeParameters = new NodeParameters(); nodeParameters.setFromTxnId(lastTxId + 1); // TODO: setToTxnId(null) when SolrDAO behaviour is fixed. nodeParameters.setToTxnId(Long.MAX_VALUE); stopWatch.start("get changed nodes"); List<Node> changedNodeList = solrDao.getNodes(nodeParameters, null); stopWatch.stop(); if (logger.isDebugEnabled()) { logger.debug("Nodes changed since last indexed transaction (ID " + lastTxId + ") = " + changedNodeList.size() + " (took " + stopWatch.getLastTaskTimeMillis() + "ms)"); } stopWatch.start("merge result sets"); Set<NodeRef> nodeRefs = new HashSet<>(changedNodeList.size()); for (Node n : changedNodeList) { nodeRefs.add(n.getNodeRef()); } // Only use the SOLR results for nodes that haven't changed since indexing. for (ChildAssociationRef car : indexResults.getChildAssocRefs()) { if (!nodeRefs.contains(car.getChildRef())) { childAssocs.add(car); } } // Merge in all the database results. childAssocs.addAll(dbResults.getChildAssocRefs()); ResultSet results = new ChildAssocRefResultSet(nodeService, childAssocs); stopWatch.stop(); // merge result sets if (logger.isDebugEnabled()) { String stats = String.format("SOLR=%d, DB=%d, total=%d", indexResults.length(), dbResults.length(), results.length()); logger.debug("Hybrid search returning combined results with counts: " + stats); logger.debug(stopWatch.prettyPrint()); } return results; } }