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