Найти наиболее часто встречающиеся слова существуют в поле Mongodb

У меня есть коллекция A и массив B какая структура, как показано ниже:

A:

{
    "_id" : ObjectId("5160757496cc6207a37ff778"),
    "name" : "Pomegranate Yogurt Bowl",
    "description" : "A simple breakfast bowl made with Greek yogurt, fresh pomegranate juice, puffed quinoa cereal, toasted sunflower seeds, and honey."
},
{
  "_id": ObjectId("5160757596cc62079cc2db18"),
  "name": "Krispy Easter Eggs",
  "description": "Imagine the Easter Bunny laying an egg.     Wait. That’s not anatomically possible.     And anyway, the Easter Bunny is a b..."
}

B:

var names = ["egg", "garlic", "cucumber", "kale", "pomegranate", "sunflower", "fish", "pork", "apple", "sunflower", "strawberry", "banana"]

Моя цель - вернуть один документ из A который имеет наибольшее количество встречающихся слов в массиве B, В этом случае он должен вернуть первый "_id" : ObjectId("5160757496cc6207a37ff778"),

Я не уверен, как решить эту проблему:

Это не работает:

db.A.find({
    "description": {
      "$in": names
    }
  }, function(err, data) {
    if (err) console.log(err);
    console.log(data);
  });

1 ответ

Решение

Это зависит от вида "слов", которые вы хотите выбросить, и от того, считаются ли они "стоп-словами", например: "a", "the", "with" и т.д. или если количество этих вещей действительно не имеет значения.

Если они не имеют значения, то рассмотрим $text индекс и поиск.

Первый индекс:

db.A.createIndex({ "name": "text", "description": "text" })

А затем просто построить поиск:

var words = [
  "egg", "garlic", "cucumber", "kale", "pomegranate",
  "sunflower", "fish", "pork", "apple", "sunflower",
  "strawberry", "banana"
];

var search = words.join(" ")

db.A.find(
    { "$text": { "$search": search } },
    { "score": { "$meta": "textScore" } }
).sort({ "score": { "$meta": "textScore" }}).limit(1)

Возвращает первый документ так:

{
    "_id" : ObjectId("5160757496cc6207a37ff778"),
    "name" : "Pomegranate Yogurt Bowl",
    "description" : "A simple breakfast bowl made with Greek yogurt, fresh pomegranate juice, puffed quinoa cereal, toasted sunflower seeds, and honey.",
    "score" : 1.7291666666666665
}

С другой стороны, если вам нужно посчитать "стоп-слова", то mapReduce Можно найти результат для вас:

db.A.mapReduce(
  function() {
    var words = [
      "egg", "garlic", "cucumber", "kale", "pomegranate",
      "sunflower", "fish", "pork", "apple", "sunflower",
      "strawberry", "banana"
    ];

    var count = 0;

    var fulltext = this.name.toLowerCase() + " " + this.description.toLowerCase();

    // Increment count by number of matches
    words.forEach(function(word) {
      count += ( fulltext.match(new RegExp(word,"ig")) || [] ).length;
    });

    emit(null,{ count: count, doc: this });

  },
  function(key,values) {
    // Sort largest first, return first
    return values.sort(function(a,b) {
      return a.count < b.count;
    })[0];
  },
  { "out": { "inline": 1 } }
)

С результатом:

{
    "_id" : null,
    "value" : {
        "count" : 4,
        "doc" : {
            "_id" : ObjectId("5160757496cc6207a37ff778"),
            "name" : "Pomegranate Yogurt Bowl",
            "description" : "A simple breakfast bowl made with Greek yogurt, fresh pomegranate juice, puffed quinoa cereal, toasted sunflower seeds, and honey."
        }
    }
}

Таким образом, "текстовый" индексный подход представляет собой "взвешивание" по количеству совпадений, а затем возвращает только наибольшее взвешенное совпадение.

mapReduce операция идет через каждый документ и составляет оценку. Затем "редуктор" сортирует результаты и просто сохраняет тот, у которого самый высокий балл.

Обратите внимание, что "редуктор" может быть вызван много раз, поэтому он "не" пытается сортировать все документы в коллекции одновременно. Но это все еще действительно "грубая сила".

Другие вопросы по тегам