Using Solr spatial search
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:
-
A property that contains the coordinates of the point. You must mark this property
[IndexField("coordinate")]
and return theCoordinate
class.For example:
/// <summary>Gets or sets the coordinate of POI.</summary>
[IndexField("coordinate")]
[DataMember]
public Coordinate Location { get; set; }
-
The coordinates of the center point to use when searching for other points within a certain distance.
-
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);
}
}
}
}