Package org.sindice.siren.solr.qparser

Source Code of org.sindice.siren.solr.qparser.SirenQParser

/**
* Copyright 2014 National University of Ireland, Galway.
*
* This file is part of the SIREn project. Project and contact information:
*
*  https://github.com/rdelbru/SIREn
*
* Licensed 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.sindice.siren.solr.qparser;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.flexible.standard.config.StandardQueryConfigHandler.Operator;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.IndexSchema;
import org.apache.solr.search.QParser;
import org.apache.solr.search.QueryParsing;
import org.apache.solr.util.SolrPluginUtils;
import org.sindice.siren.solr.schema.Datatype;
import org.sindice.siren.solr.schema.SirenField;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* The {@link SirenQParser} is in charge of parsing a SIREn query request.
* <p>
* Expand the query to multiple fields by constructing a disjunction of the
* parsed query across the fields.
* <p>
* For each <code>nested</code> parameter in the request, its argument
* is parsed as a subquery and added to the main query.
* <p>
* The default operator for use by the query parsers is {@link Operator#AND}. It
* can be overwritten using the parameter {@link QueryParsing#OP}.
*/
public abstract class SirenQParser extends QParser {

  protected Properties qnames;

  private static final Logger
  logger = LoggerFactory.getLogger(SirenQParser.class);

  public SirenQParser(final String qstr, final SolrParams localParams,
                      final SolrParams params, final SolrQueryRequest req) {
    super(qstr, localParams, params, req);
  }

  /**
   * Set the QNames mapping for use in the query parser.
   */
  public void setQNames(final Properties qnames) {
    this.qnames = qnames;
  }

  @Override
  public Query parse() throws ParseException {
    if (qstr == null || qstr.length()==0) return null;
    final SolrParams solrParams = SolrParams.wrapDefaults(localParams, params);
    final Map<String, Float> boosts = parseQueryFields(req.getSchema(), solrParams);

    final BooleanQuery main = this.getMainQuery(boosts, qstr);
    this.addNestedQuery(main, solrParams);

    return main;
  }

  /**
   * Build the main query that will be executed. Expand to multiple fields if
   * necessary.
   * @throws ParseException
   */
  private BooleanQuery getMainQuery(final Map<String, Float> boosts, final String qstr)
  throws ParseException {
    // We disable the coord because this query is an artificial construct
    final BooleanQuery query = new BooleanQuery(true);
    for (final String field : boosts.keySet()) {
      final Map<String, Analyzer> datatypeConfig = this.getDatatypeConfig(field);
      final Query q = this.parse(field, qstr, datatypeConfig);
      if (boosts.get(field) != null) {
        q.setBoost(boosts.get(field));
      }
      query.add(q, Occur.SHOULD);
    }
    return query;
  }

  /**
   * Build the nested queries and add them as a (MUST) clause of the main query.
   */
  private void addNestedQuery(final BooleanQuery main, final SolrParams solrParams)
  throws ParseException {
    if (solrParams.getParams("nested") != null) {
      for (final String nested : solrParams.getParams("nested")) {
        final QParser baseParser = this.subQuery(nested, null);
        main.add(baseParser.getQuery(), Occur.MUST);
      }
    }
  }

  protected abstract Query parse(final String field, final String qstr,
                                 final Map<String, Analyzer> datatypeConfig)
  throws ParseException;

  /**
   * Create a new QParser for parsing an embedded nested query.
   * <p>
   * Remove the nested parameters from the original request to avoid infinite
   * recursion.
   */
  @Override
  public QParser subQuery(final String q, final String defaultType)
  throws ParseException {
    final QParser nestedParser = super.subQuery(q, defaultType);
    final NamedList<Object> params = nestedParser.getParams().toNamedList();
    params.remove("nested");
    nestedParser.setParams(SolrParams.toSolrParams(params));
    return nestedParser;
  }

  /**
   * Retrieve the datatype query analyzers associated to this field
   */
  private Map<String, Analyzer> getDatatypeConfig(final String field) {
    final Map<String, Analyzer> datatypeConfig = new HashMap<String, Analyzer>();
    final SirenField fieldType = (SirenField) req.getSchema().getFieldType(field);
    final Map<String, Datatype> datatypes = fieldType.getDatatypes();

    for (final Entry<String, Datatype> e : datatypes.entrySet()) {

      if (e.getValue().getQueryAnalyzer() == null) {
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
          "Configuration Error: No analyzer defined for type 'query' in " +
          "datatype " + e.getKey());
      }

      datatypeConfig.put(e.getKey(), e.getValue().getQueryAnalyzer());
    }

    return datatypeConfig;
  }

  protected Operator getDefaultOperator() {
    final String val = params.get(QueryParsing.OP);
    Operator defaultOp = Operator.AND; // default AND operator
    if (val != null) {
      defaultOp = "AND".equals(val) ? Operator.AND : Operator.OR;
    }
    return defaultOp;
  }

  /**
   * Uses {@link SolrPluginUtils#parseFieldBoosts(String)} with the 'qf'
   * parameter. Falls back to the 'df' parameter or
   * {@link org.apache.solr.schema.IndexSchema#getDefaultSearchFieldName()}.
   */
  public static Map<String, Float> parseQueryFields(final IndexSchema indexSchema, final SolrParams solrParams)
  throws ParseException {
    final Map<String, Float> queryFields = SolrPluginUtils.parseFieldBoosts(solrParams.getParams(SirenParams.QF));
    if (queryFields.isEmpty()) {
      final String df = QueryParsing.getDefaultField(indexSchema, solrParams.get(CommonParams.DF));
      if (df == null) {
        throw new ParseException("Neither "+SirenParams.QF+", "+CommonParams.DF +", nor the default search field are present.");
      }
      queryFields.put(df, 1.0f);
    }
    checkFieldTypes(indexSchema, queryFields);
    return queryFields;
  }

  /**
   * Check if all fields are of type {@link SirenField}.
   */
  private static void checkFieldTypes(final IndexSchema indexSchema, final Map<String, Float> queryFields) {
    for (final String fieldName : queryFields.keySet()) {
      final FieldType fieldType = indexSchema.getFieldType(fieldName);
      if (!(fieldType instanceof SirenField)) {
        throw new SolrException(SolrException.ErrorCode.SERVER_ERROR,
          "FieldType: " + fieldName + " (" + fieldType.getTypeName() + ") do not support Siren's tree query");
      }
    }
  }

}
TOP

Related Classes of org.sindice.siren.solr.qparser.SirenQParser

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.