【问题标题】:Querying for embedded object inside array查询数组内的嵌入对象
【发布时间】: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


【解决方案1】:

您应该尝试聚合查询来执行此操作...

首先你必须

  1. $match名字、姓氏、电子邮件
  2. 然后$unwind按照startDate对订阅进行排序
  3. 然后再$match获取计划和状态

    db.collection.aggregate([
      {
        $match: {
          $or: [
            {
              firstName: {
                $regex: args.search,
                $options: "i"
              }
            },
            {
              lastName: {
                $regex: args.search,
                $options: "i"
              }
            },
            {
              email: {
                $regex: args.search,
                $options: "i"
              }
            }
          ]
        }
      },
      {
        $unwind: "$subscriptions"
      },
      {
        $sort: {
          "subscriptions.startDate": -1
        }
      },
      {
        $group: {
          _id: "$_id",
          subscriptions: {
            "$first": "$subscriptions"
          }
        }
      },
      {
        $match: {
          "subscriptions.status": "EXPIRED",
          "subscriptions.plan": "TRIAL"
        }
      },
    
    ])
    

【讨论】:

  • 谢谢,它成功了。我尝试返回具有所有原始属性的用户数组,但找不到比这更好的方法:snippi.com/s/9dcjstv
  • $group...你需要在那里添加更多字段
猜你喜欢
  • 1970-01-01
  • 2020-03-19
  • 1970-01-01
  • 1970-01-01
  • 2021-02-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多