/* * 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.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.common.util.NamedList; import org.apache.solr.handler.component.ResponseBuilder; import org.apache.solr.handler.component.SearchComponent; import org.locationtech.spatial4j.context.SpatialContext; import org.locationtech.spatial4j.shape.Point; import org.locationtech.spatial4j.shape.impl.PointImpl; import java.io.IOException; import java.util.List; import com.sematext.querysegmenter.QuerySegmenter; import com.sematext.querysegmenter.QuerySegmenterDefaultImpl; import com.sematext.querysegmenter.TypedSegment; import com.sematext.querysegmenter.geolocation.CentroidSegmentDictionaryMemImpl; import com.sematext.querysegmenter.geolocation.CentroidTypedSegment; /** * This SearchComponent is used to alter the user location if a segment of the query is a centroid. If a match is found, * the user location (specified in the pt request param) is changed to the center location of the centroid. Subsequent * filters that use this location to filter the result set will then be working with the centroid instead of the * original user location. If multiple centroid segments are returned from the user query, the closest centroid to the * original user location is used. * * For example, if a user searches for “pizza Aaronsburg”, the segment “Aaronsburg” could be returned as a centroid with * location 40.9068, -77.4081. This location would be used instead of the original location. This would make the results * to be filtered to retain only the ones around the centroid location. * * Note that this component only works with the centroid dictionary. * * @author sematext, http://www.sematext.com/ */ public class CentroidComponent extends SearchComponent { private static final String FILENAME = "filename"; private static final String SEPARATOR = "separator"; private SpatialContext ctx; private QuerySegmenter segmenter; @SuppressWarnings("rawtypes") @Override public void init(NamedList args) { super.init(args); segmenter = new QuerySegmenterDefaultImpl(); String filename = (String) args.get(FILENAME); String separator = (String) args.get(SEPARATOR); segmenter.addFileDictionary("centroid", filename, separator, CentroidSegmentDictionaryMemImpl.class); this.ctx = SpatialContext.GEO; } @Override public String getDescription() { return null; } @Override public void prepare(ResponseBuilder rb) throws IOException { SolrParams params = rb.req.getParams(); String q = params.get(CommonParams.Q); List<TypedSegment> typedSegments = segmenter.segment(q); if (typedSegments.isEmpty()) { return; } // If multiple matches, use the closest from the user CentroidTypedSegment centroidSegment; if (typedSegments.size() > 1) { double[] userLatlon = getUserLocation(params); centroidSegment = getClosestSegment(typedSegments, userLatlon); } else { centroidSegment = (CentroidTypedSegment) typedSegments.get(0); } // Override point to use the value from the matching centroid. ModifiableSolrParams modifiableSolrParams = new ModifiableSolrParams(params); modifiableSolrParams.set(SpatialParams.POINT, String.format("%s,%s", centroidSegment.getLatitude(), centroidSegment.getLongitude())); q = q.replaceAll(centroidSegment.getSegment(), ""); modifiableSolrParams.set(CommonParams.Q, q); rb.req.setParams(modifiableSolrParams); } private CentroidTypedSegment getClosestSegment(List<TypedSegment> typedSegments, double[] userLatlon) { CentroidTypedSegment closest = null; double closestDistance = Double.MAX_VALUE; for (TypedSegment typedSegment : typedSegments) { CentroidTypedSegment centroidSegment = (CentroidTypedSegment) typedSegment; Point p1 = new PointImpl(userLatlon[0], userLatlon[1], ctx); Point p2 = new PointImpl(centroidSegment.getLatitude(), centroidSegment.getLongitude(), ctx); double distance = ctx.getDistCalc().distance(p1, p2); if (distance < closestDistance) { closest = centroidSegment; } } return closest; } private double[] getUserLocation(SolrParams params) { String userLocation = params.get(SpatialParams.POINT); if (userLocation == null || userLocation.isEmpty()) { throw new IllegalArgumentException("pt is missing."); } String[] latlon = userLocation.split(","); if (latlon.length != 2) { throw new IllegalArgumentException("pt is invalid (should be [lat,lon])."); } return new double[] { Double.valueOf(latlon[0]), Double.valueOf(latlon[1]) }; } @Override public void process(ResponseBuilder rb) throws IOException { } }