/* * Copyright (c) Sematext International * All Rights Reserved * * THIS IS UNPUBLISHED PROPRIETARY SOURCE CODE OF Sematext International * The copyright notice above does not evidence any * actual or intended publication of such source code. */ package com.sematext.querysegmenter.solr; import org.apache.lucene.queries.function.BoostedQuery; import org.apache.lucene.search.BooleanClause; import org.apache.lucene.search.BooleanQuery; import org.apache.lucene.search.BoostQuery; import org.apache.lucene.search.Query; import org.apache.solr.common.params.SolrParams; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.search.QParser; import org.apache.solr.search.SyntaxError; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import com.sematext.querysegmenter.QuerySegmenter; import com.sematext.querysegmenter.TypedSegment; import com.sematext.querysegmenter.solr.QuerySegmenterConfig.FieldMapping; /** * This QParser is used to retrieve segments from a user query. * * If there is a segment in the user query that matches a dictionary entry, the query is rewritten using either the label * or the location of the typed segment. For example, for the query “pizza brooklyn”, if “brooklyn” is a typed segment, * the query will be rewritten to: * <p> * “pizza neighborhood:brooklyn” * </p> * or, if useLatLon was set to true: * <p> * “pizza location:[minlat,minlon TO maxlat, maxlon]” * </p> * The field to use and whether we should use the label or the location is configurable. * * @author sematext, http://www.sematext.com/ */ public class QuerySegmenterQParser extends QParser { private final QuerySegmenter segmenter; private Map<String, FieldMapping> mappings; public QuerySegmenterQParser(QuerySegmenterConfig config, String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req) { super(qstr, localParams, params, req); this.segmenter = config.getSegmenter(); this.mappings = config.getMappings(); } @Override public Query parse() throws SyntaxError { String qstr = getString(); List<TypedSegment> typedSegments = segmenter.segment(qstr); List<Query> boostQueries = new ArrayList<>(); for (TypedSegment typedSegment : typedSegments) { FieldMapping mapping = mappings.get(typedSegment.getDictionaryName()); String value = QuerySegmenterComponent.getValue(typedSegment, mapping); if (mapping.useBoostQuery) { qstr = qstr.replaceFirst(typedSegment.getSegment(), ""); String qs = String.format("%s:%s", mapping.field, value); boostQueries.add(subQuery(qs, null).getQuery()); } else { qstr = qstr.replaceFirst(typedSegment.getSegment(), String.format("%s:%s", mapping.field, value)); } } // Passing null allows to use another qparser defined with defType (like edismax) // See SOLR-2972 QParser parser = subQuery(qstr, null); BooleanQuery.Builder query = new BooleanQuery.Builder(); query.add(parser.parse(), BooleanClause.Occur.MUST); for(Query bq:boostQueries) { query.add(bq, BooleanClause.Occur.SHOULD); } return query.build(); } }