I was wondering if it's possible to combine a text search and run a geospatial query on the results / how I would do it. I'm using Mongoose at the moment but don't mind using straight Mongo for the commands.
What I'm trying to do is allow the user to search for a product and return the results that are within a particular location. I have the two commands running separately, but can't figure out how to combine them.
In MongoDB, you can store geospatial data as GeoJSON objects or as legacy coordinate pairs.
Use the $text query operator to perform text searches on a collection with a text index. $text will tokenize the search string using whitespace and most punctuation as delimiters, and perform a logical OR of all such tokens in the search string.
MongoDB text search uses the Snowball stemming library to reduce words to an expected root form (or stem) based on common language rules. Algorithmic stemming provides a quick reduction, but languages have exceptions (such as irregular or contradicting verb conjugation patterns) that can affect accuracy.
$search. string. A string of terms that MongoDB parses and uses to query the text index. MongoDB performs a logical OR search of the terms unless specified as a phrase.
Mongo DB itself does not support text and geo search at same time. Refer this
But you can combine the query and can achieve the result
const aggregate=YourCollection.aggregation()
aggregate.near({
near:coord,
includeLocs: "loc",
distanceField: "distance",
maxDistance: distance
});
aggregate.match({name:{$regex:keyword,$options: 'i'}})
aggregate.exec(function(err,yourDocuments){
//your smart code
})
Text search + regex
var aggregate=YourCollection.aggregate();
var match= {
latitude: { $lt: yourCurrentLatitude+maxDistance, $gt: yourCurrentLatitude-maxDistance },
longitude: { $lt: yourCurrentLongitude+maxDistance, $gt: yourCurrentLongitude-maxDistance },
{$text:{$search:keyword}}
};
aggregate.match(match).exec(function(err,yourDocuments){//your smart code})
Mapreduce + ( Text search Or regex )
YourCollection.mapReduce(map, reduce, {out: {inline:1, query : yourFirstQuery}}, function(err, yourNewCollection) {
// Mapreduce returns the temporary collection with the results
collection.find(yourNewQuery, function(err, result) {
//your awsome code
});
});
This is a very common requirement where geo filter and text search is required in one use case which is unfortunately not directly supported by mongodb yet.
Below code uses mongoose driver, filters documents first based on location (longitude and latitude) and then further filters based on the searched term.
var area = {
center: [51, -114], //lng = 51 and lat = -114
radius: 100,
unique: true //this option is deprecated from MongoDB 2.6 on as mongodb no longer returns duplicate results
};
var query = Post.where('loc').within().circle(area) //Step 1: filter based on location
//Step 2: Next filter on the basis of searched text
.where({
$text: {
$search: < searchTerm >
}
}, {
score: {
$meta: 'textScore'
}
})
//Step 3: Filter any document fields that should not be returned in the result
.select({
"val1": 0,
"val2": 0
});
//Execute the query
query.exec(function (err, result) {
if (err) {
//return error in the response
}
//return result object in the response
});
In this code "Post" is a mongoose schema something like below
var PostSchema = new Schema({
title: String,
description: String,
loc: {
type: [Number], // [<longitude>, <latitude>]
index: '2d' // create the geospatial index
}
//some other fields
}
module.exports = mongoose.model('Post', PostSchema);
Also for the search area, there are other options available like box
var lowerLeft = [40.73083, -73.99756]
var upperRight= [40.741404, -73.988135]
query.where('loc').within().box(lowerLeft, upperRight)
For both Geolocation search and text search to work, make sure to have index on loc field and text field. More details here. loc Search and text search
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With