Solr is a very powerful search provider that covers almost every feature that you would think of. Geographical search is one of the great features that Solr implemented really nicely by providing spatial indexes, query filters and sorting by distance. Unfortunately, the default Solr provider in Sitecore doesn’t utilise the spatial search feature in Solr. So, I’ve decided to build this small project to extend the existing provider. Thanks to @aokour86 (The author of the Sitecore spatial provider for Lucene) and to @herskinduk for their help and guidance.
Here is the installation guide and examples of how to use the extended provider.
Installation:
- Download the “Solr Search Support” package from the market place.
- Login to Sitecore (v 7.0+) and install the package via the “Installation Wizard”
- At the last step of the installation wizard, choose to restart the Sitecore server.
- Edit the /App_Config/Include/Sitecore.ContentSearch.Solr.Indexes.config to change the index type from
- Sitecore.ContentSearch.SolrProvider.SolrSearchIndex, Sitecore.ContentSearch.SolrProvider to Sitecore.ContentSearch.Spatial.Solr.Provider.SolrSearchIndexWithSpatial, Sitecore.ContentSearch.Spatial.Solr on the master, web and core indexes. E.g the web index should look like
<index id="sitecore_web_index" type="Sitecore.ContentSearch.Spatial.Solr.Provider.SolrSearchIndexWithSpatial, Sitecore.ContentSearch.Spatial.Solr"> ...</index>
- Edit the Solr Schema.xml and add the following dynamic field
<dynamicField name="*_rpt" type="location_rpt" indexed="true" stored="true" />
How to use:
1- Customising the Sitecore item templates
The Solr Search Support package will add a new custom Sitecore data type – LatLon to Sitecore. You can use this data type to add spatial support to any Sitecore template.
Adding a field of the LatLon custom data type will provide you with the following map-based interface to set the location of your items.
The map is interactive, so you can click on the map to populate the Latitude and Longitude values. Also, you can type the values in the text box above the map, and click on the “Refresh map” link to update the location on the map from the value you entered.
2- Writing spatial queries
After you have created some data, it’s time to write spatial queries to filter and sort the results. The package contains new extension functions that are handled using a custom IQueryable provider. Here is what you need to do:
Create a SearchResultItem custom class to map the GeoLocation field to a SpatialPoint type as follows:
public class CustomSearchResultItem : SearchResultItem { [IndexField("title")] public string Title { get; set; } [IndexField("body")] public string Body { get; set; } [IndexField("geolocation")] public SpatialPoint Location { get; set; } }
The SpatialPoint type is provided by the package, you will need to reference the \bin\Sitecore.ContentSearch.Spatial.DataTypes assembly.
In order to write spatial queries you will need first to reference the provided \bin\Sitecore.ContentSearch.Spatial.Solr assembly. The following code shows how to filter the results given a point and radius kilometers.
var indexName = string.Format("sitecore_{0}_index", Sitecore.Context.Database.Name); var index = ContentSearchManager.GetIndex(indexName); using (var context = index.CreateSearchContext()) { var query = context.GetQueryable<CustomSearchResultItem>(); query = query.Where(i => i.Parent == new ID("{B05F6DDC-763C-4D12-8137-B40A81AED6D7}")); query = query.WithinRadius(i => i.Location, 51.44052675988403, -3.175048828125, 50); return query.GetResults(); }
and to sort by distance, you can use the following function:
query = query.OrderByNearest();
If you have any enquires please contact me on twitter @ehabelgindy.
The source code of the module is available at https://github.com/ehabelgindy/sitecore-solr-spatial, so if you can contribute to improve the module .. please do!
Thanks
Don’t have this module for lucene?
here it is
https://marketplace.sitecore.net/Modules/L/Lucene_Spatial_Search_Support.aspx?sc_lang=en
Is it possible to return the distance?
Hi, ehabelgindy,
any idea why this does not work?
string myid = “{B05F6DDC-763C-4D12-8137-B40A81AED6D7}”;
var query = context.GetQueryable();
query = query.Where(i => i.Parent == new ID(myid));
it doesn’t work anymore whenever i replace the value of ID(“”) with a variable.
any help can be appreciated.
I am not sure, looks like the expression tree only supports constants!
Have you raised an issue with Sitecore Support?
What would be the best approach to use both SwitchOnRebuildSolrSearchIndex and SolrSearchIndexWithSpatial as the same index provider?
Would it be better to extend SwitchOnRebuildSolrSearchIndex and include the logic from SolrSearchIndexWithSpatial? Thoughts?
Nevermind, just saw the code from Github, looks like you already have a SwitchOnRebuildSolrSearchIndexWithSpatial implementation. Will take it for a spin. Thanks!
Actually nvm – I figured it out. You need to use the WithinRadius function first
glad you sorted it out
Hi,
Is this supported for SC 8.2. I’m getting the following error on index after completing the steps above:
12940 14:03:13 WARN Crawler : AddRecursive DoItemAdd failed – {BED290E2-C379-4FAD-9179-2ECB0076C770}
Exception: SolrNet.Exceptions.SolrConnectionException
Message:
400177org.apache.solr.common.SolrExceptionorg.apache.solr.common.SolrExceptionERROR: [doc=sitecore://master/{debaede6-3896-4f5b-9ccf-673fd1d6ba58}?lang=en&ver=1&ndx=providers_master_index] unknown field ‘geolocation’400
Source: SolrNet
at SolrNet.Impl.SolrConnection.PostStream(String relativeUrl, String contentType, Stream content, IEnumerable`1 parameters)
at SolrNet.Impl.SolrConnection.Post(String relativeUrl, String s)
at SolrNet.Impl.SolrBasicServer`1.SendAndParseHeader(ISolrCommand cmd)
at Sitecore.ContentSearch.SolrProvider.SolrBatchUpdateContext.AddRange(IEnumerable`1 batch)
at Sitecore.ContentSearch.SolrProvider.SolrBatchUpdateContext.AddDocument(Object itemToAdd, IExecutionContext[] executionContexts)
at Sitecore.ContentSearch.SolrProvider.SolrIndexOperations.ApplyPermissionsThenIndex(IProviderUpdateContext context, IIndexable version)
at Sitecore.ContentSearch.SitecoreItemCrawler.DoAdd(IProviderUpdateContext context, SitecoreIndexableItem indexable)
at Sitecore.ContentSearch.HierarchicalDataCrawler`1.CrawlItem(T indexable, IProviderUpdateContext context, CrawlState`1 state)
Hi ehabelgindy,
Another question – the SortByNearest sort order – how do you specify a lat/long for it to know what it is near to? For example – I have a list of locations, a user types in their location (which is converted to Lat/Long) and I want to show those locations that are ordered with the closest ones to the user first.
Does that make sense?