MongoDB 获取数组值匹配的所有文档

时间:2021-03-25 14:35:50

标签: mongodb

我的文档结构如下:

  {
    "input": {
      "fields": [
        {
          "name": "last_name_hebrew",
          "text": "test1",
        },
      ],
    },
    "output": {
      "fields": [
        {
          "name": "last_name_hebrew",
          "text": "test1"
        },
      ],
    },
  },

我想获取所有文档,其中字段具有名称为 last_name_hebrew 的对象,与 textoutput.fields 值一样。

例如,在给定的结构中,它会返回此文档,因为 input.fields.namelast_name_hebrew,而 text 等于 text 中的 output

请注意,我不能保证 fieldsinput 中的 output 数组将在数组中包含 name: last_name_hebrew

我该怎么做?

这是我首先尝试强制数组具有 name of last_name_hebrew 的文档:

db.collection.find({
  "input.fields": {
    $elemMatch: {
      "name": "last_name_hebrew"
    }
  },
  "output.fields": {
    $elemMatch: {
      "name": "last_name_hebrew"
    }
  },
  
})

但现在我需要比较 text 值。

2 个答案:

答案 0 :(得分:1)

您必须使用聚合管道来实现这一点,有多种方法可以做到,这里是一个示例:

db.collection.aggregate([
  {
    $match: {
      $expr: {
        $gt: [
          {
            $size: {
              $filter: {
                input: "$input.fields",
                as: "inputField",
                cond: {
                  $and: [
                    {
                      $eq: [
                        "$$inputField.name",
                        "last_name_hebrew"
                      ]
                    },
                    {
                      "$setIsSubset": [
                        [
                          "$$inputField.text"
                        ],
                        "$output.fields.text"
                      ]
                    }
                  ]
                }
              }
            }
          },
          0
        ]
      }
    }
  }
])

Mongo Playground 需要注意的一件事是,对于此查询,对 output.fields.name 没有限制(因为它不是必需的),如果您确实需要名称匹配,那么您可以将 .text 字段放在 $setIsSubset 中{1}} 运算符。

答案 1 :(得分:1)

  • 您使用 $elemMatch 的前 2 个条件是正确的
  • 添加表达式匹配,首先使用last_name_hebrewinput中找到具有$filter名称的匹配元素,然后使用$arrayElemAt从过滤结果中获取第一个元素,过程相同output 字段,然后使用 $eq
  • 匹配两个对象
db.collection.find({
  "input.fields": { $elemMatch: { "name": "last_name_hebrew" } },
  "output.fields": { $elemMatch: { "name": "last_name_hebrew" } },
  $expr: {
    $eq: [
      {
        $arrayElemAt: [
          {
            $filter: {
              input: "$input.fields",
              cond: { $eq: ["$$this.name", "last_name_hebrew"] }
            }
          },
          0
        ]
      },
      {
        $arrayElemAt: [
          {
            $filter: {
              input: "$output.fields",
              cond: { $eq: ["$$this.name", "last_name_hebrew"] }
            }
          },
          0
        ]
      }
    ]
  }
});

Playground


第二个选项:如果您想更具体地匹配精确的 2 个字段 nametext 都只需要添加 $let 运算符以从过滤器返回字段,

db.collection.find({
  "input.fields": { $elemMatch: { "name": "last_name_hebrew" } },
  "output.fields": { $elemMatch: { "name": "last_name_hebrew" } },
  $expr: {
    $eq: [
      {
        $let: {
          vars: {
            input: {
              $arrayElemAt: [
                {
                  $filter: {
                    input: "$input.fields",
                    cond: { $eq: ["$$this.name", "last_name_hebrew"] }
                  }
                },
                0
              ]
            }
          },
          in: { name: "$$input.name", text: "$$input.text" }
        }
      },
      {
        $let: {
          vars: {
            output: {
              $arrayElemAt: [
                {
                  $filter: {
                    input: "$output.fields",
                    cond: { $eq: ["$$this.name", "last_name_hebrew"] }
                  }
                },
                0
              ]
            }
          },
          in: { name: "$$output.name", text: "$$output.text" }
        }
      }
    ]
  }
})

Playground


第三个选项:为了更具体地检查循环中的两个字段,

  • 首先使用 $filter 在输入字段中按名称过滤匹配元素
  • 通过上面的过滤器导致另一个过滤器
  • 过滤以匹配输出字段中的名称和文本字段,如果它不是 [] 空则返回过滤结果
  • $ne 检查返回结果是否为 [] 空
db.collection.find({
  "input.fields": { $elemMatch: { "name": "last_name_hebrew" } },
  "output.fields": { $elemMatch: { "name": "last_name_hebrew" } },
  $expr: {
    $ne: [
      {
        $filter: {
          input: {
            $filter: {
              input: "$input.fields",
              cond: { $eq: ["$$this.name", "last_name_hebrew"] }
            }
          },
          as: "i",
          cond: {
            $ne: [
              {
                $filter: {
                  input: "$output.fields",
                  cond: {
                    $and: [
                      { $eq: ["$$this.name", "$$i.name"] },
                      { $eq: ["$$this.text", "$$i.text"] }
                    ]
                  }
                }
              },
              []
            ]
          }
        }
      },
      []
    ]
  }
})

Playground