001/* 002 * (C) Copyright 2006-2018 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 * Thierry Delprat 018 */ 019package org.nuxeo.apidoc.search; 020 021import java.util.ArrayList; 022import java.util.Collections; 023import java.util.HashMap; 024import java.util.List; 025import java.util.Map; 026 027import org.apache.commons.text.StrBuilder; 028import org.nuxeo.apidoc.adapters.BaseNuxeoArtifactDocAdapter; 029import org.nuxeo.apidoc.adapters.BundleGroupDocAdapter; 030import org.nuxeo.apidoc.adapters.BundleInfoDocAdapter; 031import org.nuxeo.apidoc.adapters.ComponentInfoDocAdapter; 032import org.nuxeo.apidoc.adapters.ExtensionInfoDocAdapter; 033import org.nuxeo.apidoc.adapters.ExtensionPointInfoDocAdapter; 034import org.nuxeo.apidoc.adapters.ServiceInfoDocAdapter; 035import org.nuxeo.apidoc.api.BundleGroup; 036import org.nuxeo.apidoc.api.BundleInfo; 037import org.nuxeo.apidoc.api.ComponentInfo; 038import org.nuxeo.apidoc.api.DocumentationItem; 039import org.nuxeo.apidoc.api.ExtensionInfo; 040import org.nuxeo.apidoc.api.ExtensionPointInfo; 041import org.nuxeo.apidoc.api.NuxeoArtifact; 042import org.nuxeo.apidoc.api.QueryHelper; 043import org.nuxeo.apidoc.api.ServiceInfo; 044import org.nuxeo.apidoc.repository.RepositoryDistributionSnapshot; 045import org.nuxeo.apidoc.snapshot.DistributionSnapshot; 046import org.nuxeo.apidoc.snapshot.SnapshotManager; 047import org.nuxeo.ecm.core.api.CoreSession; 048import org.nuxeo.ecm.core.api.DocumentModel; 049import org.nuxeo.ecm.core.api.DocumentModelList; 050import org.nuxeo.ecm.core.query.sql.NXQL; 051import org.nuxeo.elasticsearch.api.ElasticSearchService; 052import org.nuxeo.elasticsearch.query.NxQueryBuilder; 053import org.nuxeo.runtime.api.Framework; 054 055public class ArtifactSearcherImpl implements ArtifactSearcher { 056 057 protected static final int MAX_RESULTS = 1000; 058 059 protected NuxeoArtifact mapDoc2Artifact(DocumentModel doc) { 060 NuxeoArtifact artifact = null; 061 062 switch (doc.getType()) { 063 case BundleGroup.TYPE_NAME: 064 artifact = new BundleGroupDocAdapter(doc); 065 break; 066 case BundleInfo.TYPE_NAME: 067 artifact = new BundleInfoDocAdapter(doc); 068 break; 069 case ComponentInfo.TYPE_NAME: 070 artifact = new ComponentInfoDocAdapter(doc); 071 break; 072 case ExtensionPointInfo.TYPE_NAME: 073 artifact = new ExtensionPointInfoDocAdapter(doc); 074 break; 075 case ExtensionInfo.TYPE_NAME: 076 artifact = new ExtensionInfoDocAdapter(doc); 077 break; 078 case DistributionSnapshot.TYPE_NAME: 079 artifact = new RepositoryDistributionSnapshot(doc); 080 break; 081 case ServiceInfo.TYPE_NAME: 082 artifact = new ServiceInfoDocAdapter(doc); 083 break; 084 } 085 086 return artifact; 087 } 088 089 @Override 090 public List<NuxeoArtifact> searchArtifact(CoreSession session, String distribId, String fulltext) { 091 List<NuxeoArtifact> result = new ArrayList<>(); 092 093 DistributionSnapshot snap = Framework.getService(SnapshotManager.class).getSnapshot(distribId, session); 094 if (!(snap instanceof RepositoryDistributionSnapshot)) { 095 return Collections.emptyList(); 096 } 097 098 DocumentModel dist = ((RepositoryDistributionSnapshot) snap).getDoc(); 099 StrBuilder q = new StrBuilder("SELECT * FROM Document WHERE "); 100 q.append("ecm:path STARTSWITH '").append(dist.getPathAsString()).append("'"); 101 String query = q.toString(); 102 if (fulltext != null) { 103 query += " AND " + NXQL.ECM_FULLTEXT + " = " + NXQL.escapeString(fulltext); 104 } 105 106 ElasticSearchService ess = Framework.getService(ElasticSearchService.class); 107 DocumentModelList docs = ess.query(new NxQueryBuilder(session).nxql(query).limit(MAX_RESULTS)); 108 for (DocumentModel doc : docs) { 109 NuxeoArtifact artifact = mapDoc2Artifact(doc); 110 if (artifact != null) { 111 result.add(artifact); 112 } 113 } 114 return result; 115 } 116 117 @Override 118 public List<DocumentationItem> searchDocumentation(CoreSession session, String distribId, String fulltext, 119 String targetType) { 120 DistributionSnapshot snap = Framework.getService(SnapshotManager.class).getSnapshot(distribId, session); 121 DocumentModel dist = ((RepositoryDistributionSnapshot) snap).getDoc(); 122 String query = QueryHelper.select(DocumentationItem.TYPE_NAME, dist, NXQL.ECM_FULLTEXT, fulltext); 123 if (targetType != null) { 124 query += " AND " + DocumentationItem.PROP_TARGET_TYPE + " = " + NXQL.escapeString(targetType); 125 } 126 127 ElasticSearchService ess = Framework.getService(ElasticSearchService.class); 128 DocumentModelList docs = ess.query(new NxQueryBuilder(session).nxql(query).limit(MAX_RESULTS)); 129 List<DocumentationItem> result = new ArrayList<>(); 130 for (DocumentModel doc : docs) { 131 DocumentationItem docItem = doc.getAdapter(DocumentationItem.class); 132 if (docItem != null) { 133 result.add(docItem); 134 } 135 } 136 return result; 137 } 138 139 @Override 140 public List<NuxeoArtifact> filterArtifact(CoreSession session, String distribId, String type, String fulltext) { 141 List<NuxeoArtifact> result = new ArrayList<>(); 142 143 List<NuxeoArtifact> matchingArtifacts = searchArtifact(session, distribId, fulltext); 144 List<DocumentationItem> matchingDocumentationItems = searchDocumentation(session, distribId, fulltext, null); 145 146 Map<String, ArtifactWithWeight> sortMap = new HashMap<>(); 147 148 for (NuxeoArtifact matchingArtifact : matchingArtifacts) { 149 ArtifactWithWeight artifactWithWeight; 150 NuxeoArtifact matchingParentArtifact = resolveInTree(session, distribId, matchingArtifact, type); 151 if (matchingParentArtifact != null) { 152 artifactWithWeight = new ArtifactWithWeight(matchingParentArtifact); 153 } else if (matchingArtifact.getArtifactType().equals(type)) { 154 artifactWithWeight = new ArtifactWithWeight(matchingArtifact); 155 } else { 156 continue; 157 } 158 159 String id = artifactWithWeight.getArtifact().getId(); 160 if (sortMap.containsKey(id)) { 161 sortMap.get(id).addHit(); 162 } else { 163 sortMap.put(id, new ArtifactWithWeight(matchingParentArtifact)); 164 } 165 } 166 167 for (DocumentationItem matchingDocumentationItem : matchingDocumentationItems) { 168 NuxeoArtifact resultArtifact = resolveInTree(session, distribId, matchingDocumentationItem, type); 169 if (resultArtifact != null) { 170 if (sortMap.containsKey(resultArtifact.getId())) { 171 sortMap.get(resultArtifact.getId()).addHit(); 172 } else { 173 sortMap.put(resultArtifact.getId(), new ArtifactWithWeight(resultArtifact)); 174 } 175 } 176 } 177 178 List<ArtifactWithWeight> artifacts = new ArrayList<>(sortMap.values()); 179 Collections.sort(artifacts); 180 181 for (ArtifactWithWeight item : artifacts) { 182 result.add(item.getArtifact()); 183 } 184 return result; 185 } 186 187 protected NuxeoArtifact resolveInTree(CoreSession session, String distribId, NuxeoArtifact matchingArtifact, 188 String searchedType) { 189 String cType = matchingArtifact.getArtifactType(); 190 if (cType.equals(searchedType)) { 191 return matchingArtifact; 192 } 193 BaseNuxeoArtifactDocAdapter docAdapter = (BaseNuxeoArtifactDocAdapter) matchingArtifact; 194 DocumentModel doc = docAdapter.getDoc(); 195 List<DocumentModel> parents = session.getParentDocuments(doc.getRef()); 196 Collections.reverse(parents); 197 for (DocumentModel parent : parents) { 198 if (parent.getType().equals(searchedType)) { 199 return mapDoc2Artifact(parent); 200 } 201 } 202 return null; 203 } 204 205 protected NuxeoArtifact resolveInTree(CoreSession session, String distribId, 206 DocumentationItem matchingDocumentationItem, String searchedType) { 207 DistributionSnapshot snap = Framework.getService(SnapshotManager.class).getSnapshot(distribId, session); 208 String targetId = matchingDocumentationItem.getTarget(); 209 String targetType = matchingDocumentationItem.getTargetType(); 210 NuxeoArtifact artifact; 211 switch (targetType) { 212 case BundleGroup.TYPE_NAME: 213 artifact = snap.getBundleGroup(targetId); 214 break; 215 case BundleInfo.TYPE_NAME: 216 artifact = snap.getBundle(targetId); 217 break; 218 case ComponentInfo.TYPE_NAME: 219 artifact = snap.getComponent(targetId); 220 break; 221 case ExtensionPointInfo.TYPE_NAME: 222 artifact = snap.getExtensionPoint(targetId); 223 break; 224 case ExtensionInfo.TYPE_NAME: 225 artifact = snap.getContribution(targetId); 226 break; 227 case ServiceInfo.TYPE_NAME: 228 artifact = snap.getService(targetId); 229 break; 230 default: 231 return null; 232 } 233 return resolveInTree(session, distribId, artifact, searchedType); 234 } 235 236}