Using Solr spatial search

Abstract

Use spatial search to search for geograpical points in different ways

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.

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)

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);
            }
        }
    }
}