【问题标题】:How to query mongodb collection similar to "NOT IN" with list of value returned from subquery如何使用从子查询返回的值列表查询类似于“NOT IN”的mongodb集合
【发布时间】:2015-10-30 21:04:24
【问题描述】:

我是 mongoDB 的新手,使用版本 2.6.4。我一直在编写 mongo 查询并在这里寻求专家的帮助。

我在 myCollection 中有以下示例文档:

{   msgID: "1011",
    journalID: 1,   
    status: "FLAGGED",
    timeSent: ISODate("2015-10-27T03:44:19.359Z") },

{   msgID: "1012",
    journalID: 1,   
    status: "FLAGGED",
    timeSent: ISODate("2015-10-28T07:12:03.446Z") },

{   msgID: "1012",
    journalID: 2,   
    status: "INITIATED",
    timeReceived: ISODate("2015-10-28T08:06:21.221Z") },

{   msgID: "1013",
    journalID: 1,   
    status: "FLAGGED",
    timeSent: ISODate("2015-10-28T13:21:13.568Z") },

{   msgID: "1013",
    journalID: 2,   
    status: "INITIATED",
    timeReceived: ISODate("2015-10-28T13:56:06.419Z") },

{   msgID: "1013",
    journalID: 3,   
    status: "CLOSED",
    timeReceived: ISODate("2015-10-28T16:11:38.875Z") },

{   msgID: "1014",
    journalID: 1,   
    status: "FLAGGED",
    timeSent: ISODate("2015-10-29T13:21:13.568Z") },

{   msgID: "1015",
    journalID: 1,   
    status: "FLAGGED",
    timeSent: ISODate("2015-10-28T08:26:57.828Z") },

{   msgID: "1016",
    journalID: 1,   
    status: "FLAGGED",
    timeSent: ISODate("2015-10-28T11:03:09.075Z") },

{   msgID: "1016",
    journalID: 2,   
    status: "CLOSED",
    timeReceived: ISODate("2015-10-28T14:19:19.907Z") }

如何编写 mongo 查询,该查询将从 myCollection 中获取满足以下条件的文档:

  • 返回所有只有 FLAGGED 状态记录的 msgID(即 排除那些带有 INITIATED 或 CLOSED 条目的 msgID 州)。
  • 发送时间

预期结果是:

msgID     status        timeSent
-----     ---------     -----------------------------------
1011      FLAGGED       ISODate("2015-10-27T03:44:19.359Z")
1015      FLAGGED       ISODate("2015-10-28T08:26:57.828Z")

我知道在 SQL 中这可以通过以下查询来实现:

SELECT  m1.msgID, m1.status, m1.timeSent
FROM    myCollection m1
WHERE   m1.msgID NOT IN
            ( SELECT m2.msgID FROM myCollection m2 WHERE m2.status IN ('INITIATED', 'CLOSED'))
AND     m1.status = "FLAGGED"
AND     m1.timeSent < "10-29-2015 00:00:00"

任何帮助将不胜感激。

【问题讨论】:

  • 更重要的是,如果您查看并理解此处给出的 SQL 查询,甚至不需要子查询,真正的查询只需要查找状态“FLAGGED”和范围。所以这里的教训是不要试图重现写得不好的 SQL 的动作,而要解决数据选择的问题。
  • 一个 msgID 可以有多个记录,状态为“FLAGGED”、“INITIATED”、“CLOSED”......等等。我不希望那些有任何记录为“INITIATED”或“CLOSED”的msgID,因此这个子查询过滤掉那些msgID。
  • 子查询基本上是一个“连接”,MongoDB 不会以任何方式执行连接。这不是一个好的设计,因为当前 SQL 只是查找并排除与排除值匹配的 所有 文档/记录。逻辑前提是“FLAGGED”是第一位的,所以任何超过一个状态的东西都应该被排除在外。使用“状态”更改的“数组”而不是集合中的单独对象来对此进行建模可能会更好。查询就变得很简单了。
  • @Blakes Seven 同意你的观点,我需要更改模型以在数组级别为每个 msgID 捕获日志详细信息。谢谢。

标签: mongodb


【解决方案1】:

就像提到的@BlakesSeven 一样,最好更改架构。而不是为每个状态添加一个文档,您应该按照msgID 对您的项目进行分组并将这些项目添加到一个数组中。前三个文档将如下所示:

{
    msgID: "1011",
    items: [{
        journalID: 1,   
        status: "FLAGGED",
        timeSent: ISODate("2015-10-27T03:44:19.359Z")
    }]
},
{
    msgID: "1012",
    items: [{
        journalID: 1,   
        status: "FLAGGED",
        timeSent: ISODate("2015-10-28T07:12:03.446Z")
    }, {
        journalID: 2,   
        status: "INITIATED",
        timeReceived: ISODate("2015-10-28T08:06:21.221Z")
    }]
}

这使您能够以更好的性能进行更好的查询。您的查询将如下所示:

db.getCollection('someCollection').find({
    items: {
        $size : 1
    },
    'items.status': 'FLAGGED'
});

【讨论】:

  • 我喜欢。 NoSQL 设计变得简单。
  • @BlakesSeven 感谢您的反馈!
  • 为了记录,我以前的 cmets 都被删除了,因为它没有添加到答案的当前状态(这真的很好),这是人们真正应该遵循的例子。
  • @ThomasBormans 完美!!非常感谢您的指导。我将按照这个简单的 NoSQL 设计进行架构更改。
【解决方案2】:

您可以使用 find()

轻松完成此操作
db.collection.find(query, projection)

查询 - 指定选择标准(可选)more(SQL 中的 WHERE 子句)
投影 - 匹配文档中的所有字段(可选)

根据你的问题可以如下解决

  db.flagged.find({
        status: 'FLAGGED',
        timeSent: {
            $lt: ISODate('2015-10-29T00:00:00')
        },
        {msgID:1,status:1,timeSent:1}
    });

更多详情请关注Mongdb Tutorial

【讨论】:

    【解决方案3】:

    虽然它没有从性能角度进行优化,并且我了解存储和检索此类数据所需的架构更改,但只是为了与所有人分享,我可以根据给定的数据结构找出查询:

    db.myCollection.aggregate(
        { $group: {
          _id: "$msgID", stateChange: { $push: { status: "$status", timeSent: "$timeSent" }}}
        },
        { $match: {
            "stateChange.status": { $eq: 'FLAGGED', $nin: ['INITIATED','CLOSED'] }, 
            "stateChange.timeSent": { $lt: ISODate("2015-10-29T00:00:00")}}
        },
        { $unwind: "$stateChange" },
        { $project: {
            _id: 0, msgID: "$_id", status: "$stateChange.status", timeSent: "$stateChange.timeSent"}
        }
    )
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-22
      • 1970-01-01
      相关资源
      最近更新 更多