mongoDB聚合:$ addToSet然后$ sort

时间:2019-06-01 18:06:37

标签: mongodb aggregation-framework

我正在尝试从mongoDB集合(使用nodeJS驱动程序)的多个字段中的数组中对唯一值进行排序。

一个小数据集:

[{
    "_id" : "5c93db3dd0184516406013f7",
    "filters" : {
        "genres" : [ 
            {
                "_id" : "9CXBYc4qP8sqcNMZ5",
                "fr" : "Art Abstrait",
                "en" : "Abstract Art",
                "de" : "Abstrakte Kunst",
                "it" : "Arte astratta",
                "es" : "Arte Abstracto"
            }
        ],
        "subjects" : [ 
            {
                "_id" : "3QjL6YSfmuY6NFHGG",
                "fr" : "Abstrait",
                "en" : "Abstract",
                "de" : "Abstrakt",
                "it" : "Astratto",
                "es" : "Abstracto"
            }
        ],
        "type" : {
            "_id" : "CYK2WcepkJsy5xXMo",
            "fr" : "Gravure au carborundum",
            "en" : "Carborundum etching",
            "de" : "Carborundum Radierung",
            "it" : "Incisione carborandum",
            "es" : "Grabado al Carborundum"
        }
    }
},
{
    "_id" : "5c93db3ed0184516406013f8",
    "filters" : {
        "genres" : [ 
            {
                "_id" : "9CXBYc4qP8sqcNMZ5",
                "fr" : "Art Abstrait",
                "en" : "Abstract Art",
                "de" : "Abstrakte Kunst",
                "it" : "Arte astratta",
                "es" : "Arte Abstracto"
            }
        ],
        "subjects" : [ 
            {
                "_id" : "3QjL6YSfmuY6NFHGG",
                "fr" : "Abstrait",
                "en" : "Abstract",
                "de" : "Abstrakt",
                "it" : "Astratto",
                "es" : "Abstracto"
            }
        ],
        "type" : {
            "_id" : "CYK2WcepkJsy5xXMo",
            "fr" : "Gravure au carborundum",
            "en" : "Carborundum etching",
            "de" : "Carborundum Radierung",
            "it" : "Incisione carborandum",
            "es" : "Grabado al Carborundum"
        }
    }
},
{
    "_id" : "5c93e19ed018451640601da6",
    "filters" : {
        "genres" : [ 
            {
                "_id" : "9CXBYc4qP8sqcNMZ5",
                "fr" : "Art Abstrait",
                "en" : "Abstract Art",
                "de" : "Abstrakte Kunst",
                "it" : "Arte astratta",
                "es" : "Arte Abstracto"
            }
        ],
        "subjects" : [ 
            {
                "_id" : "3QjL6YSfmuY6NFHGG",
                "fr" : "Abstrait",
                "en" : "Abstract",
                "de" : "Abstrakt",
                "it" : "Astratto",
                "es" : "Abstracto"
            }
        ],
        "type" : {
            "_id" : "KfGWEHL2pAto8nfze",
            "fr" : "Gravure",
            "en" : "Etching",
            "de" : "Radierung",
            "it" : "Incisione",
            "es" : "Grabado"
        }
    }
}]

我的查询结果(带有lang = 'en'):

{
  "subjects": [
    {
      "_id": "3QjL6YSfmuY6NFHGG",
      "fr": "Abstrait",
      "en": "Abstract",
      "de": "Abstrakt",
      "it": "Astratto",
      "es": "Abstracto"
    },
    {
      "_id": "3QjL6YSfmuY6NFHGG",
      "fr": "Abstrait",
      "en": "Abstract",
      "de": "Abstrakt",
      "it": "Astratto",
      "es": "Abstracto"
    }
  ],
  "genres": [
    {
      "_id": "9CXBYc4qP8sqcNMZ5",
      "fr": "Art Abstrait",
      "en": "Abstract Art",
      "de": "Abstrakte Kunst",
      "it": "Arte astratta",
      "es": "Arte Abstracto"
    },
    {
      "_id": "9CXBYc4qP8sqcNMZ5",
      "fr": "Art Abstrait",
      "en": "Abstract Art",
      "de": "Abstrakte Kunst",
      "it": "Arte astratta",
      "es": "Arte Abstracto"
    }
  ],
  "types": [
    {
      "_id": "CYK2WcepkJsy5xXMo",
      "fr": "Gravure au carborundum",
      "en": "Carborundum etching",
      "de": "Carborundum Radierung",
      "it": "Incisione carborandum",
      "es": "Grabado al Carborundum"
    },
    {
      "_id": "KfGWEHL2pAto8nfze",
      "fr": "Gravure",
      "en": "Etching",
      "de": "Radierung",
      "it": "Incisione",
      "es": "Grabado"
    }
  ]
}

用于聚合的管道:

[
    { $unwind: '$filters.subjects' },
    { $unwind: '$filters.genres' },
    { $group: {
      _id: null,
      subjects: { $addToSet: '$filters.subjects' },
      types: { $addToSet: '$filters.type' },
      genres: { $addToSet: '$filters.genres' },
    }},
    { $unwind: '$subjects' },
    { $unwind: '$genres' },
    { $unwind: '$types' },
    { $sort: {
      [`subjects.${lang}`]: 1,
      [`types.${lang}`]: 1,
      [`genres.${lang}`]: 1,
    }},
    { $group: {
      _id: null,
      subjects: { $push: '$subjects' },
      types: { $push: '$types' },
      genres: { $push: '$genres' },
    }},
    { $project: {
      _id: false,
      subjects: '$subjects',
      types: '$types',
      genres: '$genres'
    }}
]

不是像这样获取唯一值的排序数组: [A, B, C, D, ...]

我得到具有非唯一值的排序数组,如下所示: [A, A, A, B, B, B, C, C, C, D, D, D, ...]

使$addToSet分组毫无用处。

关于我错了的任何主意吗?

1 个答案:

答案 0 :(得分:0)

您遇到的问题是每个$unwind都将使用要展开的数组中的单个数组元素来创建文档的副本。您具有以下条件:

...
{ $unwind: '$subjects' },
{ $unwind: '$genres' },
{ $unwind: '$types' },
...

因此,首先您要展开subjects,它会为subjects中的每个元素生成一个文档,我们将其称为subject。因此,我们为每个subject有一个文档,它们本身包含数组genrestypes。接下来展开genres时,每个subject文档都会展开,其中包含来自genre的元素genres。这样可以使用每个genres.length的{​​{1}}副本-也就是说,根据数组中有多少subject来复制每个主题。展开genres时也会发生类似情况。

简而言之,您是在每个types调用中复制数据。

以一个更简单的示例进行说明:

$unwind

要解决此问题,立即想到了两个选择:
 1.您可以// Doc: { ints: [1, 2], alpha: ['a', 'b', 'c'] } // Pipeline: [ { $unwind: "$ints" }, { $unwind: "$alpha" } ] // After unwinding "ints": [ { ints: 1, alpha: ['a', 'b', 'c'] }, { ints: 2, alpha: ['a', 'b', 'c'] } ] // After unwinding "alpha": [ { ints: 1, alpha: 'a' }, { ints: 1, alpha: 'b' }, { ints: 1, alpha: 'c' }, { ints: 2, alpha: 'a' }, { ints: 2, alpha: 'b' }, { ints: 2, alpha: 'c' } ] // Result: 3 duplicates of each value in "ints", 2 duplicates of each value in "alpha". 一个数组,$unwind,然后将$sort的结果$group个元素放回到一个数组中,对每个数组分别重复一次,一次。请注意,在分组时,您将需要使用$push运算符仅获取每个重复数组的一个副本。
 2.您可以将最后一个$first流水线阶段更改为使用$group而不是$addToSet操作。

您可能还可以使用其他选项,但是以上两种方法都可以满足您的快速需求。