Найти повторяющиеся значения внутри массива в Mon go DB, но он может присутствовать вне объекта - PullRequest
1 голос
/ 12 марта 2020
{
    "_id" : ObjectId("15672"),
    "userName" : "4567",
    "library" : [ 
        {
            "serialNumber" : "Book_1"
        }, 
        {
            "serialNumber" : "Book_2"
        }, 
        {
            "serialNumber" : "Book_4"
        }
    ]
},
{
    "_id" : ObjectId("123456"),
    "userName" : "123",
    "library" : [ 
        {
            "serialNumber" : "Book_2"
        }
    ]
},
{
    "_id" : ObjectId("1835242"),
    "userName" : "13526",
    "library" : [ 
        {
            "serialNumber" : "Book_7"
        }, 
        {
            "serialNumber" : "Book_6"
        }, 
        {
            "serialNumber" : "Book_5"
        }, 
        {
            "serialNumber" : "Book_4"
        }, 
        {
            "serialNumber" : "Book_3"
        }, 
        {
            "serialNumber" : "Book_5"
        }
    ]
}

Я хочу запрос, который даст мне имя пользователя, в котором значения serialNumber повторяются. Значения серийного номера в одной библиотеке могут присутствовать в другой библиотеке имен пользователей, но их не должно быть в одной конкретной библиотеке имен пользователей

1 Ответ

1 голос
/ 13 марта 2020

Попробуйте этот запрос:

db.collection.aggregate([
    /** First match stage is optional if all of your docs are of type array & not empty */
    { $match: { $expr: { $and: [{ $eq: [{ $type: "$library" }, "array"] }, { $ne: ["$library", []] }] } } },
    /** Add a new field allUnique to each doc, will be false where if elements in library have duplicates */
    {
        $addFields: {
            allUnique: {
                $eq: [
                    {
                        $size:
                        {
                            $reduce: {
                                input: "$library.serialNumber",
                                initialValue: [], // start with empty array
                                /** iterate over serialNumber's array from library & push current value if it's not there in array, at the end reduce would produce an array with uniques */
                                in: { $cond: [{ $in: ["$$this", "$$value"] }, [], { $concatArrays: [["$$this"], "$$value"] }] }
                            }
                        }
                    },
                    {
                        $size: "$library"
                    }
                ]
            }
        }
    },
    /** get docs where allUnique: false */
    {
        $match: {
            allUnique: false
        }
    },
    /** Project only needed fields & remove _id which is bydefault projected */
    {
        $project: {
            userName: 1,
            _id: 0
        }
    }
])

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

Тест : MongoDB-Playground

Или из ответа @Dennis по этой ссылке дубликаты-записи-из-массива , вы можете попробовать, как показано ниже:

db.collection.aggregate([
  {
    $match: {
      $expr: {
        $and: [
          {
            $eq: [
              {
                $type: "$library"
              },
              "array"
            ]
          },
          {
            $ne: [
              "$library",
              []
            ]
          }
        ]
      }
    }
  },
  {
    $addFields: {
      allUnique: {
        $eq: [
          {
            $size: {
              "$setUnion": [
                "$library.serialNumber",
                []
              ]
            }
          },
          {
            $size: "$library"
          }
        ]
      }
    }
  },
  {
    $match: {
      allUnique: false
    }
  },
  {
    $project: {
      userName: 1,
      _id: 0
    }
  }
])

Тест: MongoDB-Playground

...