агрегация mongoDB: $ addToSet затем $ sort - PullRequest
2 голосов
/ 01 июня 2019

Я пытаюсь отсортировать уникальные значения в массивах в нескольких полях из коллекции 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 Ответ

1 голос
/ 02 июня 2019

Проблема, с которой вы сталкиваетесь, заключается в том, что каждый $unwind собирается создать копию документа с одним элементом массива из массива, который вы раскручиваете.У вас есть следующее:

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

Итак, сначала вы раскручиваете subjects, который создает документы для каждого элемента в subjects, который мы назовем subject.Таким образом, у нас есть документ для каждого subject, который сам содержит массивы genres и types.При раскручивании genres next, каждый документ subject становится лишним, и в нем содержится элемент genre из genres.Это дает использование genres.length копий каждого subject, то есть каждый предмет дублируется в зависимости от количества genres в массиве.Аналогичная ситуация возникает при раскручивании types.

Короче говоря, вы дублируете свои данные при каждом вызове $unwind.

Для иллюстрации на более простом примере:

// 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".

Чтобы решить эту проблему, сразу приходит на ум несколько вариантов:
1. Вы можете $unwind массив, $sort его и $group результаты для $push элементов обратно вмассив, повторяющийся для каждого массива индивидуально, по одному за раз.Обратите внимание, что вам нужно будет использовать оператор $first, чтобы получить только одну копию каждого дублированного массива при группировке.
2. Вы можете изменить свой последний $group этап конвейера, чтобы использовать $addToSet вместо $push операций.

Вероятно, вам доступны и другие варианты, но одного из вышеперечисленных должно хватить для быстрого выполнения.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...