MongoDB - фильтрация содержимого внутреннего массива в наборе результатов

Я новичок в MongoDB и не знаю, как решить следующую проблему:

У меня есть коллекция документов, как это:

{
 "URL": "www.stackru.com",
 "TAGS": [
         {"NAME": "question", "VOTES": 3},
         {"NAME": "answer", "VOTES": 5},
         {"NAME": "problem", "VOTES": 2}
         ]
}

Прежде всего, я хотел, чтобы все URL-адреса имели все теги, приведенные в списке. Я решил это путем запроса:

db.links.find( { "Tags.Name" : { $all: ["question","answers"] } } );

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

Результат, который я ищу:

{
 "URL": "www.stackru.com",
 "TAGS": [{"NAME": "question", "VOTES": 3},
         {"NAME": "answer", "VOTES": 5}]
}

и не:

{
 "URL": "www.stackru.com",
 "TAGS": [{"NAME": "question", "VOTES": 3},
         {"NAME": "answer", "VOTES": 5},
         {"NAME": "problem", "VOTES": 2}]
}

Потому что я попросил только теги ["question","answers"].

Я думал об использовании MapReduce или парсинге набора результатов, но я не знаю, является ли это правильным способом решения проблемы. Может быть, есть встроенная функция, которая решает ее более эффективно.

Спасибо!

6 ответов

Решение

Спасибо, Роберт. Я понял, что то, что я ищу, в данный момент не реализовано. Вот ссылка на вопрос. Я надеюсь, что MongoDB cominuty реализует это в короткие сроки. Спасибо!

Вы можете использовать структуру агрегации MongoDB,

Если у вас есть документ в вашей коллекции, как;

{
 "URL": "www.stackru.com",
 "TAGS": [
         {"NAME": "question", "VOTES": 3},
         {"NAME": "answer", "VOTES": 5},
         {"NAME": "problem", "VOTES": 2}
         ]
}

и вы хотите отфильтровать некоторые элементы массива, вы можете использовать образец агрегации;

db.sof_table.aggregate
([
{$unwind:'$TAGS'}, 
{$match:{'TAGS.NAME':{$in:['answer','question']}}},
{$group:{_id:'$URL',TAGS:{$push:'$TAGS'}}}
])

Это приведет к;

{
    "result" : [
        {
            "_id" : "www.stackru.com",
            "TAGS" : [
                {
                    "NAME" : "question",
                    "VOTES" : 3
                },
                {
                    "NAME" : "answer",
                    "VOTES" : 5
                }
            ]
        }
    ],
    "ok" : 1
}

как ваш ожидаемый результат.

Вообще говоря любой find() операция MongoDB возвращает все документы, соответствующие запросу, и все документы извлекаются полностью. Если вы хотите только определенный раздел документа, то вы должны выполнить эту обработку на стороне клиента.

Это принципиальное различие между базами данных документов и базами данных SQL. Как правило, в базе данных документов запрос возвращает все документы, которые ему соответствуют, тогда как в базе данных SQL вы можете выбрать возврат только части таблицы. Если, конечно, вы не говорите, что делаете MapReduce, но это похоже на избыточность для вашего варианта использования.

Не для того, чтобы отговорить вас использовать MongoDB, но независимо от того, над каким проектом вы работаете, подумайте, действительно ли базы данных NoSQL отвечают всем требованиям (отвечают ли они требованиям, которые не может выполнить SQL), или вам лучше работать с традиционной базой данных SQL.

Можно подавлять ключи и элементы массива в возвращаемом документе, но не так, как вы хотите.

В вашем примере вы можете подавить URL-ключ следующим запросом, который использует второй аргумент для find():

db.links.find({"TAGS.NAME" : {$all : ["question","answer"]}}, {"URL" : 0})

Однако я не верю, что можно подавить отдельные элементы массива на стороне сервера с помощью find (), основываясь на том, какие члены массива были указаны с помощью $all.

Вы можете использовать $slice для возврата только определенных членов массива, но он основан на позиции. Например,

{$slice : [1, 2]}

пропускает первый элемент массива и возвращает до следующих двух.

Меня просто отсылали к этому разговору по ссылке . Это работает, но, оглядываясь назад, настоящая проблема заключалась в том, что тогда я не понимал MongoDB.

Мой вывод: если вы обнаружите, что фильтруете встроенные массивы в MongoDB, это, вероятно, означает, что вы не понимаете MongoDB.

Официальное решение этой проблемы

Официальная рекомендация — отдавать предпочтение встраиванию данных, а не их ссылке или фильтрации.

Да, это означает, что вы должны дублировать данные. Да, это означает, что вы делаете свою БД менее абстрактной и более адаптированной к вашему конкретному решению.

И да, это может показаться странным из-за SQL.

Значение...

Мое предложение состояло бы в том, чтобы создать новую коллекцию для ответов и встроить правильные в объект URL. Это называется «шаблон встроенного подмножества» .

Техническое, неправильное решение

Тогда, прежде чем MongoDB щелкнул за меня, мне удалось отфильтровать внутренние массивы на стороне сервера, переопределив свойство, хранящее массив, с его отфильтрованным подмножеством.

на собственное решение проблемыВы можете прочитать об этом здесь . Опять же - хоть я и очень люблю свой хак, но не рекомендую. По сути, это памятник тому, как сильно я тогда не понимал MongoDB.

Это может помочь вам.

Оператор проекции $elemMatch принимает явный аргумент условия. Это позволяет вам проецировать на основе условия, отсутствующего в запросе, или если вам нужно проецировать на основе нескольких полей во встроенных документах массива. **

https://docs.mongodb.com/manual/reference/operator/projection/elemMatch/

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