Package org.apache.jackrabbit.oak.plugins.index.solr.query

Source Code of org.apache.jackrabbit.oak.plugins.index.solr.query.SolrQueryIndex

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*     http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.oak.plugins.index.solr.query;

import java.util.Collection;

import org.apache.jackrabbit.oak.api.PropertyValue;
import org.apache.jackrabbit.oak.plugins.index.solr.configuration.OakSolrConfiguration;
import org.apache.jackrabbit.oak.spi.query.Cursor;
import org.apache.jackrabbit.oak.spi.query.Filter;
import org.apache.jackrabbit.oak.spi.query.IndexRow;
import org.apache.jackrabbit.oak.spi.query.PropertyValues;
import org.apache.jackrabbit.oak.spi.query.QueryIndex;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.solr.client.solrj.SolrQuery;
import org.apache.solr.client.solrj.SolrServer;
import org.apache.solr.client.solrj.response.QueryResponse;
import org.apache.solr.common.SolrDocument;
import org.apache.solr.common.SolrDocumentList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* A Solr based {@link QueryIndex}
*/
public class SolrQueryIndex implements QueryIndex {

    private static final Logger log = LoggerFactory.getLogger(SolrQueryIndex.class);
    public static final String TYPE = "solr";

    private final String name;
    private final SolrServer solrServer;
    private final OakSolrConfiguration configuration;

    public SolrQueryIndex(String name, SolrServer solrServer, OakSolrConfiguration configuration) {
        this.name = name;
        this.solrServer = solrServer;
        this.configuration = configuration;
    }

    @Override
    public String getIndexName() {
        return name;
    }

    @Override
    public double getCost(Filter filter, NodeState root) {
        // TODO : estimate no of returned values and 0 is not good for no restrictions
        return (filter.getPropertyRestrictions() != null ? filter.getPropertyRestrictions().size() * 0.1 : 0)
                + (filter.getFulltextConditions() != null ? filter.getFulltextConditions().size() * 0.01 : 0)
                + (filter.getPathRestriction() != null ? 0.2 : 0);
    }

    @Override
    public String getPlan(Filter filter, NodeState nodeState) {
        return getQuery(filter).toString();
    }

    private SolrQuery getQuery(Filter filter) {

        SolrQuery solrQuery = new SolrQuery();
        setDefaults(solrQuery);

        StringBuilder queryBuilder = new StringBuilder();

        // TODO : handle node type restriction
        Filter.PathRestriction pathRestriction = filter.getPathRestriction();
        if (pathRestriction != null) {
            String path = purgePath(filter);
            String fieldName = configuration.getFieldForPathRestriction(pathRestriction);
            if (fieldName != null) {
                queryBuilder.append(fieldName);
                queryBuilder.append(':');
                queryBuilder.append(path);
                if (!path.equals("\\/")) {
                    queryBuilder.append("\\/");
                }
                // TODO: Also handle other path restriction types
                if (pathRestriction.equals(Filter.PathRestriction.ALL_CHILDREN)) {
                    queryBuilder.append("*");
                }
                queryBuilder.append(" ");
            }
        }
        Collection<Filter.PropertyRestriction> propertyRestrictions = filter.getPropertyRestrictions();
        if (propertyRestrictions != null && !propertyRestrictions.isEmpty()) {
            for (Filter.PropertyRestriction pr : propertyRestrictions) {
                if (pr.propertyName.contains("/")) {
                    // lucene cannot handle child-level property restrictions
                    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);
                    if (first!= null && !"\\/".equals(first)) {
                        queryBuilder.append("\\/");
                    }
                } else {
                    queryBuilder.append(fieldName).append(':');
                    if (pr.first != null && pr.last != null && pr.first.equals(pr.last)) {
                        queryBuilder.append(first);
                    } else if (pr.first == null && pr.last == null) {
                        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(createRangeQuery(first, last, pr.firstIncluding, pr.lastIncluding));
                    } else if (pr.isLike) {
                        // TODO : the current parameter substitution is not expected to work well
                        queryBuilder.append(partialEscape(String.valueOf(pr.first.getValue(pr.first.getType())).replace('%', '*').replace('_', '?')));
                    } else {
                        throw new RuntimeException("[unexpected!] not handled case");
                    }
                }
                queryBuilder.append(" ");
            }
        }

        Collection<String> fulltextConditions = filter.getFulltextConditions();
        for (String fulltextCondition : fulltextConditions) {
            queryBuilder.append(fulltextCondition).append(" ");
        }
        if(queryBuilder.length() == 0) {
            queryBuilder.append("*:*");
        }
        String escapedQuery = queryBuilder.toString();
        solrQuery.setQuery(escapedQuery);

        if (log.isDebugEnabled()) {
            log.debug(new StringBuilder("JCR query: \n" + filter.getQueryStatement() + " \nhas been converted to Solr query: \n").
                    append(solrQuery.toString()).toString());
        }

        return solrQuery;
    }

    private void setDefaults(SolrQuery solrQuery) {
        solrQuery.setParam("q.op", "AND");

        // TODO : change this to be not hard coded
        solrQuery.setParam("df", "catch_all");

        // TODO : can we handle this better?
        solrQuery.setParam("rows", String.valueOf(Integer.MAX_VALUE));
    }

    private static String createRangeQuery(String first, String last, boolean firstIncluding, boolean lastIncluding) {
        // TODO : handle inclusion / exclusion of bounds
        StringBuilder rangeQueryBuilder = new StringBuilder("[");
        rangeQueryBuilder.append(first != null ? first : "*");
        rangeQueryBuilder.append(" TO ");
        rangeQueryBuilder.append(last != null ? last : "*");
        rangeQueryBuilder.append("]");
        return rangeQueryBuilder.toString();
    }

    private static String purgePath(Filter filter) {
        return partialEscape(filter.getPath()).toString();
    }


    // partially borrowed from SolrPluginUtils#partialEscape
    private static CharSequence partialEscape(CharSequence s) {
        StringBuilder sb = new StringBuilder();
        for (int i = 0; i < s.length(); i++) {
            char c = s.charAt(i);
            if (c == '\\' || c == '!' || c == '(' || c == ')' ||
                    c == ':' || c == '^' || c == '[' || c == ']' || c == '/' ||
                    c == '{' || c == '}' || c == '~' || c == '*' || c == '?' ||
                    c == '-' || c == ' ') {
                sb.append('\\');
            }
            sb.append(c);
        }
        return sb;
    }

    @Override
    public Cursor query(Filter filter, NodeState root) {
        Cursor cursor;
        try {
            SolrQuery query = getQuery(filter);
            QueryResponse queryResponse = solrServer.query(query);
            cursor = new SolrCursor(queryResponse);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
        return cursor;
    }


    private class SolrCursor implements Cursor {

        private final SolrDocumentList results;

        private int i;

        public SolrCursor(QueryResponse queryResponse) {
            this.results = queryResponse.getResults();
            i = 0;
        }

        @Override
        public boolean hasNext() {
            return results != null && i < results.size();
        }

        @Override
        public void remove() {
            results.remove(i);
        }

        public IndexRow next() {
            if (i < results.size()) {
                final SolrDocument doc = results.get(i);
                i++;
                return new IndexRow() {
                    @Override
                    public String getPath() {
                        String path = String.valueOf(doc.getFieldValue(
                                configuration.getPathField()));
                        if ("/".equals(path)) {
                            return "/";
                        } else {
                            return path.substring(0, path.length() - 1);
                        }
                    }

                    @Override
                    public PropertyValue getValue(String columnName) {
                        Object o = doc.getFieldValue(columnName);
                        return o == null ? null : PropertyValues.newString(o.toString());
                    }

                };
            } else {
                return null;
            }
        }
    }
}
TOP

Related Classes of org.apache.jackrabbit.oak.plugins.index.solr.query.SolrQueryIndex

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.