org.apache.solr.search.SpatialQParser.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.solr.search.SpatialQParser.java

Source

/**
 * 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.solr.search;

import java.util.ArrayList;
import javax.servlet.http.HttpServletRequest;

import org.apache.lucene.document.Document;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.BooleanQuery;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.Sort;
import org.apache.lucene.search.SortField;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.spatial.geometry.LatLng;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.ModifiableSolrParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.params.SpatialParams;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.schema.FieldType;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.util.GeotargetAdapter;

/**
 * This class parses and executes a spatial query
 */
public class SpatialQParser extends QParser {

    private static final String NAME_FIELD = "asciiname";
    private static final String STATE_FIELD = "admin1code";
    private static final String COUNTRY_FIELD = "countrycode";
    private static final String LOCATION_FIELD = "location";
    private static final String POPULATION_FIELD = "population";

    public static final String SEARCH_RADIUS = "20";
    public static final String CITY = "ct";
    public static final String STATE = "s";
    public static final String COUNTRY = "c";
    public static final String DISTANCE = "d";

    private GeotargetAdapter geoTargeter;

    /**
     * Constructor
     * 
     * @param qstr
     *          the query string
     * @param localParams
     *          solr localParams
     * @param params
     *          other params
     * @param req
     *          the query request object
     * @param geoTargeter
     *          the adapter to use to geotarget client ip if no location is
     *          provided
     */
    public SpatialQParser(String qstr, SolrParams localParams, SolrParams params, SolrQueryRequest req,
            GeotargetAdapter geoTargeter) {
        super(qstr, localParams, params, req);
        this.geoTargeter = geoTargeter;
    }

    @Override
    public Query parse() throws ParseException {
        // this will combine the results that are found for each
        // administrative level
        BooleanQuery allQuery = new BooleanQuery();

        // attempt to create a query on city (low level administrative division)
        String city = localParams.get(CITY);
        if (city != null) {
            city = city.toLowerCase();
            SchemaField nameField = req.getSchema().getField(NAME_FIELD);
            FieldType nameFieldType = nameField.getType();
            Query cityQuery = nameFieldType.getFieldQuery(this, nameField, city);
            allQuery.add(cityQuery, Occur.MUST);
        }

        // attempt to create a query on state (mid level administrative division)
        String state = localParams.get(STATE);
        if (state != null) {
            state = state.toLowerCase();
            SchemaField stateField = req.getSchema().getField(STATE_FIELD);
            FieldType stateFieldType = stateField.getType();
            Query stateQuery = stateFieldType.getFieldQuery(this, stateField, state);
            allQuery.add(stateQuery, Occur.MUST);
        }

        // attempt to create a query on city (high level administrative division)
        String country = localParams.get(COUNTRY);
        if (country != null) {
            country = country.toLowerCase();
            SchemaField countryField = req.getSchema().getField(COUNTRY_FIELD);
            FieldType countryFieldType = countryField.getType();
            Query countryQuery = countryFieldType.getFieldQuery(this, countryField, country);
            allQuery.add(countryQuery, Occur.MUST);
        }

        String latitude = null;
        String longitude = null;

        // no location provided, computer user's location via reverse-ip lookup
        if (allQuery.getClauses().length == 0) {
            HttpServletRequest httpreq = req.getHttpServletRequest();
            String ip = httpreq.getRemoteAddr();

            LatLng currLoc = geoTargeter.getCurrentLocation(ip);

            if (currLoc != null) {
                latitude = Double.toString(currLoc.getLat());
                longitude = Double.toString(currLoc.getLng());
            }
        } else {
            SolrIndexSearcher searcher = req.getSearcher();
            Document geocodeDoc = null;

            try {
                Sort s = new Sort(new SortField(POPULATION_FIELD, SortField.LONG, true));
                DocList docs = searcher.getDocList(allQuery, new ArrayList<Query>(), s, 0, 1, 0);

                if (docs == null)
                    return query;

                DocIterator iter = docs.iterator();
                int geocodeDocId = iter.nextDoc();
                geocodeDoc = searcher.doc(geocodeDocId);
            } catch (Exception e) {
                e.printStackTrace();
                return query;
            }
            latitude = geocodeDoc.get("latitude");
            longitude = geocodeDoc.get("longitude");
        }

        // combine the spatial and free-text queries
        BooleanQuery finalQuery = new BooleanQuery();

        // if no location is provided and user's location cannot be determined,
        // do not search location
        if (latitude != null && longitude != null) {
            String distance = localParams.get(DISTANCE);

            try {
                Double.parseDouble(distance);
            } catch (Exception e) {
                distance = SEARCH_RADIUS;
            }

            SpatialFilterQParserPlugin spatialFilter = new SpatialFilterQParserPlugin();
            ModifiableSolrParams spatialParams = new ModifiableSolrParams();
            spatialParams.add(SpatialParams.POINT, latitude + "," + longitude);
            spatialParams.add(SpatialParams.DISTANCE, distance);
            spatialParams.add(CommonParams.FL, LOCATION_FIELD);
            Query spatialQuery = spatialFilter.createParser(qstr, spatialParams, spatialParams, req).parse();
            finalQuery.add(spatialQuery, Occur.MUST);
        }

        // get results from default LuceneQParser
        Query defQuery = new LuceneQParserPlugin().createParser(qstr, localParams, params, req).parse();

        finalQuery.add(defQuery, Occur.MUST);

        return finalQuery;
    }
}