#regex #mongodb #aggregation-framework
#регулярное выражение #mongodb #агрегация-фреймворк
Вопрос:
Я ищу способ подсчитать количество тегов, которые существуют для документов.
Данные выглядят следующим образом:
[
{
"_id": ObjectId("...."),
"tags": ["active-adult", "active-tradeout"]
},
{
"_id": ObjectId("...."),
"tags": ["active-child", "active-tradeout", "active-junk-tag"]
},
{
"_id": ObjectId("...."),
"tags": ["inactive-adult"]
}
]
Вот как я хотел бы, чтобы результат агрегации выглядел:
[
{
"_id": "active",
"total": 2,
"subtags": {
"adult": 1,
"child": 1,
"tradeout": 2,
"junk-tag": 1
}
},
{
"_id": "inactive",
"total": 1,
"subtags": {
"adult": 1
}
}
]
Я знаю, что могу посчитать теги, но я ищу регулярное выражение
db.User.aggregate([
{$unwind: "$tags"},
{$group: {_id: "$tags", total: {$sum: 1}}}
])
Комментарии:
1. Я бы не стал использовать регулярные выражения для встроенных структур, таких как таблицы стилей или XML-файлы. Вместо этого используйте алгоритм или, по крайней мере, программу GREP, которая может работать с текстом за несколько шагов
2. Да, именно так я это и делал, но было любопытно, есть ли способ сделать это с помощью платформы агрегации.
Ответ №1:
Вы можете выполнить небольшую обработку строк с $substr
$cond
помощью операторов и, чтобы получить желаемый результат (нет необходимости в регулярных выражениях). Для этого потребуется MongoDB 2.6 :
db.User.aggregate([
{ $unwind : "$tags"},
{ $project : {
tagType : {
$cond : {
if : { $eq : [ { $substr : [ "$tags", 0, 6] }, "active" ]},
then: "active",
else: "inactive"}
},
tag: {
$cond : {
if : { $eq : [ { $substr : [ "$tags", 0, 6] }, "active" ]},
then: { $substr : ["$tags", 7, -1]},
else: { $substr : ["$tags", 9, -1]}}
}
}},
{ $group : { _id : {tagType : "$tagType", tag: "$tag"} ,
total: { $sum: 1}}},
{ $group : { _id : "$_id.tagType",
subtags: { $push : {tag : "$_id.tag", total: "$total"}},
total: { $sum : "$total"}}}
]);
Результатом этого запроса будет следующее:
{
"_id" : "inactive",
"subtags" : [
{
"tag" : "adult",
"total" : 1
}
],
"total" : 1
}
{
"_id" : "active",
"subtags" : [
{
"tag" : "junk-tag",
"total" : 1
},
{
"tag" : "child",
"total" : 1
},
{
"tag" : "tradeout",
"total" : 2
},
{
"tag" : "adult",
"total" : 1
}
],
"total" : 5
}
Редактировать:
Я только что заметил, что итог в результате подсчитывает общее количество тегов, а не количество документов, в которых был хотя бы один активный тег. Этот запрос даст вам точный результат, который вы хотели, хотя и немного сложнее:
db.User.aggregate([
/* unwind so we can process each tag from the array */
{ $unwind : "$tags"},
/* Remove the active/inactive strings from the tag values
and create a new value tagType */
{ $project : {
tagType : {
$cond : {
if : { $eq : [ { $substr : [ "$tags", 0, 6] }, "active" ]},
then: "active",
else: "inactive"}
},
tag: {
$cond : {
if : { $eq : [ { $substr : [ "$tags", 0, 6] }, "active" ]},
then: { $substr : ["$tags", 7, -1]},
else: { $substr : ["$tags", 9, -1]}}
}
}},
/* Group the documents by tag type, so we can
find num. of docs by tag type (total) */
{ $group : { _id : "$tagType",
tags :{ $push : "$tag"},
docId :{ $addToSet : "$_id"}}},
/* project the values so we can get the 'total' for tag type */
{ $project : { tagType : "$_id",
tags : 1,
"docTotal": { $size : "$docId" }}},
/* we must unwind to get total count for each tag */
{ $unwind : "$tags"},
/* sum the tags by type and tag value */
{ $group : { _id : {tagType : "$tagType", tag: "$tags"} ,
total: { $sum: 1}, docTotal: {$first : "$docTotal"}}},
/* finally group by tagType so we can get subtags */
{ $group : { _id : "$_id.tagType",
subtags: { $push : {tag : "$_id.tag", total: "$total"}},
total: { $first : "$docTotal"}}}
]);
Комментарии:
1. Ах, я вижу, так что было бы невозможно запустить это для неизвестного количества корневых тегов (т. Е. ‘active’, ‘active2’)? Это не условие, а скорее вопрос.
2. Вероятно, следует отметить, что для этого требуется
MongoDB 2.6
3. > Я только что заметил, что общее количество в результате подсчитывает общее количество тегов, а не количество документов На самом деле я искал количество документов с корневым тегом и вложенными тегами.
4. @RyanSchumacher этот подход был скорректирован в соответствии с вашим примером (потому что я думал, что у вас только два типа тегов), но, к сожалению, он не будет хорошо работать для неизвестного количества тегов.
5. @RyanSchumacher если вы можете каким-то образом предварительно обработать имена тегов (и, возможно, добавить «категорию» для каждого документа) при создании документа, тогда вы, вероятно, могли бы выполнить более простую агрегацию.