В MongoDB поиск в массиве и сортировка по количеству совпадений

Вопрос следующий:

Получить документы с тегами в списке, упорядоченные по общему количеству совпадений

Но говорят, что можно с помощью Aggregation Framework, это возможно?


person Wiliam    schedule 13.09.2012    source источник


Ответы (2)


Да, это возможно с помощью Aggregation Framework.

Предположения

Запрос

Этот подход вынуждает вас раскручивать результаты и переоценивать предикат совпадения с раскрученными результатами, поэтому он действительно неэффективен.

db.test_col.aggregate(
    {$match: {tags: {$in: ["shirt","cotton","black"]}}}, 
    {$unwind: "$tags"}, 
    {$match: {tags: {$in: ["shirt","cotton","black"]}}}, 
    {$group: {
        _id:{"_id":1}, 
        matches:{$sum:1}
    }}, 
    {$sort:{matches:-1}}
);

Ожидаемые результаты

{
    "result" : [
        {
            "_id" : {
                "_id" : ObjectId("5051f1786a64bd2c54918b26")
            },
            "matches" : 3
        },
        {
            "_id" : {
                "_id" : ObjectId("5051f1726a64bd2c54918b24")
            },
            "matches" : 2
        },
        {
            "_id" : {
                "_id" : ObjectId("5051f1756a64bd2c54918b25")
            },
            "matches" : 1
        }
    ],
    "ok" : 1
}
person Samuel García    schedule 13.09.2012
comment
Неэффективно? Я делаю предложение, мне нужно, чтобы это было как можно быстрее, вы думаете, что это можно сделать лучше? - person Wiliam; 13.09.2012
comment
Нет, нет лучшего способа, используя эту схему, сделать это. Это действительно намного быстрее, чем подход MR. Обратите внимание, что если у вас есть миллионы (совпадающих) документов, это раскручивает их, создавая в памяти миллионы * avg_tags_size. Aggregation Framework имеет ограничения на использование памяти. Используйте его с осторожностью. - person Samuel García; 13.09.2012
comment
Я пробовал, я использую v2.2, поэтому мне пришлось изменить _id:{_id:1} на _id:{_id:$_id} и удалить второе совпадение $. Это работало нормально, но недостаточно быстро, спасибо :) (КОД PHP: gist.github.com/41d31bccd0cc3814fdda) - person Wiliam; 14.09.2012
comment
Да, я использовал синтаксис 2.1 (версия для разработчиков). В MongoDB 2.2 объявление группы _id изменилось. Я отредактировал в соответствии с вашим комментарием. - person Samuel García; 14.09.2012
comment
Вы обеспечили индексацию тегов attr? Сколько документов в вашей коллекции? - person Samuel García; 14.09.2012
comment
Я проверил это снова, и требуется второе совпадение. Если у вас есть документ соответствия с тегами: [рубашка, дом, собака, машина, небо] без второго пункта соответствия, он будет отсортирован первым, и только один тег действительно соответствует. - person Samuel García; 14.09.2012
comment
Документ содержит 120 000 документов со средним числом тегов 3-5 на документ. Теги имеют индекс. Это занимает ~ 5 с, а с обычным поиском и оператором $ all - ‹ 1 мс. - person Wiliam; 14.09.2012
comment
Сколько документов соответствует вашему запросу? Это нормально, это занимает больше времени, так как второе совпадение вообще не использует индекс. Групповой алгоритм тоже не быстрый. - person Samuel García; 14.09.2012
comment
красиво сделано. понравилась идея. использовал его в другом месте. спасибо - person Visakh Vijayan; 21.06.2021

Использование $size и $setIntersection эффективно решит эту проблему, не вызывая умножения памяти.

tagList = ['shirt', 'cotton', 'black']

db.test_col.aggregate(
    {$match: {tags: {$in: ["shirt","cotton","black"]}}}, 
    {$project: 
        {"title":1,"tags":1}, 
        {$order: 
            {"$size": 
                {"$setIntersection": [ tagList, "$tags" ]}}, 
    {$sort:{order:-1}}
    );
  1. Сначала мы сопоставляем документы, у которых есть хотя бы одно совпадение элементов.

  2. Затем мы проецируем нужные нам ключи/столбцы вместе с новым ключом/столбцом порядка. Порядок создается путем подсчета пересекающихся элементов между «тегами в базе данных» и «тегами из запроса».

  3. Затем делаем простую сортировку по убыванию. Это сработало для меня. Ответ на аналогичный вопрос здесь

person Hemant Hadawale    schedule 03.06.2020