【问题标题】:Select Matching Array Element and Return Selected Fields选择匹配的数组元素并返回选定的字段
【发布时间】:2018-01-06 16:03:58
【问题描述】:

我想知道如何从 Mongoose 查询中为匹配的对象数组设置投影。

例如,如果我有一个看起来像这样的 Mongoose 模型:

var User = new Schema({
  name: String,
  children: [Child]
});

var Child = new Schema({
  name: String,
  height: Number,
  age: Number,
  secret: Number
});

换句话说,一个可能由此模型产生的示例 JSON 对象:

User: {
  name: 'abc',
  children: [
    {
      name: 'def',
      height: 123,
      age: 7,
      secret: 2
    },
    {
      name: 'ghi',
      height: 456,
      age: 9,
      secret: 3
    }
  ]
}

如您所见,该模型包含一个属性children,它是Child 对象的数组。

如果我只匹配 User 包含 children 中具有属性 name: 'def' 的项目:

Users.find({
  children.name: 'def'
})

然后我可以设置投影以选择属性(例如name)以及使用位置运算符($)从匹配对象中选择属性:

.select({
  name: 1,
  children.$: 1
}

现在的问题是,使用此投影,children.$ 将始终返回整个 Child 对象,包括我可能不想查询的属性,例如 secret

{
  name: 'abc',
  children: [
    {
      name: 'def',
      height: 123,
      age: 7,
      secret: 2
    }
  ]
}

理想情况下,我还希望能够从通过$ 获得的child 对象中选择某些属性,类似于从父对象User 中选择name,但我找不到方法这个。

选择单个属性的一种方法是使用 children.$.age 格式,但这只能用于选择 1 个属性,因为多次这样做会导致错误,因为您不能多次使用定位 $ 运算符.

.select({
  name: 1,

  // and now select the height and age
  // but only of the child that matches name = 'def'
  // WITHOUT returning the entire object (exclude name and secret)
  children.$.age,
  children.$.height // error
})

是否可以在 Mongoose 中为位置运算符获得的对象选择投影?

【问题讨论】:

  • .find({ 'foo._id': 'def' }).select({ 'bar': 1, 'foo.$.tmp': 1 })。您可能必须滚动链接的答案(有很多),但肯定存在。还指出“单一”属性匹配不需要$elemMatch
  • @NeilLunn 如果我需要匹配foo 中的多个字段怎么办?现在它返回一个错误,因为我不能有多个位置 $。
  • 那么也许您应该真正完整地阅读现有答案,因为也有解决方案。
  • 我已完整阅读现有答案数次,但对它们不满意。我已经编辑了我的问题,以更好地反映我的要求。为了澄清起见,我正在寻找一种从对象数组中选择特定字段的方法,类似于我从标准查询中选择字段的方式。对于该线程上给出的所有答案,它们总是返回完整的子对象。
  • 你当时没有正确地把它们变红。 Go and read them all again。有几个答案提到了$filter

标签: javascript node.js mongodb mongoose


【解决方案1】:

如果您只想选择要返回的数组的某些字段,那么您正在谈论“重塑”文档。对于“基本”字段选择之外的任何内容,这意味着使用.aggregate() 作为方法而不是.find()

所以这里的两个要求是$filter对数组内容进行“匹配”并返回,以及$map从数组本身实际“返回的字段”:

User.aggregate([
  { "$match": { "children.name": "def" } },
  { "$project": {
     "name": 1,
     "children": {
       "$map": {
         "input": {
           "$filter": {
             "input": "$children",
             "as": "c",
             "cond": { "$eq": [ "$$c.name", "def" ] } 
           }
         },
         "as": "c",
         "in": {
           "age": "$$c.age",
           "height": "$$c.height"
         }
       }
     }
  }}
])

这里使用$filter 是为了将数组的内容减少到仅匹配条件的内容。具有与"def" 相同的"name" 属性的那些。然后将其作为"input" 参数传递给 $map.

$map 运算符的工作方式与其他语言对应物一样,它“重塑数组”以根据您在 "in" 参数中指定的内容返回某些内容。所以这里我们实际上只显式地命名属性并为当前正在处理的数组元素使用变量赋值,这样这些就是作为“新”数组内容返回的内容。

整体结果是一个数组,包含:

  1. 只有符合指定条件的项目。
  2. 仅指定要返回的字段。

【讨论】:

    猜你喜欢
    • 2013-01-22
    • 1970-01-01
    • 1970-01-01
    • 2012-10-25
    • 1970-01-01
    • 2012-01-22
    • 1970-01-01
    • 2017-07-09
    • 1970-01-01
    相关资源
    最近更新 更多