MongoDb кроме эквивалента - PullRequest
       26

MongoDb кроме эквивалента

0 голосов
/ 14 сентября 2018

У меня есть вопрос о проблеме, с которой я столкнулся при попытке использовать $ setDifference для коллекции документов.

Все, что я хочу, это все документы, которые содержатся в Root 1, и удалить все документы, которыетакже включены в Root 2 на основе "reference.id".

Моя коллекция представляет две древовидные структуры и в основном выглядит следующим образом:

/* Tree Root 1 */
{
    "_id" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "name" : "Root 1",
    "children" : [ 
        LUUID("ca01f1ab-7c32-4e6b-a07a-e0ee9d8ec5ac"), 
        LUUID("6dd8c8ed-4a60-41ca-abf1-a4d795a0c213")
    ]
},
/* Child 1 - Root 1 */
{
    "_id" : LUUID("ca01f1ab-7c32-4e6b-a07a-e0ee9d8ec5ac"),
    "parentId" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "reference" : {
        "type" : "someType",
        "id" : LUUID("331503FB-C4D1-4F7A-A461-933C701EF9AB")
    },
    "rootReferenceId" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "name" : "Child 1 (Root 1)"
}
/* Child 2 - Root 1 */
{
    "_id" : LUUID("6dd8c8ed-4a60-41ca-abf1-a4d795a0c213"),
    "parentId" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "reference" : {
        "type" : "someType",
        "id" : LUUID("23E8B540-3EFB-455A-AA5C-2B67D6B59943")
    },
    "rootReferenceId" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "displayName" : "Child 2 (Root 1)"
}
/* Tree Root 2 */
{
    "_id" : LUUID("27f2b4a6-5471-406a-a39b-1e0b0f8c4eb9"),
    "name" : "Root 2",
    "children" : [ 
        LUUID("ad4ad076-322e-4c26-8855-91c9b1912d1f"), 
        LUUID("66452420-dd2f-4d27-91c9-78bd0990817c")
    ]
},
/* Child 1 - Root 2 */
{
    "_id" : LUUID("ad4ad076-322e-4c26-8855-91c9b1912d1f"),
    "parentId" : LUUID("27f2b4a6-5471-406a-a39b-1e0b0f8c4eb9"),
    "reference" : {
        "type" : "someType",
        "id" : LUUID("331503FB-C4D1-4F7A-A461-933C701EF9AB")
    },
    "rootReferenceId" : LUUID("27f2b4a6-5471-406a-a39b-1e0b0f8c4eb9"),
    "displayName" : "Child 1 (Root 2)"
}

Это означает, что в конце я хочуиметь документ:

/* Child 2 - Root 1 */
{
    "_id" : LUUID("6dd8c8ed-4a60-41ca-abf1-a4d795a0c213"),
    "parentId" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "reference" : {
        "type" : "someType",
        "id" : LUUID("23E8B540-3EFB-455A-AA5C-2B67D6B59943")
    },
    "rootReferenceId" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "displayName" : "Child 2 (Root 1)"
}

Поскольку его reference.id содержится в корне 1, но не в корне 2 (поэтому он не будет исключен из результирующего набора, такого как дочерний элемент 1)

Iуже написал этап агрегации для группировки «reference.id» следующим образом:

db.getCollection('test').aggregate([
    {
        $match: {
            rootReferenceId: { $ne: null }
        }
    },
    {
        $group: {
            _id: "$rootReferenceId",
            referenceIds: { $addToSet: "$reference.id" } 
        }
    }
])

Что мне возвращает это:

/* 1 */
{
    "_id" : LUUID("27f2b4a6-5471-406a-a39b-1e0b0f8c4eb9"),
    "referenceIds" : [ 
        LUUID("331503fb-c4d1-4f7a-a461-933c701ef9ab")
    ]
}

/* 2 */
{
    "_id" : LUUID("9f3a73df-bca7-48b7-b111-285359e50a02"),
    "referenceIds" : [ 
        LUUID("23e8b540-3efb-455a-aa5c-2b67d6b59943"), 
        LUUID("331503fb-c4d1-4f7a-a461-933c701ef9ab")
    ]
}

Кто-нибудь знает, как я могу спроецировать этов формат, который принимает $ setDifference?

Я думаю, он должен выглядеть следующим образом:

{
    LUUID("27f2b4a6-5471-406a-a39b-1e0b0f8c4eb9") : [ 
        LUUID("331503fb-c4d1-4f7a-a461-933c701ef9ab")
    ]
    LUUID("9f3a73df-bca7-48b7-b111-285359e50a02") : [ 
        LUUID("23e8b540-3efb-455a-aa5c-2b67d6b59943"), 
        LUUID("331503fb-c4d1-4f7a-a461-933c701ef9ab")
    ]
}

Или это совершенно другой способ добиться этого, о котором я не знаю?

Любая помощь приветствуется!

Изменить решение:

Решение теперь похоже на dnickless.Действительно хороший!Большое спасибо за это!

Ответы [ 2 ]

0 голосов
/ 15 сентября 2018

Вот что вы можете сделать, не сохраняя повторяющиеся значения в строковом формате. Что хорошего в этом решении, так это

a) он возвращает весь интересующий вас документ, поэтому вам не нужен второй запрос (если вам не нужен весь документ, оператор $filter можно просто заменить битом $setDifference)

b) он состоит из очень небольшого количества дешевых этапов (без группировки!) И будет использовать индексы в поле rootReferenceId (если есть какие-либо, которые я бы порекомендовал).

db.getCollection('test').aggregate([
  { "$facet": {
    "allInRoot1": [{
      "$match": { "rootReferenceId": LUUID("9f3a73df-bca7-48b7-b111-285359e50a02") }
    }],
    "allInRoot2": [{
      "$match": { "rootReferenceId": LUUID("27f2b4a6-5471-406a-a39b-1e0b0f8c4eb9") }
    }]
  }}, {
    "$project": {
      "difference": {
        "$filter": {
            "input": "$allInRoot1",
            "as": "this",
            "cond": { "$in": [ "$$this.reference.id", { "$setDifference": [ "$allInRoot1.reference.id", "$allInRoot2.reference.id" ] } ] }
        }
      }
    }
  }
])
0 голосов
/ 14 сентября 2018

Вы можете попробовать агрегацию ниже в mongodb 3.6 и выше.

 db.getCollection('test').aggregate([
  { "$match": { "rootReferenceId": { "$ne": null }}},
  { "$group": {
    "_id": "$rootReferenceId",
    "referenceIds": { "$addToSet": "$reference.id" } 
  }},
  { "$group": {
    "_id": null,
    "data": {
      "$push": { "k": { "$toString": "$_id" }, "v": "$referenceIds" }
    }
  }},
  { "$replaceRoot": { "newRoot": { "$arrayToObject": "$data" }}}
])
...