检查MongoDB数组中的所有元素是否与给定查询匹配

检查MongoDB数组中的所有元素是否与给定查询匹配,mongodb,mongodb-shell,Mongodb,Mongodb Shell,以下是我的文档的外观: nookstore = { "name": "Nook store", "categories": ["bookstore"], "working_hours": [ {"opens": 8*60*60, "closes": 21*60*60 }, {"opens": 8*60*60, "closes": 21*60*60 }, {"opens": 8*60*60, "closes": 21*6

以下是我的文档的外观:

nookstore = {
     "name": "Nook store",
     "categories": ["bookstore"],
     "working_hours": [
        {"opens": 8*60*60, "closes": 21*60*60 },
        {"opens": 8*60*60, "closes": 21*60*60 },
        {"opens": 8*60*60, "closes": 21*60*60 },
        {"opens": 8*60*60, "closes": 21*60*60 },
        {"opens": 8*60*60, "closes": 21*60*60 },
        {"opens": 8*60*60, "closes": 21*60*60 },
        {"opens": 9*60*60, "closes": 20*60*60 },
     ]
  }

  ...

kindlestore = {
     "name": "Kindle store",
     "categories": ["bookstore"],
     "working_hours": [
        {"opens": 0, "closes": 24*60*60 },
        {"opens": 0, "closes": 24*60*60 },
        {"opens": 0, "closes": 24*60*60 },
        {"opens": 0, "closes": 24*60*60 },
        {"opens": 0, "closes": 24*60*60 },
        {"opens": 0, "closes": 24*60*60 },
        {"opens": 0, "closes": 24*60*60 },
     ],
  }
我正在寻找24小时商店,即
工作时间
每个元素
0打开,在
24*60*60关闭的商店(午夜后几秒)


我尝试过使用
$all
$elemMatch
,但是如果数组中至少有一个元素符合给定的条件(我不需要),它们就可以工作。

而我认为如果您修改了模式,将特殊状态作为另一个字段包含(
open24hours
),这将是最好的选择,您可以使用聚合框架来查找匹配项

db.stores.aggregate({$unwind : '$working_hours' }, 
   { $group : { 
       _id : { 
         name: '$name', 
         opens: '$working_hours.opens', 
         closes: '$working_hours.closes' }, 
      total: { $sum : 1 }
      }
  }, 
  { $match : { total : 7 } })
这将展开打开的小时数,然后在名称上分组,打开和关闭时间,从而生成唯一的组合,然后合计每个组合的匹配数。如果总共有7天,那么这7天都是匹配的。您可以进行进一步的匹配,只在
0
处查找
打开的
,在
86400处查找
关闭的

结果:

{
    "result" : [
            {
                    "_id" : {
                            "name" : "Kindle store",
                            "opens" : 0,
                            "closes" : 86400
                    },
                    "total" : 7
            }
    ],
    "ok" : 1
}

您可以使用聚合框架来实现这一点,可能有多种方式。这是一个只需分组就可以找到唯一的打开/关闭时间,并在一个单独的打开/关闭时间(从午夜到午夜)的末尾找到匹配项

db.stores.aggregate(
[ 
{$REWIND:'$working_hours'},
{$group:{{u id:'$name',times:{$addToSet:'$working_hours'}},
{$unwind:'$times'},
{$group:{{u id:'$\u id',times:{$push:'$times'},cnt:{$sum:1}},
{$match:{'times.opens':0,'times.closes':86400,cnt:1}
]
)
编辑:另一个可能更好的选择是尽早匹配,不必对不相关的值进行分组

db.stores.aggregate(
  [
    { $unwind: '$working_hours' },
    { $match: { 'working_hours.opens': 0, 'working_hours.closes': 86400 } },
    { $group: { _id: '$name', cnt: { $sum: 1 } } },
    { $match: { 'cnt': 7 } }
  ]
)
EDIT2:也可以在没有聚合框架的情况下通过查找所有没有不匹配时间的存储来完成

db.stores.find({
  'working_hours': { 
    $not: { 
      $elemMatch: {
        $or:[ { 'opens': { $ne: 0 } }, { 'closes': { $ne: 86400 } } ]
      }
    }
  }
}).pretty()

如果在保存文档时将其存储为不同的字段,则效率最高。或者,您需要使用
$和
..检查每个条件的每个数组索引。。。这样看起来会很乱,可能会有点慢。如果使用聚合框架,则可以根据打开/关闭时间进行分组和求和,并检查时间==7的情况。我添加了一个带有聚合解决方案的答案。