Reputation: 2160
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.
Upvotes: 14
Views: 3697
Reputation: 153
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
})
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
});
});
Upvotes: 6
Reputation: 161
Full text search is possible with geospatial.
You can try this in mongo shell:
db.collection.aggregate([
{ "$geoNear": {
"near": {
"type": "Point",
"coordinates": [
-73.86,
41.076
]
},
"spherical": true,
"maxDistance": 600,
"distanceField": "distance",
"query": {
"fieldname": /querystring/
}
}}
])
Using the full text search:
db.collection.find({
"$text": { "$search": "query string" },
"location": { // this will your field name
"$geoWithin": {
"$centerSphere": [[
-73.86,
43.03
], 500 ]
}
}
})
Upvotes: 0
Reputation: 333
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
Upvotes: 5
Reputation: 1368
You can filter a text search by adding a filter parameter (I'm using node-mongodb-native):
db.command({ text: 'thecollection', search: searchString, filter: query }, function(err, o) {
if (err) throw err;
var results = o.results.map(function(result) { return result.obj });
// process the response ...
});
If it is a rough geographical search you want to do, you can filter your results by defining a min and max around your latitude longitude:
var distances = {'200m': 0.002, '500m': 0.005, '1km': 0.01, '5km': 0.05 }
, distance = distances[reqDis];
var query = {
latitude: { $lt: reqLat+distance, $gt: reqLat-distance },
longitude: { $lt: reqLng+distance, $gt: reqLng-distance }
};
// put the query in the db.command as described above
Here is something on the subject in the mongodb documentation: http://docs.mongodb.org/manual/tutorial/limit-number-of-items-scanned-for-text-search/
Upvotes: 3