David D.
David D.

Reputation: 153

How to inject "distance" into a list of Locations

We have a model Event that has a "Location" field.

What we want achieve is: given a query near a lat/lon coordinate, we want each result in the list to also have a field, called distance, that is the distance in km from the provided coordinate to the location.geo for each object in the result set.

We see in the KeystoneJS Location type definition there are underscore methods for this.

But we can't figure out how to inject this as a "virtual" for each result in the list.

Does anyone have any ideas for how to pass the requested Lat/Lon to those underscore methods and populate it in the results list?

var q = Event.paginate({ page: req.query.page || 1, perPage: req.query.max || 20 })
                         .where('eventDate').gt(since)
                         .where('state').equals('published')
                         .sort("eventDate")

if (req.query.lon && req.query.lat) {
    var coords = [req.query.lon, req.query.lat];
    var maxD = (req.query.maxDistance || 5) / 6371;
    q = q.where('location.geo')
             .near({ center: coords, maxDistance: maxD, spherical:true })
}
q.exec(function(err, items) {
    if (err) return res.apiError('database error', err);
    res.apiResponse({
        events: items
    });
});

Upvotes: 0

Views: 69

Answers (1)

Shea Hunter Belsky
Shea Hunter Belsky

Reputation: 3218

KeystoneJS items can have methods that act as functions of the document. Virtual functions do not accept parameters (they are used to calculate/return values based on properties of the document). So a method on the schema is necessary here.

In your Event.js Model code, you need to create the appropriate method that calculates the distance (in kilometers) based on the coordinates that are stored in the current document.

Event.schema.methods.distanceKM = function (latitude, longitude, cb) {
    return cb(this._.location.kmFrom([longitude, latitude]));
};

That's an asynchronous implementation. If you can't/don't want to do it that way, use this instead.

Event.schema.methods.distanceKM = function (latitude, longitude) {
    return this._.location.kmFrom([longitude, latitude]);
};

If you need for the result to be in miles, just change kmFrom to milesFrom in the method.

You can then call upon the distance function for each of your Events. The implementation of that is up to you, but every one of your items returned by the query below will now have the distance function, which returns the distance (km or miles) from that Event to the parameterized coordinates.

IMPORTANT

I discovered in the midst of writing this that the current implementations for kmFrom and milesFrom are broken. The most recent release on npm has this broken code. The functions have been updated on GitHub (and you can see the small amount of code to change locally that will make them work), but it has not been pushed into a release of Keystone at time of writing.

Upvotes: 1

Related Questions