如何获取水果数组中只有 2 个或更多香蕉的 json 对象?

How to get the json object with only 2 or more bananas in the fruits array?

我有这个猫鼬模式。

const storeSchema = mongoose.Schema({
    name: {
        type: String,
        required: true,
    },
    fruits: {
        type: [String],
        required: true,
    },
});

而且我需要获取水果数组中所有包含 2 个或更多香蕉的对象,就像这样。

{
    "name": "Beautiful Store",
    "fruits": ["banana", "apple", "banana", "pear"]
}

我正在尝试类似的方法,但没有成功...

const query = await StoreModel.find({ fruits: { $gd: 2 } })

像这样:

选项 1:(效率较低但直观)

  db.collection.aggregate([
  {
   $addFields: {
    numBananas: {
    $size: {
      $filter: {
        input: "$fruits",
        as: "item",
        cond: {
          $eq: [
            "$$item",
            "banana"
          ]
         }
       }
      }
     }
    }
   },
  {
   $match: {
     numBananas: {
        $gte: 2
      }
    }
   },
  {
    $project: {
       name: 1,
       fruits: 1
   }
  }
 ])

解释:

  1. 添加带有香蕉数量的字段“numBananas”
  2. 仅过滤具有 >=2 个 banas 的文档
  3. 只投射必要的产出

playground

选项 2:(最佳,灵感来自下方的 Buzz :))

 db.collection.aggregate([
 {
  $match: {
   $expr: {
    $gte: [
      {
        $size: {
          $filter: {
            input: "$fruits",
            as: "item",
            cond: {
              $eq: [
                 "$$item",
                 "banana"
                ]
              }
             }
            }
         },
         2
        ]
     }
    }
  },
 {$sort:{name:1}}
])

解释: 只匹配那些有 >=2 bananas 的,最好是在 fruits 上创建索引 (我认为这个解决方案与下面 Buzz 的 $reduce 选项有 50/50 的性能竞争,我会打赌这个)

playground

这里有两个主题变体。

第一个使用$reduce直接计算香蕉的数量(本质上就是上面的{$size:{$filter...}}。为了额外的性能,我们将calc和$match一起放在一个单级:

db.foo.aggregate([
    {$match: {$expr: {$gte: [
        {$reduce: {
            input: '$fruits',
            initialValue: 0,
                in: {$cond:[{$eq: ['$$this','banana']}, {$add:['$$value',1]}, '$$value']}
        }}, 2]}
    }}
]);

第二个是通用水果计数器,以防您想要更复杂的表达式,例如>=2 个香蕉或(1 个梨和 1 个苹果)。它确实需要 $unwind/$group.

的成本
c=db.foo.aggregate([
    {$unwind: '$fruits'}
    ,{$group: {_id: {id: '$_id', f: '$fruits'}, N: {$sum:1}}}

    ,{$match: {'_id.f': 'banana', N:{$gte:2}} }
]);