MongoDB 使用 PHP 按时间段(开始日期、结束日期)过滤数组字段
MongoDB filter an array field by period (start date, end date) using PHP
我有 MongoDB 个具有这种结构的文档:
{
_id: 1
dates: [
ISODate ("2015-08-21T22: 00: 00Z")
ISODate ("2015-09-27T22: 00: 00Z")
],
...
}
在我的示例中,我们看到访问了文档 1:
- 2015-08-21
- 2015-09-27
在我的应用程序中,我可以按时间段(开始日期、结束日期)进行过滤。
如果在我的应用程序中我过滤了时间段“2015-09-01 - 2015-09-01”:
按理应该找不到文档1,因为这段时间没有查阅。但是通过我的测试,找到了文档 1。
我必须使用什么条件才能正确过滤数据?
我试过了,但它很容易出错:
<?php
$begin = new \DateTime('2015-09-01');
$end = new \DateTime('2015-09-01');
$expr1 = $qb->expr()->field('dates')->gte($begin);
$expr2 = $qb->expr()->field('dates')->lte($end);
$qb
->addAnd($expr1)
->addAnd($expr2)
;
感谢您的帮助,
约翰
有聚合
这是 mongoshell 的代码片段,您可以试试
> db.collection.aggregate([{$unwind:"$dates"},{$match:{"dates":{$gt:new ISODate("2015-01-01"),$lt:new ISOD
ate("2015-09-01")}}}])
使用查找查询
db.collection.find({dates:{$elemMatch:{$gt:new ISODate("2015-01-10"),$lt:new ISODate("2015-09-01")}}},{"dates.$":1})
谢谢大家,我根据你们的回答解决了问题,完全测试了。
用 odm 学说很难做到这一点,这里是解决方案:
<?php
$begin = new \DateTime($options['begin']);
$end = new \DateTime($options['end']);
$expr = $qb->expr()
->gte($begin)
->lte($end)
;
$exprElementMatch = $qb->expr()->field('dates')->elemMatch($expr);
$qb->addAnd($exprElementMatch);
当前标记为已解决的答案实际上对我不起作用。
给定以下数据:
{
_id: 1
dates: [
ISODate("2015-08-21T22:01:12Z")
ISODate("2015-08-22T00:06:45Z")
],
...
}
以下查询与上面的文档匹配:
db.collection.find({dates:{$elemMatch:{$gt:new ISODate("2017-08-21"),$lt:new ISODate("2017-08-22")}}}
我很困惑并阅读了 $elemMatch documentation 并指出:
The $elemMatch operator limits the contents of an field from the query results to contain only the first element matching the $elemMatch condition.
因此,从这个已解决的问题来看,这根本无法按预期工作。对于我的用例,我能够仅评估 dates
数组中的第一个元素,以使用以下查询过滤我的文档。
db.collection.find({"dates.0":{$gte:new ISODate("2017-07-02"),$lt:new ISODate("2017-07-03")})
This answer 详细介绍了如何实际查询 dates
数组。
我有 MongoDB 个具有这种结构的文档:
{
_id: 1
dates: [
ISODate ("2015-08-21T22: 00: 00Z")
ISODate ("2015-09-27T22: 00: 00Z")
],
...
}
在我的示例中,我们看到访问了文档 1:
- 2015-08-21
- 2015-09-27
在我的应用程序中,我可以按时间段(开始日期、结束日期)进行过滤。
如果在我的应用程序中我过滤了时间段“2015-09-01 - 2015-09-01”:
按理应该找不到文档1,因为这段时间没有查阅。但是通过我的测试,找到了文档 1。
我必须使用什么条件才能正确过滤数据?
我试过了,但它很容易出错:
<?php
$begin = new \DateTime('2015-09-01');
$end = new \DateTime('2015-09-01');
$expr1 = $qb->expr()->field('dates')->gte($begin);
$expr2 = $qb->expr()->field('dates')->lte($end);
$qb
->addAnd($expr1)
->addAnd($expr2)
;
感谢您的帮助, 约翰
有聚合
这是 mongoshell 的代码片段,您可以试试
> db.collection.aggregate([{$unwind:"$dates"},{$match:{"dates":{$gt:new ISODate("2015-01-01"),$lt:new ISOD
ate("2015-09-01")}}}])
使用查找查询
db.collection.find({dates:{$elemMatch:{$gt:new ISODate("2015-01-10"),$lt:new ISODate("2015-09-01")}}},{"dates.$":1})
谢谢大家,我根据你们的回答解决了问题,完全测试了。
用 odm 学说很难做到这一点,这里是解决方案:
<?php
$begin = new \DateTime($options['begin']);
$end = new \DateTime($options['end']);
$expr = $qb->expr()
->gte($begin)
->lte($end)
;
$exprElementMatch = $qb->expr()->field('dates')->elemMatch($expr);
$qb->addAnd($exprElementMatch);
当前标记为已解决的答案实际上对我不起作用。
给定以下数据:
{
_id: 1
dates: [
ISODate("2015-08-21T22:01:12Z")
ISODate("2015-08-22T00:06:45Z")
],
...
}
以下查询与上面的文档匹配:
db.collection.find({dates:{$elemMatch:{$gt:new ISODate("2017-08-21"),$lt:new ISODate("2017-08-22")}}}
我很困惑并阅读了 $elemMatch documentation 并指出:
The $elemMatch operator limits the contents of an field from the query results to contain only the first element matching the $elemMatch condition.
因此,从这个已解决的问题来看,这根本无法按预期工作。对于我的用例,我能够仅评估 dates
数组中的第一个元素,以使用以下查询过滤我的文档。
db.collection.find({"dates.0":{$gte:new ISODate("2017-07-02"),$lt:new ISODate("2017-07-03")})
This answer 详细介绍了如何实际查询 dates
数组。