/******************************************************************************* * Copyright (c) 2013 aegif. * * This file is part of NemakiWare. * * NemakiWare is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * NemakiWare 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 General Public License for more details. * * You should have received a copy of the GNU General Public License along with NemakiWare. * If not, see <http://www.gnu.org/licenses/>. * * Contributors: * linzhixing(https://github.com/linzhixing) - initial API and implementation ******************************************************************************/ package jp.aegif.nemaki.cmis.aspect.query.solr; import java.math.BigInteger; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.concurrent.locks.Lock; import jp.aegif.nemaki.businesslogic.ContentService; import jp.aegif.nemaki.cmis.aspect.CompileService; import jp.aegif.nemaki.cmis.aspect.ExceptionService; import jp.aegif.nemaki.cmis.aspect.PermissionService; import jp.aegif.nemaki.cmis.aspect.query.QueryProcessor; import jp.aegif.nemaki.cmis.aspect.type.TypeManager; import jp.aegif.nemaki.model.Content; import jp.aegif.nemaki.util.lock.ThreadLockService; import org.antlr.runtime.tree.Tree; import org.apache.chemistry.opencmis.commons.PropertyIds; import org.apache.chemistry.opencmis.commons.data.ObjectList; import org.apache.chemistry.opencmis.commons.definitions.TypeDefinition; import org.apache.chemistry.opencmis.commons.definitions.TypeDefinitionContainer; import org.apache.chemistry.opencmis.commons.enums.IncludeRelationships; import org.apache.chemistry.opencmis.commons.impl.dataobjects.ObjectListImpl; import org.apache.chemistry.opencmis.commons.server.CallContext; import org.apache.chemistry.opencmis.server.support.query.QueryObject; import org.apache.chemistry.opencmis.server.support.query.QueryObject.SortSpec; import org.apache.chemistry.opencmis.server.support.query.QueryUtilStrict; import org.apache.commons.lang.StringUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.lucene.index.Term; import org.apache.lucene.search.Query; import org.apache.lucene.search.TermQuery; import org.apache.solr.client.solrj.SolrQuery; import org.apache.solr.client.solrj.SolrServer; import org.apache.solr.client.solrj.SolrServerException; import org.apache.solr.client.solrj.response.QueryResponse; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.params.CommonParams; public class SolrQueryProcessor implements QueryProcessor { private TypeManager typeManager; private ContentService contentService; private PermissionService permissionService; private CompileService compileService; private ExceptionService exceptionService; private ThreadLockService threadLockService; private SolrUtil solrUtil; private static final Log logger = LogFactory .getLog(SolrQueryProcessor.class); public SolrQueryProcessor() { } private class CmisTypeManager implements org.apache.chemistry.opencmis.server.support.TypeManager{ private String repositoryId; private TypeManager typeManager; public CmisTypeManager(String repositoryId, TypeManager typeManager){ this.repositoryId = repositoryId; this.typeManager = typeManager; } @Override public void addTypeDefinition(TypeDefinition arg0, boolean arg1) { // TODO Auto-generated method stub } @Override public void deleteTypeDefinition(String typeId) { typeManager.deleteTypeDefinition(repositoryId, typeId); } @Override public String getPropertyIdForQueryName(TypeDefinition typeDefinition, String propQueryName) { return typeManager.getPropertyIdForQueryName(repositoryId, typeDefinition, propQueryName); } @Override public List<TypeDefinitionContainer> getRootTypes() { return typeManager.getRootTypes(repositoryId); } @Override public TypeDefinitionContainer getTypeById(String typeId) { return typeManager.getTypeById(repositoryId, typeId); } @Override public TypeDefinition getTypeByQueryName(String typeQueryName) { return typeManager.getTypeByQueryName(repositoryId, typeQueryName); } @Override public Collection<TypeDefinitionContainer> getTypeDefinitionList() { return typeManager.getTypeDefinitionList(repositoryId); } @Override public void updateTypeDefinition(TypeDefinition typeDefinition) { typeManager.updateTypeDefinition(repositoryId, typeDefinition); } } @Override public ObjectList query(CallContext callContext, String repositoryId, String statement, Boolean searchAllVersions, Boolean includeAllowableActions, IncludeRelationships includeRelationships, String renditionFilter, BigInteger maxItems, BigInteger skipCount) { SolrServer solrServer = solrUtil.getSolrServer(); // TODO walker is required? QueryUtilStrict util = new QueryUtilStrict(statement, new CmisTypeManager(repositoryId, typeManager), null); QueryObject queryObject = util.getQueryObject(); // Get where caluse as Tree Tree whereTree = null; try { util.processStatement(); Tree tree = util.parseStatement(); whereTree = extractWhereTree(tree); } catch (Exception e) { e.printStackTrace(); } // Build solr statement of WHERE String whereQueryString = ""; if (whereTree == null || whereTree.isNil()) { whereQueryString = "*:*"; } else { try { SolrPredicateWalker solrPredicateWalker = new SolrPredicateWalker(repositoryId, queryObject, solrUtil, contentService); Query whereQuery = solrPredicateWalker.walkPredicate(whereTree); whereQueryString = whereQuery.toString(); } catch (Exception e) { e.printStackTrace(); // TODO Output more detailed exception exceptionService.invalidArgument("Invalid CMIS SQL statement!"); } } // Build solr query of FROM String fromQueryString = ""; String repositoryQuery = "repository_id:" + repositoryId; fromQueryString += repositoryQuery + " AND "; TypeDefinition td = queryObject.getMainFromName(); // includedInSupertypeQuery List<TypeDefinitionContainer> typeDescendants = typeManager .getTypesDescendants(repositoryId, td.getId(), BigInteger.valueOf(-1), false); Iterator<TypeDefinitionContainer> iterator = typeDescendants.iterator(); List<String> tables = new ArrayList<String>(); while (iterator.hasNext()) { TypeDefinition descendant = iterator.next().getTypeDefinition(); if (td.getId() != descendant.getId()) { boolean isq = (descendant.isIncludedInSupertypeQuery() == null) ? false : descendant.isIncludedInSupertypeQuery(); if (!isq) continue; } String table = descendant.getQueryName(); tables.add(table.replaceAll(":", "\\\\:")); } Term t = new Term( solrUtil.getPropertyNameInSolr(PropertyIds.OBJECT_TYPE_ID), StringUtils.join(tables, " ")); fromQueryString += new TermQuery(t).toString(); // Execute query SolrQuery solrQuery = new SolrQuery(); solrQuery.setQuery(whereQueryString); solrQuery.setFilterQueries(fromQueryString); //TEST solrQuery.set(CommonParams.START, 0); solrQuery.set(CommonParams.ROWS, maxItems.intValue()); QueryResponse resp = null; try { resp = solrServer.query(solrQuery); } catch (SolrServerException e) { e.printStackTrace(); } // Output search results to ObjectList if (resp != null && resp.getResults() != null && resp.getResults().getNumFound() != 0) { SolrDocumentList docs = resp.getResults(); List<Content> contents = new ArrayList<Content>(); for (SolrDocument doc : docs) { String docId = (String) doc.getFieldValue("object_id"); Content c = contentService.getContent(repositoryId, docId); // When for some reason the content is missed, pass through if (c == null) { logger.warn("[objectId=" + docId + "]It is missed in DB but still rests in Solr."); } else { contents.add(c); } } List<Lock> locks = threadLockService.readLocks(repositoryId, contents); try{ threadLockService.bulkLock(locks); // Filter out by permissions List<Content> permitted = permissionService.getFiltered( callContext, repositoryId, contents); // Filter return value with SELECT clause Map<String, String> requestedWithAliasKey = queryObject .getRequestedPropertiesByAlias(); String filter = null; if (!requestedWithAliasKey.keySet().contains("*")) { // Create filter(queryNames) from query aliases filter = StringUtils.join(requestedWithAliasKey.values(), ","); } // Build ObjectList String orderBy = orderBy(queryObject); ObjectList result = compileService.compileObjectDataList( callContext, repositoryId, permitted, filter, includeAllowableActions, includeRelationships, renditionFilter, false, maxItems, skipCount, false, orderBy); return result; }finally{ threadLockService.bulkUnlock(locks); } } else { ObjectListImpl nullList = new ObjectListImpl(); nullList.setHasMoreItems(false); nullList.setNumItems(BigInteger.ZERO); return nullList; } } private String orderBy(QueryObject queryObject){ List<SortSpec> sortSpecs = queryObject.getOrderBys(); List<String> _orderBy = new ArrayList<String>(); for (SortSpec sortSpec : sortSpecs) { List<String> _sortSpec = new ArrayList<String>(); _sortSpec.add(sortSpec.getSelector().getName()); if (!sortSpec.isAscending()) { _sortSpec.add("DESC"); } _orderBy.add(StringUtils.join(_sortSpec, " ")); } String orderBy = StringUtils.join(_orderBy, ","); return orderBy; } private Tree extractWhereTree(Tree tree){ for (int i = 0; i < tree.getChildCount(); i++) { Tree selectTree = tree.getChild(i); if ("SELECT".equals(selectTree.getText())) { for(int j=0; j < selectTree.getChildCount(); j++){ Tree whereTree = selectTree.getChild(j); if("WHERE".equals(whereTree.getText())){ return whereTree.getChild(0); } } } } return null; } public void setTypeManager(TypeManager typeManager) { this.typeManager = typeManager; } public void setContentService(ContentService contentService) { this.contentService = contentService; } public void setPermissionService(PermissionService permissionService) { this.permissionService = permissionService; } public void setCompileService(CompileService compileService) { this.compileService = compileService; } public void setExceptionService(ExceptionService exceptionService) { this.exceptionService = exceptionService; } public void setSolrUtil(SolrUtil solrUtil) { this.solrUtil = solrUtil; } public void setThreadLockService(ThreadLockService threadLockService) { this.threadLockService = threadLockService; } }