001/*
002 * (C) Copyright 2006-2010 Nuxeo SA (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.lang.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        if (doc.getType().equals(BundleGroup.TYPE_NAME)) {
063            artifact = new BundleGroupDocAdapter(doc);
064        } else if (doc.getType().equals(BundleInfo.TYPE_NAME)) {
065            artifact = new BundleInfoDocAdapter(doc);
066        } else if (doc.getType().equals(ComponentInfo.TYPE_NAME)) {
067            artifact = new ComponentInfoDocAdapter(doc);
068        } else if (doc.getType().equals(ExtensionPointInfo.TYPE_NAME)) {
069            artifact = new ExtensionPointInfoDocAdapter(doc);
070        } else if (doc.getType().equals(ExtensionInfo.TYPE_NAME)) {
071            artifact = new ExtensionInfoDocAdapter(doc);
072        } else if (doc.getType().equals(DistributionSnapshot.TYPE_NAME)) {
073            artifact = new RepositoryDistributionSnapshot(doc);
074        } else if (doc.getType().equals(ServiceInfo.TYPE_NAME)) {
075            artifact = new ServiceInfoDocAdapter(doc);
076        }
077
078        return artifact;
079    }
080
081    @Override
082    public List<NuxeoArtifact> searchArtifact(CoreSession session, String distribId, String fulltext) {
083        List<NuxeoArtifact> result = new ArrayList<>();
084
085        DistributionSnapshot snap = Framework.getService(SnapshotManager.class).getSnapshot(distribId, session);
086        if (!(snap instanceof RepositoryDistributionSnapshot)) {
087            return Collections.emptyList();
088        }
089
090        DocumentModel dist = ((RepositoryDistributionSnapshot) snap).getDoc();
091        StrBuilder q = new StrBuilder("SELECT * FROM Document WHERE ");
092        q.append("ecm:path STARTSWITH '").append(dist.getPathAsString()).append("'");
093        String query = q.toString();
094        if (fulltext != null) {
095            query += " AND " + NXQL.ECM_FULLTEXT + " = " + NXQL.escapeString(fulltext);
096        }
097
098        ElasticSearchService ess = Framework.getService(ElasticSearchService.class);
099        DocumentModelList docs = ess.query(new NxQueryBuilder(session).nxql(query).limit(MAX_RESULTS));
100        for (DocumentModel doc : docs) {
101            NuxeoArtifact artifact = mapDoc2Artifact(doc);
102            if (artifact != null) {
103                result.add(artifact);
104            }
105        }
106        return result;
107    }
108
109    @Override
110    public List<DocumentationItem> searchDocumentation(CoreSession session, String distribId, String fulltext,
111            String targetType) {
112        DistributionSnapshot snap = Framework.getService(SnapshotManager.class).getSnapshot(distribId, session);
113        DocumentModel dist = ((RepositoryDistributionSnapshot) snap).getDoc();
114        String query = QueryHelper.select(DocumentationItem.TYPE_NAME, dist, NXQL.ECM_FULLTEXT, fulltext);
115        if (targetType != null) {
116            query += " AND " + DocumentationItem.PROP_TARGET_TYPE + " = " + NXQL.escapeString(targetType);
117        }
118
119        ElasticSearchService ess = Framework.getService(ElasticSearchService.class);
120        DocumentModelList docs = ess.query(new NxQueryBuilder(session).nxql(query).limit(MAX_RESULTS));
121        List<DocumentationItem> result = new ArrayList<>();
122        for (DocumentModel doc : docs) {
123            DocumentationItem docItem = doc.getAdapter(DocumentationItem.class);
124            if (docItem != null) {
125                result.add(docItem);
126            }
127        }
128        return result;
129    }
130
131    @Override
132    public List<NuxeoArtifact> filterArtifact(CoreSession session, String distribId, String type, String fulltext) {
133        List<NuxeoArtifact> result = new ArrayList<>();
134
135        List<NuxeoArtifact> matchingArtifacts = searchArtifact(session, distribId, fulltext);
136        List<DocumentationItem> matchingDocumentationItems = searchDocumentation(session, distribId, fulltext, null);
137
138        Map<String, ArtifactWithWeight> sortMap = new HashMap<>();
139
140        for (NuxeoArtifact matchingArtifact : matchingArtifacts) {
141            ArtifactWithWeight artifactWithWeight;
142            NuxeoArtifact matchingParentArtifact = resolveInTree(session, distribId, matchingArtifact, type);
143            if (matchingParentArtifact != null) {
144                artifactWithWeight = new ArtifactWithWeight(matchingParentArtifact);
145            } else if (matchingArtifact.getArtifactType().equals(type)) {
146                artifactWithWeight = new ArtifactWithWeight(matchingArtifact);
147            } else {
148                continue;
149            }
150
151            String id = artifactWithWeight.getArtifact().getId();
152            if (sortMap.containsKey(id)) {
153                sortMap.get(id).addHit();
154            } else {
155                sortMap.put(id, new ArtifactWithWeight(matchingParentArtifact));
156            }
157        }
158
159        for (DocumentationItem matchingDocumentationItem : matchingDocumentationItems) {
160            NuxeoArtifact resultArtifact = resolveInTree(session, distribId, matchingDocumentationItem, type);
161            if (resultArtifact != null) {
162                if (sortMap.containsKey(resultArtifact.getId())) {
163                    sortMap.get(resultArtifact.getId()).addHit();
164                } else {
165                    sortMap.put(resultArtifact.getId(), new ArtifactWithWeight(resultArtifact));
166                }
167            }
168        }
169
170        List<ArtifactWithWeight> artifacts = new ArrayList<>(sortMap.values());
171        Collections.sort(artifacts);
172
173        for (ArtifactWithWeight item : artifacts) {
174            result.add(item.getArtifact());
175        }
176        return result;
177    }
178
179    protected NuxeoArtifact resolveInTree(CoreSession session, String distribId, NuxeoArtifact matchingArtifact,
180            String searchedType) {
181        String cType = matchingArtifact.getArtifactType();
182        if (cType.equals(searchedType)) {
183            return matchingArtifact;
184        }
185        BaseNuxeoArtifactDocAdapter docAdapter = (BaseNuxeoArtifactDocAdapter) matchingArtifact;
186        DocumentModel doc = docAdapter.getDoc();
187        List<DocumentModel> parents = session.getParentDocuments(doc.getRef());
188        Collections.reverse(parents);
189        for (DocumentModel parent : parents) {
190            if (parent.getType().equals(searchedType)) {
191                return mapDoc2Artifact(parent);
192            }
193        }
194        return null;
195    }
196
197    protected NuxeoArtifact resolveInTree(CoreSession session, String distribId,
198            DocumentationItem matchingDocumentationItem, String searchedType) {
199        DistributionSnapshot snap = Framework.getService(SnapshotManager.class).getSnapshot(distribId, session);
200        String targetId = matchingDocumentationItem.getTarget();
201        String targetType = matchingDocumentationItem.getTargetType();
202        NuxeoArtifact artifact;
203        if (targetType.equals(BundleGroup.TYPE_NAME)) {
204            artifact = snap.getBundleGroup(targetId);
205        } else if (targetType.equals(BundleInfo.TYPE_NAME)) {
206            artifact = snap.getBundle(targetId);
207        } else if (targetType.equals(ComponentInfo.TYPE_NAME)) {
208            artifact = snap.getComponent(targetId);
209        } else if (targetType.equals(ExtensionPointInfo.TYPE_NAME)) {
210            artifact = snap.getExtensionPoint(targetId);
211        } else if (targetType.equals(ExtensionInfo.TYPE_NAME)) {
212            artifact = snap.getContribution(targetId);
213        } else if (targetType.equals(ServiceInfo.TYPE_NAME)) {
214            artifact = snap.getService(targetId);
215        } else {
216            artifact = null;
217        }
218        return resolveInTree(session, distribId, artifact, searchedType);
219    }
220
221}