MongoDB:将数组转换为CSV以获取复杂数据

时间:2018-10-22 21:15:46

标签: mongodb mongodb-query

我们有一个称为Notes的MongoDB文档,其中包含一个Layout部分和一个Data部分。 Data部分使用Layout部分来描述和标记字段。例如,这是注释文档的简化示例:

{
  "_id" : ObjectId("5aafefbecbf20b364c14d037"),
  "Title" : "Some Note Name",
    "CreatedDate" : ISODate("2018-10-22T13:12:20.343-04:00"),
  "Layout" : {
    "Name" : "Some Layout Name",
    "ComponentId" : "531a5112-2467-410c-a477-936c6527256b",
    "Tabs" : [
      {
        "Name" : "Some Tab Name",
        "Icon" : "tab",
        "Sections" : [
          {
            "Name" : "Some Section Name",
            "MappingId" : "SomeSectionId",
            "Sets" : [
              {
                "SetId" : NumberLong("1"),
                "Questions" : [
                  {
                    "MappingId" : "SomeShortAnswerId",
                    "Label" : "Some Short Answer Label",
                    "Set" : NumberLong("1"),
                    "QuestionType" : "ShortAnswer"
                  },
                  {
                    "MappingId" : "SomeMultipleChoiceId",
                    "Label" : "Some Multiple Choice Label",
                    "Set" : NumberLong("1"),
                    "QuestionType" : "MultipleChoice"
                  },
                  {
                    "MappingId" : "SomeYesNoId",
                    "Label" : "Some Yes No Label",
                    "Set" : NumberLong("1"),
                    "QuestionType" : "YesNo"
                  }
                ]
              }
            ]
          }
        ]
      }
    ]
  },
  "Data" : {
    "SomeSectionId" : [
      {
        "SomeShortAnswerId" : "blah blah blah",
        "SomeMultipleChoiceId" : [
          "Answer 1",
          "Answer 2",
          "Answer 3"
        ],
        "SomeYesNoId" : true
      }
    ]
  }
}

您可以看到Data节的字段名称对应于Layout.Tabs.Sections MappingIdQuestions.MappingId。我当前的查询仅用于返回数据部分:

db.myCollection.aggregate( 
  [
    { $project: { CreatedDate: 1, Data: 1 } },
    { $unwind: "$Data.SomeSectionId" },
    {
        $addFields: {
            "Data.SomeSectionId.CreatedDate": "$CreatedDate",
            "Data.SomeSectionId._id": "$_id"
        }
    },
    { $replaceRoot: { newRoot: "$Data.SomeSectionId" } }
  ]
)

以及返回的数据:

{
  "SomeShortAnswerId" : "blah blah blah",
  "SomeMultipleChoiceId" : [
    "Answer 1, Answer 2, Answer 3"
  ]
  "SomeYesNoId" : true
}

不幸的是,此数据被传递到报表应用程序,该应用程序具有局限性,无法处理多项选择题答案的子数组值。

我需要像这样格式化数据:

{
  "SomeShortAnswerId" : "blah blah blah",
  "SomeMultipleChoiceId" : "Answer 1, Answer 2, Answer 3",
  "SomeYesNoId" : true
}

整个过程如此复杂的原因是,除了Data字段名称之外,其他所有字段名称都可以在Layout部分(MappingId s)中找到。

考虑到我们Note文档的自我描述性质,有人可以建议一种将答案数组转换为单个文本逗号分隔值的方法吗?这真杀了我...

更新

我可能还不清楚,以上“注释”内容没有固定的字段。我不能指望在那里的SomeShortAnswerIdSomeMultipleChoiceIdSomeYesNoId。这些字段可能存在,或者可能还有其他多个选择字段,以转换为CSV格式。这些都是基于Layout部分的内容。

是否可以遍历节(Data)中的所有字段而不必指定实际的字段名称?如果可以的话,我可以对每个字段应用$ reduce。

1 个答案:

答案 0 :(得分:0)

根据Alex Blex的建议,我能够自由使用聚合功能$objectToArray$reduce$group$arrayToObject来制定解决方案$project$replaceRoot$group$addToSet和其他一些常见的。关键实际上是使用$objectToArray$arrayToObject,因为它不需要任何有关属性名称的知识。

这是我在这里稍作修改的查询:

db.myCollection.aggregate([
    {
        $match: {
            "_id": { $eq: ObjectId("5aafefbecbf20b364c14d037") }
        }
    },
    {
        $project: {
            CreatedDate: 1,
            data: { $objectToArray: "$$ROOT.Data" }
        }
    },
    {
        $addFields: {
            "data.v.CreatedDate": "$CreatedDate",
            "data.v._id": "$_id"
        }
    },
    { $unwind: "$data" },
    { $unwind: "$data.v" },
    { $replaceRoot: { newRoot: "$data.v" } },
    { $project: { data: { $objectToArray: "$$ROOT" } } },
    { $unwind: "$data" },
    {
        $project: {
            "Question": "$data.k",
            "Answer": {
                $switch: {
                    branches: [
                        {
                            case: { $eq: [{ $type: "$data.v" }, "array"] },
                            then: {
                                $reduce: {
                                    input: "$data.v",
                                    initialValue: "",
                                    in: {
                                        $concat: [
                                            "$$value",
                                            { $cond: { if: { $eq: ["$$value", ""] }, then: "", else: ", " } },
                                            { $concat: ["'", "$$this", "'"] }
                                        ]
                                    }
                                }
                            }
                        }
                    ],
                    default: "$data.v"
                }
            },
        }
    },
    {
        $group: {
            _id: "$_id",
            section: { $addToSet: { "k": "$Question", "v": "$Answer" } }
        }
    },
    { $project: { section: { $arrayToObject: "$section" } } },
    { $replaceRoot: { newRoot: "$section" } }
])