【发布时间】:2018-05-21 19:28:08
【问题描述】:
我正在开发一个页面的过滤系统。我有以下架构结构:
User: {
firstName
lastName
email
subscriptions: [
{
id: '5a687c7680eab407064c3990'
plan: "TRIAL"
startDate: '2018-03-01'
endDate: '2018-04-01'
status: 'EXPIRED'
}
{
id: '5afacc040e53b4075dcdd600'
plan: "BASIC"
startDate: '2018-04-01'
endDate: '2018-05-01'
status: 'EXPIRED'
}
{
id: '5afacc040e53b4075dcdd600'
plan: "BASIC"
startDate: '2018-05-01'
endDate: '2018-06-01'
status: 'CANCELLED'
}
]
}
我需要得到一个用户列表:
- 名字、姓氏或电子邮件都是“约翰”
- 当前订阅状态:已过期
- 当前订阅计划:TRIAL
这应该返回 null。
这是我尝试过的:
User.find({
$or:
[
{ firstName: { $regex: args.search, $options: 'i' } },
{ lastName: { $regex: args.search, $options: 'i' } },
{ email: { $regex: args.search, $options: 'i' } },
],
$and:
[
{ 'subscriptions.status': 'EXPIRED' },
{ 'subscriptions.plan': 'TRIAL' },
],
}).limit(limit).skip(skip);
问题是我只需要搜索用户的最新订阅(具有最新 startDate 的订阅),而不是数组中的每个对象。有没有办法做到这一点?我的schema上也有虚拟字段,但是好像不能查询虚拟字段。
【问题讨论】:
-
不幸的是,没有办法通过标准的查找查询来做到这一点。话虽如此,您可以查看
aggregate()命令和聚合管道。如果您使用$unwind之类的运算符将订阅数组转换为单个文档,然后按降序排序并使用$first运算符获取第一个,这应该会为您提供所需的内容。然后,您可以在该最终子文档上使用您的标准尝试$match。 -
@B.Fleming 这并不完全正确。我们可以在常规查询中毫无问题地搜索“第一个”数组元素。唯一需要做的是确保在对数组内容进行任何更新时,“最新”始终是第一个数组元素。因此,所有这些需求都是诱使 OP 真正做到这一点,并且他们在不使用聚合的情况下获得非常有效的查询。
-
@NeilLunn 这是真的,但我的假设是 OP 正在处理遗留数据并且缺乏进行这种更改的奢侈。此外,像这样的异步数组更新不能使用更安全的
$push运算符进行,因此元素最终可能会丢失。最后,存在数组不一定按照严格的时间顺序排列的问题。在不了解这些约束的情况下,聚合是最安全的建议:) -
@B.Fleming 那么不要做假设。 “改为询问 OP”,这实际上是最后一条评论的重点。因为 OP 也看到了这一点并有机会做出回应。这根本不是一个好的或安全的建议。
-
是的,我正在处理无法更改的旧数据,我不确定是否可以假设数组中的最后一个订阅始终是最新的。
标签: mongodb mongoose aggregation-framework