Найти наиболее часто встречающиеся слова существуют в поле 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
операция идет через каждый документ и составляет оценку. Затем "редуктор" сортирует результаты и просто сохраняет тот, у которого самый высокий балл.
Обратите внимание, что "редуктор" может быть вызван много раз, поэтому он "не" пытается сортировать все документы в коллекции одновременно. Но это все еще действительно "грубая сила".