MongoDB: объединение текстового поиска и геопространственного запроса

Мне было интересно, возможно ли комбинировать текстовый поиск и выполнить геопространственный запрос по результатам/как я это сделаю. Я использую Mongoose в настоящий момент, но не против использовать прямой Mongo для команд.

То, что я пытаюсь сделать, это позволить пользователю искать продукт и возвращать результаты, находящиеся в определенном месте. У меня две команды работают отдельно, но не могу понять, как их объединить.

Ответ 1

Mongo DB сам по себе не поддерживает текстовый и гео-поиск в одно и то же время. [Refer this] (http://docs.mongodb.org/manual/reference/limits/#Queries-cannot-use-both-text-and-Geospatial-Indexes)

Вы можете сделать это, выполнив

  • Попробуйте regex для текста и рядом для пространственного поиска.

    var 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})
    

`  3. Разделите документы на части с помощью mapreduce. Отфильтруйте doucmets либо путем поиска по тексту/гео и сохраните его в новой коллекции. Пройдите через новую коллекцию и найдите точную коллекцию с помощью фильтра leftig.

    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
        });
    });

Ответ 2

Это очень распространенное требование, когда гео-фильтр и текстовый поиск требуются в одном случае, к сожалению, пока не поддерживается mongodb.

Ниже код использует драйвер mongoose, сначала фильтрует документы на основе местоположения (долготы и широты), а затем дополнительно фильтрует на основе найденного термина.

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
});

В этом коде "Почта" есть схема мангуста, что-то вроде ниже

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);

Также для области поиска есть другие доступные опции, например box

var lowerLeft = [40.73083, -73.99756]
var upperRight= [40.741404,  -73.988135]
query.where('loc').within().box(lowerLeft, upperRight)

Для поиска геолокации и текстового поиска убедитесь, что у вас есть индекс для поля loc и текстового поля. Подробнее здесь. loc Поиск и текстовый поиск

Ответ 3

Вы можете отфильтровать текстовый поиск, добавив параметр фильтра (я использую 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 ...

  });

Если это грубый географический поиск, который вы хотите сделать, вы можете отфильтровать результаты, указав минимальные и максимальные значения долготы широты:

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

В документации mongodb есть что-то по этому поводу: http://docs.mongodb.org/manual/tutorial/limit-number-of-items-scanned-for-text-search/

Ответ 4

Полнотекстовый поиск возможен с геопространственным.

Вы можете попробовать это в оболочке mongo:

db.collection.aggregate([
    { "$geoNear": {
        "near": {
            "type": "Point",
            "coordinates": [
               -73.86,
               41.076
            ]
        },
        "spherical": true,
        "maxDistance": 600,
        "distanceField": "distance",
        "query": {
           "fieldname": /querystring/
        }
    }}
])

Использование полнотекстового поиска:

db.collection.find({
    "$text": { "$search": "query string" },
    "location": {    // this will your field name
        "$geoWithin": {
            "$centerSphere": [[
               -73.86,
               43.03
            ], 500 ]
        }
    }
})