Using Solr spatial search

Last updated Monday, December 18, 2017 in Sitecore Experience Platform for Developer
Keywords: Development, Search

Note

This topic is valid for Sitecore 9.0 and later.

You can use some of the Solr spatial search features from Sitecore. You use spatial search to:

  • Search for an index point (using the format: Latitude, Longitude)
  • Filter search results based on a circle or bounding box
  • Sort or boost scoring by distance between points

Sitecore supports the SpatialRecursivePrefixTreeFieldType type (RPT for short) from Solr for indexing and searching. There is a Coordinate Sitecore template, in /sitecore/templates/System/Geospatial/ in the master database, for storing index points. The Coordinate template has two fields – Latitude and Longitude. When you create an item from this template, the coordinates are indexed in Solr.

Use

This example code shows how you use a Sitecore LINQ query to search for a list of points that are within a 10-kilometer radius of the Kuala Lumpur city center, which has the coordinates 3.156230 and 101.713614:

context.GetQueryable < SearchResultItem > ()
    .WithinRadius(s => s.Location, new Coordinate(3.156230, 101.713614), 10)

The WithinRadius API

This API has three parameters:

  1. A property that contains the coordinates of the point. You must mark this property [IndexField("coordinate")] and return the Coordinate class.

    For example:

    /// <summary>Gets or sets the coordinate of POI.</summary>

    [IndexField("coordinate")]

    [DataMember]

    public Coordinate Location { get; set; }

  2. The coordinates of the center point to use when searching for other points within a certain distance.
  3. The radius used to search for any point that is within a certain distance from the specified center point coordinates.

There is a fourth, optional parameter. You use it to specify whether Solr searches for points within a bounding box instead of within a circle.

You can use the Sitecore LINQ query order to return points that are ordered in either ascending (the default) or descending distance:

var queryable = context.GetQueryable < SearchResultItem > ()
    .OrderByDistance(s => s.Location, "3.156230, 101.713614");
var queryable = context.GetQueryable < SearchResultItem > ()
    .OrderByDistanceDescending(s => s.Location, "3.156230, 101.713614");

You can combine the WithinRadius API with the OrderByDistance API or the OrderByDistanceDescending API to retrieve a list of points that lie within the specified radius. The results are ordered by ascending or descending proximity to the center point, as in this example:

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="WithinRadius.aspx.cs" Inherits="Sitecore.ContentSearch.SolrProvider.Example.WithinRadius" %>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    
    </div>
    </form>
</body>
</html>

WithinRadius.aspx.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using Sitecore.ContentSearch.Data;
using Sitecore.ContentSearch.Linq;
using Sitecore.ContentSearch.SearchTypes;
namespace Sitecore.ContentSearch.SolrProvider.Example {
    public partial class WithinRadius : System.Web.UI.Page {
        protected void Page_Load (object sender, EventArgs e) {
            var index = ContentSearchManager.GetIndex ("sitecore_master_index");
            var htmlBuilder = new StringBuilder ();
            using (var context = index.CreateSearchContext ()) {
                var queryable = context.GetQueryable<SearchResultItem> ()
                    .WithinRadius (s => s.Location, new Coordinate (3.156230, 101.713614), 10)
                    //.OrderByDistanceDescending(s => s.Location, "3.156230, 101.713614");
                    .OrderByDistance (s => s.Location, "3.156230, 101.713614");
                var list = queryable.ToList ();
                if (list.Count == 0) {
                    Response.Write ("<div style='color:red'>No POI within the radius</div>");
                    return;
                }
                htmlBuilder.AppendFormat ("Found {0} item within vicinity. <br/>", list.Count);
                htmlBuilder.Append ("<div><ul>");
                foreach (var item in list) {
                    htmlBuilder.AppendFormat ("<li>{0} : {1}</li>", item.Name, item.Location.ToString ());
                }
                htmlBuilder.Append ("</ul></div>");
                Response.Write (htmlBuilder);
            }
        }
    }
}

Send feedback about the documentation to docsite@sitecore.net.