Используйте значения массива как путь для сопоставления документов в mongodb

Учитывая перечень документов, таких как:

[
  {
    _id: 1,
    field: {
      subfield1: {
        subsubfield1: [
          "value",
          "test"
        ]
      },
      subfield2: {
        subsufield2: [
          "value"
        ]
      }
    },
    paths: [
      "field.subfield1.subsubfield1",
      "field.subfield2.subsubfield2"
    ]
  },
  {
    _id: 2,
    field: {
      subfield1: {
        subsubfield1: [
          "value"
        ]
      },
      subfield2: {
        subsufield2: [
          "value",
          "test"
        ]
      }
    },
    paths: [
      "field.subfield2.subsubfield2"
    ]
  },
  {
    _id: 3,
    field: {
      subfield1: {
        subsubfield1: [
          "value"
        ]
      },
      subfield2: {
        subsufield2: [
          "value"
        ]
      }
    },
    paths: [
      "field.subfield1.subsubfield1"
    ]
  },
  {
    _id: 4,
    field: {
      subfield1: {
        subsubfield1: [
          "value"
        ]
      },
      subfield2: {
        subsufield2: [
          "value"
        ]
      }
    },
    paths: [
      "field.subfield1.subsubfield1"
      "field.subfield2.subsubfield2"
    ]
  }
]

в котором поле paths представляет собой массив с путями к документу. Мне нужно вернуть документы, которые имеют значение test хотя бы в одном из путей, указанных в массиве paths. Например, будут возвращены документы с _id 1 и _id 2, поскольку хотя бы одно из значений paths является путем в документе со значением test. Документы с _id 3 и _id 4 не возвращаются, поскольку ни один элемент paths не является путем в документе, имеющим значение test.

🤔 А знаете ли вы, что...
MongoDB поддерживает аутентификацию и авторизацию пользователей для обеспечения безопасности данных...


1
50
1

Ответ:

Решено

Обновлено: поскольку OP обновил предположение, что поле всегда вложено в 3 уровня. Это фактически упрощает сценарий и, следовательно, конвейер агрегации.

  1. используйте $objectToArray для анализа объекта field как массива кортежа k-v с именем parsed
    • используйте $map для перебора массива parsed
    • слегка повозитесь с k, чтобы добавить field. в качестве префикса
  2. повторите разбор
  3. $match только если какой-либо элемент массива parsed находится внутри массива paths и имеет значение test
db.collection.aggregate([
  {
    "$set": {
      "parsed": {
        "$map": {
          "input": {
            "$objectToArray": "$field"
          },
          "as": "kv",
          "in": {
            k: {
              "$concat": [
                "field",
                ".",
                "$$kv.k"
              ]
            },
            v: "$$kv.v"
          }
        }
      }
    }
  },
  {
    "$set": {
      "parsed": {
        "$reduce": {
          "input": "$parsed",
          "initialValue": [],
          "in": {
            "$concatArrays": [
              "$$value",
              {
                "$map": {
                  "input": {
                    "$objectToArray": "$$this.v"
                  },
                  "as": "kv",
                  "in": {
                    k: {
                      "$concat": [
                        "$$this.k",
                        ".",
                        "$$kv.k"
                      ]
                    },
                    v: "$$kv.v"
                  }
                }
              }
            ]
          }
        }
      }
    }
  },
  {
    "$match": {
      "$expr": {
        "$anyElementTrue": {
          "$map": {
            "input": "$parsed",
            "as": "p",
            "in": {
              //1. matching
              "$and": [
                // 1.1 matching path
                {
                  "$in": [
                    "$$p.k",
                    "$paths"
                  ]
                },
                // 1.2 matching value "test"
                {
                  "$in": [
                    "test",
                    "$$p.v"
                  ]
                }
              ]
            }
          }
        }
      }
    }
  }
])

Игровая площадка Монго