Search in sources :

Example 16 with FullTextExpression

use of org.apache.jackrabbit.oak.query.fulltext.FullTextExpression in project jackrabbit-oak by apache.

the class LuceneIndex method query.

@Override
public Cursor query(final IndexPlan plan, NodeState rootState) {
    final Filter filter = plan.getFilter();
    FullTextExpression ft = filter.getFullTextConstraint();
    final Set<String> relPaths = getRelativePaths(ft);
    if (relPaths.size() > 1) {
        return new MultiLuceneIndex(filter, rootState, relPaths).query();
    }
    final String parent = relPaths.size() == 0 ? "" : relPaths.iterator().next();
    // we only restrict non-full-text conditions if there is
    // no relative property in the full-text constraint
    final boolean nonFullTextConstraints = parent.isEmpty();
    final int parentDepth = getDepth(parent);
    QueryEngineSettings settings = filter.getQueryEngineSettings();
    Iterator<LuceneResultRow> itr = new AbstractIterator<LuceneResultRow>() {

        private final Deque<LuceneResultRow> queue = Queues.newArrayDeque();

        private final Set<String> seenPaths = Sets.newHashSet();

        private ScoreDoc lastDoc;

        private int nextBatchSize = LUCENE_QUERY_BATCH_SIZE;

        private boolean noDocs = false;

        private long lastSearchIndexerVersion;

        private int reloadCount;

        @Override
        protected LuceneResultRow computeNext() {
            while (!queue.isEmpty() || loadDocs()) {
                return queue.remove();
            }
            return endOfData();
        }

        private LuceneResultRow convertToRow(ScoreDoc doc, IndexSearcher searcher, String excerpt) throws IOException {
            IndexReader reader = searcher.getIndexReader();
            PathStoredFieldVisitor visitor = new PathStoredFieldVisitor();
            reader.document(doc.doc, visitor);
            String path = visitor.getPath();
            if (path != null) {
                if ("".equals(path)) {
                    path = "/";
                }
                if (!parent.isEmpty()) {
                    // TODO OAK-828 this breaks node aggregation
                    // get the base path
                    // ensure the path ends with the given
                    // relative path
                    // if (!path.endsWith("/" + parent)) {
                    // continue;
                    // }
                    path = getAncestorPath(path, parentDepth);
                    // avoid duplicate entries
                    if (seenPaths.contains(path)) {
                        return null;
                    }
                    seenPaths.add(path);
                }
                return new LuceneResultRow(path, doc.score, excerpt);
            }
            return null;
        }

        /**
             * Loads the lucene documents in batches
             * @return true if any document is loaded
             */
        private boolean loadDocs() {
            if (noDocs) {
                return false;
            }
            ScoreDoc lastDocToRecord = null;
            IndexNode indexNode = tracker.acquireIndexNode((String) plan.getAttribute(ATTR_INDEX_PATH));
            checkState(indexNode != null);
            try {
                IndexSearcher searcher = indexNode.getSearcher();
                LuceneRequestFacade luceneRequestFacade = getLuceneRequest(filter, searcher.getIndexReader(), nonFullTextConstraints, indexNode.getDefinition());
                if (luceneRequestFacade.getLuceneRequest() instanceof Query) {
                    Query query = (Query) luceneRequestFacade.getLuceneRequest();
                    TopDocs docs;
                    long time = System.currentTimeMillis();
                    checkForIndexVersionChange(searcher);
                    while (true) {
                        if (lastDoc != null) {
                            LOG.debug("loading the next {} entries for query {}", nextBatchSize, query);
                            docs = searcher.searchAfter(lastDoc, query, nextBatchSize);
                        } else {
                            LOG.debug("loading the first {} entries for query {}", nextBatchSize, query);
                            docs = searcher.search(query, nextBatchSize);
                        }
                        time = System.currentTimeMillis() - time;
                        LOG.debug("... took {} ms", time);
                        nextBatchSize = (int) Math.min(nextBatchSize * 2L, 100000);
                        PropertyRestriction restriction = filter.getPropertyRestriction(QueryImpl.REP_EXCERPT);
                        boolean addExcerpt = restriction != null && restriction.isNotNullRestriction();
                        Analyzer analyzer = indexNode.getDefinition().getAnalyzer();
                        if (addExcerpt) {
                            // setup highlighter
                            QueryScorer scorer = new QueryScorer(query);
                            scorer.setExpandMultiTermQuery(true);
                            highlighter.setFragmentScorer(scorer);
                        }
                        for (ScoreDoc doc : docs.scoreDocs) {
                            String excerpt = null;
                            if (addExcerpt) {
                                excerpt = getExcerpt(analyzer, searcher, doc);
                            }
                            LuceneResultRow row = convertToRow(doc, searcher, excerpt);
                            if (row != null) {
                                queue.add(row);
                            }
                            lastDocToRecord = doc;
                        }
                        if (queue.isEmpty() && docs.scoreDocs.length > 0) {
                            lastDoc = lastDocToRecord;
                        } else {
                            break;
                        }
                    }
                } else if (luceneRequestFacade.getLuceneRequest() instanceof SpellcheckHelper.SpellcheckQuery) {
                    SpellcheckHelper.SpellcheckQuery spellcheckQuery = (SpellcheckHelper.SpellcheckQuery) luceneRequestFacade.getLuceneRequest();
                    noDocs = true;
                    SuggestWord[] suggestWords = SpellcheckHelper.getSpellcheck(spellcheckQuery);
                    // ACL filter spellchecks
                    Collection<String> suggestedWords = new ArrayList<String>(suggestWords.length);
                    QueryParser qp = new QueryParser(Version.LUCENE_47, FieldNames.SUGGEST, indexNode.getDefinition().getAnalyzer());
                    for (SuggestWord suggestion : suggestWords) {
                        Query query = qp.createPhraseQuery(FieldNames.SUGGEST, suggestion.string);
                        TopDocs topDocs = searcher.search(query, 100);
                        if (topDocs.totalHits > 0) {
                            for (ScoreDoc doc : topDocs.scoreDocs) {
                                Document retrievedDoc = searcher.doc(doc.doc);
                                if (filter.isAccessible(retrievedDoc.get(FieldNames.PATH))) {
                                    suggestedWords.add(suggestion.string);
                                    break;
                                }
                            }
                        }
                    }
                    queue.add(new LuceneResultRow(suggestedWords));
                } else if (luceneRequestFacade.getLuceneRequest() instanceof SuggestHelper.SuggestQuery) {
                    SuggestHelper.SuggestQuery suggestQuery = (SuggestHelper.SuggestQuery) luceneRequestFacade.getLuceneRequest();
                    noDocs = true;
                    List<Lookup.LookupResult> lookupResults = SuggestHelper.getSuggestions(indexNode.getLookup(), suggestQuery);
                    // ACL filter suggestions
                    Collection<String> suggestedWords = new ArrayList<String>(lookupResults.size());
                    QueryParser qp = new QueryParser(Version.LUCENE_47, FieldNames.FULLTEXT, indexNode.getDefinition().getAnalyzer());
                    for (Lookup.LookupResult suggestion : lookupResults) {
                        Query query = qp.createPhraseQuery(FieldNames.FULLTEXT, suggestion.key.toString());
                        TopDocs topDocs = searcher.search(query, 100);
                        if (topDocs.totalHits > 0) {
                            for (ScoreDoc doc : topDocs.scoreDocs) {
                                Document retrievedDoc = searcher.doc(doc.doc);
                                if (filter.isAccessible(retrievedDoc.get(FieldNames.PATH))) {
                                    suggestedWords.add("{term=" + suggestion.key + ",weight=" + suggestion.value + "}");
                                    break;
                                }
                            }
                        }
                    }
                    queue.add(new LuceneResultRow(suggestedWords));
                }
            } catch (IOException e) {
                LOG.warn("query via {} failed.", LuceneIndex.this, e);
            } finally {
                indexNode.release();
            }
            if (lastDocToRecord != null) {
                this.lastDoc = lastDocToRecord;
            }
            return !queue.isEmpty();
        }

        private void checkForIndexVersionChange(IndexSearcher searcher) {
            long currentVersion = LucenePropertyIndex.getVersion(searcher);
            if (currentVersion != lastSearchIndexerVersion && lastDoc != null) {
                reloadCount++;
                if (reloadCount > MAX_RELOAD_COUNT) {
                    LOG.error("More than {} index version changes detected for query {}", MAX_RELOAD_COUNT, plan);
                    throw new IllegalStateException("Too many version changes");
                }
                lastDoc = null;
                LOG.debug("Change in index version detected {} => {}. Query would be performed without " + "offset; reload {}", currentVersion, lastSearchIndexerVersion, reloadCount);
            }
            this.lastSearchIndexerVersion = currentVersion;
        }
    };
    SizeEstimator sizeEstimator = new SizeEstimator() {

        @Override
        public long getSize() {
            IndexNode indexNode = tracker.acquireIndexNode((String) plan.getAttribute(ATTR_INDEX_PATH));
            checkState(indexNode != null);
            try {
                IndexSearcher searcher = indexNode.getSearcher();
                LuceneRequestFacade luceneRequestFacade = getLuceneRequest(filter, searcher.getIndexReader(), nonFullTextConstraints, indexNode.getDefinition());
                if (luceneRequestFacade.getLuceneRequest() instanceof Query) {
                    Query query = (Query) luceneRequestFacade.getLuceneRequest();
                    TotalHitCountCollector collector = new TotalHitCountCollector();
                    searcher.search(query, collector);
                    int totalHits = collector.getTotalHits();
                    LOG.debug("Estimated size for query {} is {}", query, totalHits);
                    return totalHits;
                }
                LOG.debug("Estimated size: not a Query: {}", luceneRequestFacade.getLuceneRequest());
            } catch (IOException e) {
                LOG.warn("query via {} failed.", LuceneIndex.this, e);
            } finally {
                indexNode.release();
            }
            return -1;
        }
    };
    return new LucenePathCursor(itr, settings, sizeEstimator);
}
Also used : IndexSearcher(org.apache.lucene.search.IndexSearcher) Set(java.util.Set) HashSet(java.util.HashSet) Query(org.apache.lucene.search.Query) PhraseQuery(org.apache.lucene.search.PhraseQuery) PrefixQuery(org.apache.lucene.search.PrefixQuery) MatchAllDocsQuery(org.apache.lucene.search.MatchAllDocsQuery) WildcardQuery(org.apache.lucene.search.WildcardQuery) TermQuery(org.apache.lucene.search.TermQuery) BooleanQuery(org.apache.lucene.search.BooleanQuery) TermRangeQuery(org.apache.lucene.search.TermRangeQuery) QueryEngineSettings(org.apache.jackrabbit.oak.query.QueryEngineSettings) ArrayList(java.util.ArrayList) Analyzer(org.apache.lucene.analysis.Analyzer) Document(org.apache.lucene.document.Document) ScoreDoc(org.apache.lucene.search.ScoreDoc) TopDocs(org.apache.lucene.search.TopDocs) Lookup(org.apache.lucene.search.suggest.Lookup) TotalHitCountCollector(org.apache.lucene.search.TotalHitCountCollector) AbstractIterator(com.google.common.collect.AbstractIterator) PropertyRestriction(org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction) QueryScorer(org.apache.lucene.search.highlight.QueryScorer) SuggestHelper(org.apache.jackrabbit.oak.plugins.index.lucene.util.SuggestHelper) IOException(java.io.IOException) Deque(java.util.Deque) QueryParser(org.apache.lucene.queryparser.classic.QueryParser) Filter(org.apache.jackrabbit.oak.spi.query.Filter) FullTextExpression(org.apache.jackrabbit.oak.query.fulltext.FullTextExpression) IndexReader(org.apache.lucene.index.IndexReader) SuggestWord(org.apache.lucene.search.spell.SuggestWord) SpellcheckHelper(org.apache.jackrabbit.oak.plugins.index.lucene.util.SpellcheckHelper) Collection(java.util.Collection)

Example 17 with FullTextExpression

use of org.apache.jackrabbit.oak.query.fulltext.FullTextExpression in project jackrabbit-oak by apache.

the class LuceneIndex method getFullTextQuery.

static Query getFullTextQuery(FullTextExpression ft, final Analyzer analyzer, final IndexReader reader) {
    // a reference to the query, so it can be set in the visitor
    // (a "non-local return")
    final AtomicReference<Query> result = new AtomicReference<Query>();
    ft.accept(new FullTextVisitor() {

        @Override
        public boolean visit(FullTextContains contains) {
            return contains.getBase().accept(this);
        }

        @Override
        public boolean visit(FullTextOr or) {
            BooleanQuery q = new BooleanQuery();
            for (FullTextExpression e : or.list) {
                Query x = getFullTextQuery(e, analyzer, reader);
                q.add(x, SHOULD);
            }
            result.set(q);
            return true;
        }

        @Override
        public boolean visit(FullTextAnd and) {
            BooleanQuery q = new BooleanQuery();
            for (FullTextExpression e : and.list) {
                Query x = getFullTextQuery(e, analyzer, reader);
                /* Only unwrap the clause if MUST_NOT(x) */
                boolean hasMustNot = false;
                if (x instanceof BooleanQuery) {
                    BooleanQuery bq = (BooleanQuery) x;
                    if ((bq.getClauses().length == 1) && (bq.getClauses()[0].getOccur() == BooleanClause.Occur.MUST_NOT)) {
                        hasMustNot = true;
                        q.add(bq.getClauses()[0]);
                    }
                }
                if (!hasMustNot) {
                    q.add(x, MUST);
                }
            }
            result.set(q);
            return true;
        }

        @Override
        public boolean visit(FullTextTerm term) {
            return visitTerm(term.getPropertyName(), term.getText(), term.getBoost(), term.isNot());
        }

        private boolean visitTerm(String propertyName, String text, String boost, boolean not) {
            String p = propertyName;
            if (p != null && p.indexOf('/') >= 0) {
                p = getName(p);
            }
            Query q = tokenToQuery(text, p, analyzer, reader);
            if (q == null) {
                return false;
            }
            if (boost != null) {
                q.setBoost(Float.parseFloat(boost));
            }
            if (not) {
                BooleanQuery bq = new BooleanQuery();
                bq.add(q, MUST_NOT);
                result.set(bq);
            } else {
                result.set(q);
            }
            return true;
        }
    });
    return result.get();
}
Also used : BooleanQuery(org.apache.lucene.search.BooleanQuery) Query(org.apache.lucene.search.Query) PhraseQuery(org.apache.lucene.search.PhraseQuery) PrefixQuery(org.apache.lucene.search.PrefixQuery) MatchAllDocsQuery(org.apache.lucene.search.MatchAllDocsQuery) WildcardQuery(org.apache.lucene.search.WildcardQuery) TermQuery(org.apache.lucene.search.TermQuery) BooleanQuery(org.apache.lucene.search.BooleanQuery) TermRangeQuery(org.apache.lucene.search.TermRangeQuery) FullTextOr(org.apache.jackrabbit.oak.query.fulltext.FullTextOr) FullTextContains(org.apache.jackrabbit.oak.query.fulltext.FullTextContains) AtomicReference(java.util.concurrent.atomic.AtomicReference) FullTextVisitor(org.apache.jackrabbit.oak.query.fulltext.FullTextVisitor) FullTextTerm(org.apache.jackrabbit.oak.query.fulltext.FullTextTerm) FullTextAnd(org.apache.jackrabbit.oak.query.fulltext.FullTextAnd) FullTextExpression(org.apache.jackrabbit.oak.query.fulltext.FullTextExpression)

Example 18 with FullTextExpression

use of org.apache.jackrabbit.oak.query.fulltext.FullTextExpression in project jackrabbit-oak by apache.

the class LuceneIndex method getLuceneRequest.

/**
     * Get the Lucene query for the given filter.
     *
     * @param filter the filter, including full-text constraint
     * @param reader the Lucene reader
     * @param nonFullTextConstraints whether non-full-text constraints (such a
     *            path, node type, and so on) should be added to the Lucene
     *            query
     * @param indexDefinition nodestate that contains the index definition
     * @return the Lucene query
     */
private static LuceneRequestFacade getLuceneRequest(Filter filter, IndexReader reader, boolean nonFullTextConstraints, IndexDefinition indexDefinition) {
    List<Query> qs = new ArrayList<Query>();
    Analyzer analyzer = indexDefinition.getAnalyzer();
    FullTextExpression ft = filter.getFullTextConstraint();
    if (ft == null) {
    // there might be no full-text constraint
    // when using the LowCostLuceneIndexProvider
    // which is used for testing
    } else {
        qs.add(getFullTextQuery(ft, analyzer, reader));
    }
    PropertyRestriction pr = filter.getPropertyRestriction(NATIVE_QUERY_FUNCTION);
    if (pr != null) {
        String query = String.valueOf(pr.first.getValue(pr.first.getType()));
        QueryParser queryParser = new QueryParser(VERSION, "", indexDefinition.getAnalyzer());
        if (query.startsWith("mlt?")) {
            String mltQueryString = query.replace("mlt?", "");
            if (reader != null) {
                Query moreLikeThis = MoreLikeThisHelper.getMoreLikeThis(reader, analyzer, mltQueryString);
                if (moreLikeThis != null) {
                    qs.add(moreLikeThis);
                }
            }
        }
        if (query.startsWith("spellcheck?")) {
            String spellcheckQueryString = query.replace("spellcheck?", "");
            if (reader != null) {
                return new LuceneRequestFacade<SpellcheckHelper.SpellcheckQuery>(SpellcheckHelper.getSpellcheckQuery(spellcheckQueryString, reader));
            }
        } else if (query.startsWith("suggest?")) {
            String suggestQueryString = query.replace("suggest?", "");
            if (reader != null) {
                return new LuceneRequestFacade<SuggestHelper.SuggestQuery>(SuggestHelper.getSuggestQuery(suggestQueryString));
            }
        } else {
            try {
                qs.add(queryParser.parse(query));
            } catch (ParseException e) {
                throw new RuntimeException(e);
            }
        }
    } else if (nonFullTextConstraints) {
        addNonFullTextConstraints(qs, filter, reader, analyzer, indexDefinition);
    }
    if (qs.size() == 0) {
        return new LuceneRequestFacade<Query>(new MatchAllDocsQuery());
    }
    return LucenePropertyIndex.performAdditionalWraps(qs);
}
Also used : PropertyRestriction(org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction) Query(org.apache.lucene.search.Query) PhraseQuery(org.apache.lucene.search.PhraseQuery) PrefixQuery(org.apache.lucene.search.PrefixQuery) MatchAllDocsQuery(org.apache.lucene.search.MatchAllDocsQuery) WildcardQuery(org.apache.lucene.search.WildcardQuery) TermQuery(org.apache.lucene.search.TermQuery) BooleanQuery(org.apache.lucene.search.BooleanQuery) TermRangeQuery(org.apache.lucene.search.TermRangeQuery) ArrayList(java.util.ArrayList) Analyzer(org.apache.lucene.analysis.Analyzer) MatchAllDocsQuery(org.apache.lucene.search.MatchAllDocsQuery) QueryParser(org.apache.lucene.queryparser.classic.QueryParser) FullTextExpression(org.apache.jackrabbit.oak.query.fulltext.FullTextExpression) SpellcheckHelper(org.apache.jackrabbit.oak.plugins.index.lucene.util.SpellcheckHelper) ParseException(org.apache.lucene.queryparser.classic.ParseException)

Example 19 with FullTextExpression

use of org.apache.jackrabbit.oak.query.fulltext.FullTextExpression in project jackrabbit-oak by apache.

the class FilterQueryParser method parseFullTextExpression.

private static String parseFullTextExpression(FullTextExpression ft, final OakSolrConfiguration configuration) {
    final StringBuilder fullTextString = new StringBuilder();
    ft.accept(new FullTextVisitor() {

        @Override
        public boolean visit(FullTextOr or) {
            fullTextString.append('(');
            for (int i = 0; i < or.list.size(); i++) {
                if (i > 0 && i < or.list.size()) {
                    fullTextString.append(" OR ");
                }
                FullTextExpression e = or.list.get(i);
                String orTerm = parseFullTextExpression(e, configuration);
                fullTextString.append(orTerm);
            }
            fullTextString.append(')');
            fullTextString.append(' ');
            return true;
        }

        @Override
        public boolean visit(FullTextContains contains) {
            return contains.getBase().accept(this);
        }

        @Override
        public boolean visit(FullTextAnd and) {
            fullTextString.append('(');
            for (int i = 0; i < and.list.size(); i++) {
                if (i > 0 && i < and.list.size()) {
                    fullTextString.append(" AND ");
                }
                FullTextExpression e = and.list.get(i);
                String andTerm = parseFullTextExpression(e, configuration);
                fullTextString.append(andTerm);
            }
            fullTextString.append(')');
            fullTextString.append(' ');
            return true;
        }

        @Override
        public boolean visit(FullTextTerm term) {
            if (term.isNot()) {
                fullTextString.append('-');
            }
            String p = term.getPropertyName();
            if (p != null && p.indexOf('/') >= 0) {
                p = getName(p);
            }
            if (p == null || "*".equals(p)) {
                p = configuration.getCatchAllField();
            }
            fullTextString.append(partialEscape(p));
            fullTextString.append(':');
            String termText = term.getText();
            if (termText.indexOf(' ') > 0) {
                fullTextString.append('"');
            }
            fullTextString.append(termText.replace("/", "\\/").replace(":", "\\:"));
            if (termText.indexOf(' ') > 0) {
                fullTextString.append('"');
            }
            String boost = term.getBoost();
            if (boost != null) {
                fullTextString.append('^');
                fullTextString.append(boost);
            }
            fullTextString.append(' ');
            return true;
        }
    });
    return fullTextString.toString();
}
Also used : FullTextVisitor(org.apache.jackrabbit.oak.query.fulltext.FullTextVisitor) FullTextTerm(org.apache.jackrabbit.oak.query.fulltext.FullTextTerm) FullTextOr(org.apache.jackrabbit.oak.query.fulltext.FullTextOr) FullTextContains(org.apache.jackrabbit.oak.query.fulltext.FullTextContains) FullTextAnd(org.apache.jackrabbit.oak.query.fulltext.FullTextAnd) FullTextExpression(org.apache.jackrabbit.oak.query.fulltext.FullTextExpression)

Example 20 with FullTextExpression

use of org.apache.jackrabbit.oak.query.fulltext.FullTextExpression in project jackrabbit-oak by apache.

the class FilterQueryParser method getQuery.

static SolrQuery getQuery(Filter filter, QueryIndex.IndexPlan plan, OakSolrConfiguration configuration) {
    SolrQuery solrQuery = new SolrQuery();
    setDefaults(solrQuery, configuration);
    StringBuilder queryBuilder = new StringBuilder();
    FullTextExpression ft = filter.getFullTextConstraint();
    if (ft != null) {
        queryBuilder.append(parseFullTextExpression(ft, configuration));
        queryBuilder.append(' ');
    } else if (filter.getFulltextConditions() != null) {
        Collection<String> fulltextConditions = filter.getFulltextConditions();
        for (String fulltextCondition : fulltextConditions) {
            queryBuilder.append(fulltextCondition).append(" ");
        }
    }
    List<QueryIndex.OrderEntry> sortOrder = plan.getSortOrder();
    if (sortOrder != null) {
        for (QueryIndex.OrderEntry orderEntry : sortOrder) {
            SolrQuery.ORDER order;
            if (QueryIndex.OrderEntry.Order.ASCENDING.equals(orderEntry.getOrder())) {
                order = SolrQuery.ORDER.asc;
            } else {
                order = SolrQuery.ORDER.desc;
            }
            String sortingField;
            if (JcrConstants.JCR_PATH.equals(orderEntry.getPropertyName())) {
                sortingField = partialEscape(configuration.getPathField()).toString();
            } else if (JcrConstants.JCR_SCORE.equals(orderEntry.getPropertyName())) {
                sortingField = "score";
            } else {
                if (orderEntry.getPropertyName().indexOf('/') >= 0) {
                    log.warn("cannot sort on relative properties, ignoring {} clause", orderEntry);
                    // sorting by relative properties not supported until index time aggregation is supported
                    continue;
                }
                sortingField = partialEscape(getSortingField(orderEntry.getPropertyType().tag(), orderEntry.getPropertyName())).toString();
            }
            solrQuery.addOrUpdateSort(sortingField, order);
        }
    }
    Collection<Filter.PropertyRestriction> propertyRestrictions = filter.getPropertyRestrictions();
    if (propertyRestrictions != null && !propertyRestrictions.isEmpty()) {
        for (Filter.PropertyRestriction pr : propertyRestrictions) {
            if (pr.isNullRestriction()) {
                // can not use full "x is null"
                continue;
            }
            // facets
            if (QueryImpl.REP_FACET.equals(pr.propertyName)) {
                solrQuery.setFacetMinCount(1);
                solrQuery.setFacet(true);
                String value = pr.first.getValue(Type.STRING);
                solrQuery.addFacetField(value.substring(QueryImpl.REP_FACET.length() + 1, value.length() - 1) + "_facet");
            }
            // native query support
            if (SolrQueryIndex.NATIVE_SOLR_QUERY.equals(pr.propertyName) || SolrQueryIndex.NATIVE_LUCENE_QUERY.equals(pr.propertyName)) {
                String nativeQueryString = String.valueOf(pr.first.getValue(pr.first.getType()));
                if (isSupportedHttpRequest(nativeQueryString)) {
                    // pass through the native HTTP Solr request
                    String requestHandlerString = nativeQueryString.substring(0, nativeQueryString.indexOf('?'));
                    if (!"select".equals(requestHandlerString)) {
                        if (requestHandlerString.charAt(0) != '/') {
                            requestHandlerString = "/" + requestHandlerString;
                        }
                        solrQuery.setRequestHandler(requestHandlerString);
                    }
                    String parameterString = nativeQueryString.substring(nativeQueryString.indexOf('?') + 1);
                    for (String param : parameterString.split("&")) {
                        String[] kv = param.split("=");
                        if (kv.length != 2) {
                            throw new RuntimeException("Unparsable native HTTP Solr query");
                        } else {
                            // more like this
                            if ("/mlt".equals(requestHandlerString)) {
                                if ("stream.body".equals(kv[0])) {
                                    kv[0] = "q";
                                    String mltFlString = "mlt.fl=";
                                    int mltFlIndex = parameterString.indexOf(mltFlString);
                                    if (mltFlIndex > -1) {
                                        int beginIndex = mltFlIndex + mltFlString.length();
                                        int endIndex = parameterString.indexOf('&', beginIndex);
                                        String fields;
                                        if (endIndex > beginIndex) {
                                            fields = parameterString.substring(beginIndex, endIndex);
                                        } else {
                                            fields = parameterString.substring(beginIndex);
                                        }
                                        kv[1] = "_query_:\"{!dismax qf=" + fields + " q.op=OR}" + kv[1] + "\"";
                                    }
                                }
                                if ("mlt.fl".equals(kv[0]) && ":path".equals(kv[1])) {
                                    // rep:similar passes the path of the node to find similar documents for in the :path
                                    // but needs its indexed content to find similar documents
                                    kv[1] = configuration.getCatchAllField();
                                }
                            }
                            if ("/spellcheck".equals(requestHandlerString)) {
                                if ("term".equals(kv[0])) {
                                    kv[0] = "spellcheck.q";
                                }
                                solrQuery.setParam("spellcheck", true);
                            }
                            if ("/suggest".equals(requestHandlerString)) {
                                if ("term".equals(kv[0])) {
                                    kv[0] = "suggest.q";
                                }
                                solrQuery.setParam("suggest", true);
                            }
                            solrQuery.setParam(kv[0], kv[1]);
                        }
                    }
                    return solrQuery;
                } else {
                    queryBuilder.append(nativeQueryString);
                }
            } else {
                if (SolrQueryIndex.isIgnoredProperty(pr, configuration)) {
                    continue;
                }
                String first = null;
                if (pr.first != null) {
                    first = partialEscape(String.valueOf(pr.first.getValue(pr.first.getType()))).toString();
                }
                String last = null;
                if (pr.last != null) {
                    last = partialEscape(String.valueOf(pr.last.getValue(pr.last.getType()))).toString();
                }
                String prField = configuration.getFieldForPropertyRestriction(pr);
                CharSequence fieldName = partialEscape(prField != null ? prField : pr.propertyName);
                if ("jcr\\:path".equals(fieldName.toString())) {
                    queryBuilder.append(configuration.getPathField());
                    queryBuilder.append(':');
                    queryBuilder.append(first);
                } else {
                    if (pr.first != null && pr.last != null && pr.first.equals(pr.last)) {
                        queryBuilder.append(fieldName).append(':');
                        queryBuilder.append(first);
                    } else if (pr.first == null && pr.last == null) {
                        if (!queryBuilder.toString().contains(fieldName + ":")) {
                            queryBuilder.append(fieldName).append(':');
                            queryBuilder.append('*');
                        }
                    } else if ((pr.first != null && pr.last == null) || (pr.last != null && pr.first == null) || (!pr.first.equals(pr.last))) {
                        // TODO : need to check if this works for all field types (most likely not!)
                        queryBuilder.append(fieldName).append(':');
                        queryBuilder.append(createRangeQuery(first, last, pr.firstIncluding, pr.lastIncluding));
                    } else if (pr.isLike) {
                        // TODO : the current parameter substitution is not expected to work well
                        queryBuilder.append(fieldName).append(':');
                        queryBuilder.append(partialEscape(String.valueOf(pr.first.getValue(pr.first.getType())).replace('%', '*').replace('_', '?')));
                    } else {
                        throw new RuntimeException("[unexpected!] not handled case");
                    }
                }
            }
            queryBuilder.append(" ");
        }
    }
    if (configuration.useForPrimaryTypes()) {
        String[] pts = filter.getPrimaryTypes().toArray(new String[filter.getPrimaryTypes().size()]);
        StringBuilder ptQueryBuilder = new StringBuilder();
        for (int i = 0; i < pts.length; i++) {
            String pt = pts[i];
            if (i == 0) {
                ptQueryBuilder.append("(");
            }
            if (i > 0 && i < pts.length) {
                ptQueryBuilder.append("OR ");
            }
            ptQueryBuilder.append("jcr\\:primaryType").append(':').append(partialEscape(pt)).append(" ");
            if (i == pts.length - 1) {
                ptQueryBuilder.append(")");
                ptQueryBuilder.append(' ');
            }
        }
        solrQuery.addFilterQuery(ptQueryBuilder.toString());
    }
    if (filter.getQueryStatement() != null && filter.getQueryStatement().contains(QueryImpl.REP_EXCERPT)) {
        if (!solrQuery.getHighlight()) {
            // enable highlighting
            solrQuery.setHighlight(true);
            // defaults
            solrQuery.set("hl.fl", "*");
            solrQuery.set("hl.encoder", "html");
            solrQuery.set("hl.mergeContiguous", true);
            solrQuery.setHighlightSimplePre("<strong>");
            solrQuery.setHighlightSimplePost("</strong>");
        }
    }
    if (configuration.useForPathRestrictions()) {
        Filter.PathRestriction pathRestriction = filter.getPathRestriction();
        if (pathRestriction != null) {
            String path = purgePath(filter, plan.getPathPrefix());
            String fieldName = configuration.getFieldForPathRestriction(pathRestriction);
            if (fieldName != null) {
                if (pathRestriction.equals(Filter.PathRestriction.ALL_CHILDREN)) {
                    solrQuery.addFilterQuery(fieldName + ':' + path);
                } else {
                    queryBuilder.append(fieldName);
                    queryBuilder.append(':');
                    queryBuilder.append(path);
                }
            }
        }
    }
    if (configuration.collapseJcrContentNodes()) {
        solrQuery.addFilterQuery("{!collapse field=" + configuration.getCollapsedPathField() + " min=" + configuration.getPathDepthField() + " hint=top_fc nullPolicy=expand}");
    }
    if (queryBuilder.length() == 0) {
        queryBuilder.append("*:*");
    }
    String escapedQuery = queryBuilder.toString();
    solrQuery.setQuery(escapedQuery);
    if (log.isDebugEnabled()) {
        log.debug("JCR query {} has been converted to Solr query {}", filter.getQueryStatement(), solrQuery.toString());
    }
    return solrQuery;
}
Also used : SolrQuery(org.apache.solr.client.solrj.SolrQuery) Filter(org.apache.jackrabbit.oak.spi.query.Filter) FullTextExpression(org.apache.jackrabbit.oak.query.fulltext.FullTextExpression) Collection(java.util.Collection) QueryIndex(org.apache.jackrabbit.oak.spi.query.QueryIndex)

Aggregations

FullTextExpression (org.apache.jackrabbit.oak.query.fulltext.FullTextExpression)20 Filter (org.apache.jackrabbit.oak.spi.query.Filter)7 FullTextAnd (org.apache.jackrabbit.oak.query.fulltext.FullTextAnd)6 FullTextContains (org.apache.jackrabbit.oak.query.fulltext.FullTextContains)6 FullTextTerm (org.apache.jackrabbit.oak.query.fulltext.FullTextTerm)5 BooleanQuery (org.apache.lucene.search.BooleanQuery)5 MatchAllDocsQuery (org.apache.lucene.search.MatchAllDocsQuery)5 PrefixQuery (org.apache.lucene.search.PrefixQuery)5 Query (org.apache.lucene.search.Query)5 TermQuery (org.apache.lucene.search.TermQuery)5 TermRangeQuery (org.apache.lucene.search.TermRangeQuery)5 WildcardQuery (org.apache.lucene.search.WildcardQuery)5 FullTextOr (org.apache.jackrabbit.oak.query.fulltext.FullTextOr)4 FullTextVisitor (org.apache.jackrabbit.oak.query.fulltext.FullTextVisitor)4 PropertyRestriction (org.apache.jackrabbit.oak.spi.query.Filter.PropertyRestriction)4 Test (org.junit.Test)4 ArrayList (java.util.ArrayList)3 AtomicReference (java.util.concurrent.atomic.AtomicReference)3 NodeBuilder (org.apache.jackrabbit.oak.spi.state.NodeBuilder)3 Analyzer (org.apache.lucene.analysis.Analyzer)3