Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

MongoDB: Combine Text Search and Geospatial Query

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.

like image 267
Leonidas Avatar asked Jun 02 '13 13:06

Leonidas


People also ask

Does MongoDB support geospatial?

In MongoDB, you can store geospatial data as GeoJSON objects or as legacy coordinate pairs.

How do I search for text in MongoDB?

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.

How does MongoDB text search work?

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.

What is $text in MongoDB?

$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.


2 Answers

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

Solutions

  1. regex + near
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
}) 
  1. 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})
    
  2. 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
    });
});
like image 88
b4d4r Avatar answered Sep 24 '22 09:09

b4d4r


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

like image 31
Sauchin Avatar answered Sep 25 '22 09:09

Sauchin