Gravatar for jhansen@singlestoneconsulting.com

Question by Jeff Hansen, May 15, 2015 10:16 AM

Sorting by dynamic calculated field (distance)

Hello,

I've got a page that's returning a list of results that have latitude and longitude coordinate associated with them. I've also got a long/lat point associated with the user performing the query. We've already configured some filtering of results using the Coveo for Sitecore components, such that we're getting back maybe 100 results. Instead of limiting the results, we'd like to simply sort them by the distance from the point associated with the user performing the query. I've got a few questions related to this:

  1. Do the lat/long fields in the index need to be a certain type? They're coming through as String right now. It seems like they'd need to be numeric or floating for the dist function to work, but I've not seen that explicitly stated anywhere. If they do need to be of another type, what return type do I need to set on my computed fields to get those coming through as floating or numeric? Most of the stuff that I've read has been concerned with setting the return type for datetime.
  2. Does the dynamically generated "distance" field need to be registered in my configuration files anywhere in order for me to be able to sort on it?
  3. How do I pass the $qf to the JavaScript performing the query to basically hardcode the sort? I've seen a lot about passing the $qf to advancedExpression or constantExpression, but not about how to pass it to sort.

Here's the excerpt of the code that I'm using:

 <script type="text/javascript">
 Coveo.$(function() {
     CoveoForSitecore.componentsOptions = @(Html.Raw(Model.GetJavaScriptInitializationOptions()));

        // lat/long of user harded for now but will be dynamically retrieved
      var geoQuery = "$qf(function:'dist(@Model.ToCoveoFieldName("latitude"), @Model.ToCoveoFieldName("longitude"), 46.8167, -71.2167)', fieldName: 'distance')";
      var sort = "$sort(criteria: 'fieldascending', field: 'distance')";
      Coveo.$('#search')
         .on("buildingQuery", function(e, args) {
             args.queryBuilder.advancedExpression.add(geoQuery);
                //somehow add the sort expression here
         })
         .coveoForSitecore('init', CoveoForSitecore.componentsOptions);
         });
 </script>

Here's some additional information about the configuration and code in case someone can help:

FieldMap:

<fieldMap type="Coveo.Framework.Fields.CoveoFieldMap, Coveo.Framework">
        <param desc="coveoReflectionFactory" type="Coveo.Framework.Utils.CoveoReflectionFactory, Coveo.Framework" />
        <fieldNames hint="raw:AddFieldByFieldName">
            .....
          <fieldType fieldName='latitude' settingType='Coveo.Framework.Configuration.FieldConfiguration, Coveo.Framework' returnType="System.Double" />
          <fieldType fieldName='longitude' settingType='Coveo.Framework.Configuration.FieldConfiguration, Coveo.Framework' returnType="System.Double" />
        </fieldNames>
</fieldMap>

Computed field definition:

<fields hint="raw:AddComputedIndexField">
        ...
        <field fieldname="latitude">Client.Web.Coveo.ComputedFields.LatitudeComputedField, Client.Web.Coveo</field>
        <field fieldname="longitude">Client.Web.Coveo.ComputedFields.LongitudeComputedField, Client.Web.Coveo</field>
   </fields>

Code generating the computed field value:

public class LongitudeComputedField : IComputedIndexField
{
    /// <inheritdoc />
    public string FieldName { get; set; }

    /// <inheritdoc />
    public string ReturnType { get; set; }

    /// <inheritdoc />
    public object ComputeFieldValue(IIndexable pIndexable)
    {
        Item item = pIndexable as SitecoreIndexableItem;

        if (item == null)
        {
            return null;
        }

        var zipcode = item["zip"] ?? string.Empty;

        if (string.IsNullOrWhiteSpace(zipcode))
        {
            return null;
        }

        var matchingRecord = ZipcodeHelpers.RetrieveZipcodeFromDatabase(zipcode);

        if (matchingRecord == null)
        {
            return null;
        }

        // the Longitude property is of type "System.Double"
        return matchingRecord.Longitude;
    }
}
Gravatar for jflheureux@coveo.com

Comment by Jean-François L'Heureux, May 15, 2015 11:24 AM

The dist query function is detailed here: https://developers.coveo.com/display/JsSearch/Getting+Geolocalized+Results+in+a+JavaScript+Search+Page

Gravatar for jhansen@singlestoneconsulting.com

Comment by Jeff Hansen, May 15, 2015 11:41 AM

Hey Jeff,

Yeah, I reviewed that page and that's where my code sample from above comes from. What it doesn't address is:

  1. How long and lat are stored in the index (string versus numeric versus floating)
  2. How to use it to sort instead of exclude results

Thanks,

Jeff

Gravatar for jhansen@singlestoneconsulting.com

Comment by Jeff Hansen, May 15, 2015 11:49 AM

I also get an "InvalidQueryFunctionFieldType" on my latitude field when I run the code above, which makes me think that the field needs to be something other than string, but I'd like to have that confirmed and know how to set that computed field as something other than string in the index (whatever it needs to be to be used in the dist function).

Gravatar for wnijmeijer@coveo.com

Comment by Wim Nijmeijer, May 19, 2015 8:32 AM

Jeff, Make sure your Longitude/Latitude field is a floating point in Coveo.

Gravatar for jhansen@singlestoneconsulting.com

Comment by Jeff Hansen, May 20, 2015 11:30 AM

How do I ensure that's the case? The above configuration and code seems to result in latitude and longitude being stored as strings. I see a lot of stuff about how to get things to be date fields but haven't found any documentation on setting the return type to ensure floating point.

Gravatar for jflheureux@coveo.com

Comment by Jean-François L'Heureux, May 20, 2015 2:07 PM

The ReturnType property of your our LongitudeComputedField class should return "Number" as the format to be configured as a floating point field in CES.

You need to rebuild your index after the configuration modification for the CES field set to be updated and the documents to be updated with the value of the correct type.

0 Reply
Ask a question