MongoDB — условные ограничения уникальности для разных полей «id»

У меня есть требование обеспечить уникальность документов в коллекции MongoDB.

В моих документах есть поле «id», поле «имя», а также несколько других полей. Поле «id» НЕ уникально.

Мое требование таково, что «имя» должно быть уникальным для разных значений «id». Однако может существовать несколько документов с одним и тем же «идентификатором» и одним и тем же «именем». (В одном и том же «id» может быть несколько «имен»).

Например:
Разрешены следующие документы (один и тот же идентификатор, одинаковые/разные имена):

{ "id" : "1", "name": "Alice"}  
{ "id" : "1", "name": "Bob"}  
{ "id" : "1", "name": "Alice"}

Однако следующее не разрешено (то же имя, разные идентификаторы):

{ "id" : "1", "name": "Alice"}  
{ "id" : "2", "name": "Alice"}

Можно ли реализовать это в самой базе данных, а не в логике приложения?


54
1

Ответ:

Решено

Я думаю, что невозможно «обеспечить соблюдение» правила с существующими данными только с помощью базы данных. Возможно, вам придется выполнить единоразовую очистку, если у вас есть данные, которые нарушают правило. Однако для вновь вставленных документов вы можете выполнить следующие действия и объединить их в коллекцию.

  1. $unionWith новые документы, которые вы хотели вставить (вставив их $documents
  2. $setWindowFields для вычисления $denseRank внутри раздела name и в порядке id
    • В случае 1, если у них разные имена, они оба будут иметь ранг: 1.
    • В случае 2, если у них одинаковое имя и одинаковый идентификатор, они оба будут иметь ранг: 1.
    • В случае 3, если у них одинаковое имя, но разные идентификаторы, новый документ будет иметь рейтинг > 1.
  3. $match выбрать только документы с рангом: 1
  4. (косметика) $unset поле ранга
  5. $merge в коллекцию
db.collection.aggregate([
  {
    "$unionWith": {
      "coll": "collection",
      "pipeline": [
        {
          "$documents": [
            // documents to be inserted here
            {
              "id": "1",
              "name": "Alice"
            }
          ]
        }
      ]
    }
  },
  {
    "$setWindowFields": {
      "partitionBy": "$name",
      "sortBy": {
        "id": 1
      },
      "output": {
        "idRankInName": {
          "$denseRank": {}
        }
      }
    }
  },
  {
    "$match": {
      "idRankInName": 1
    }
  },
  {
    "$unset": "idRankInName"
  },
  {
    "$merge": {
      "into": "collection"
    }
  }
])

Mongo Playground для случаев 1 и 2
Игровая площадка Mongo для случая 3