【问题标题】:MongoDB Collection with Non-repeated field value具有非重复字段值的 MongoDB 集合
【发布时间】:2014-09-15 06:34:35
【问题描述】:

所以我对 MongoDB 比较陌生。这是一个具有以下格式的虚构数据库。

{
    "_id": "message_id", 
    "headers": {
        "from": <from_email>,
        "to": <to_email>, 
        "timestamp": <timestamp>
    },   
    "message": {
        "message": <the message contents>, 
        "signature": <signature contents>
    }
} 

假设收到的所有电子邮件都插入其中,有时电子邮件会重复发送。如何在没有任何双重发送的情况下返回作者的电子邮件集合。

我认为这可能会做到,但它似乎没有按预期工作:

db.mycoll.find({"headers.from": <authorname>}).distinct("message.message")

编辑: 请原谅,我似乎一直在打错字,上面的查询有效,但它只返回没有标题的messages.messages,我如何保持标题完好无损?

【问题讨论】:

  • 运行时有什么问题? distinct() 是集合对象具有的函数 - 看起来 find() 返回一个列表,而不是 mongodb 集合。我会尝试运行它在几个..

标签: python mongodb mongodb-query aggregation-framework pymongo


【解决方案1】:

很难从您的问题中真正确定哪个部分是“重复的”或因此应该是唯一的。尽管诸如消息“_id”和“timestamp”之类的内容不会重复,但这是有道理的,因此这只会真正留下消息内容,并且该消息可能是“来自”同一个人的额外偏执。

文档重塑通常最好由aggregation framework 处理:

db.collection.aggregate([
    { "$group": {
        "_id": { "message": "$message.message", "from": "$headers.from" },
        "message_id": { "$first": "$_id" },
        "headers": { "$first": "$headers" },
        "message": { "$first": "$message" }
    }},
    { "$project": {
        "_id": "$message_id",
        "headers": 1,
        "message": 1
    }}
])

$group 将过滤掉任何匹配的消息内容,$first 操作仅选择文档分组边界上匹配字段的“第一个”找到的项目。

这里假设现有订单是“时间戳”,但如果不是,那么您可能希望将$sort 作为第一个管道阶段应用在其他阶段之前:

{ "$sort": { "headers.timestamp": 1 } }

最后的$project实际上只是恢复了原始文档形式并删除了之前提供的“分组键”。比复制信息和/或把事情搞砸更漂亮。

【讨论】:

  • 我想知道_id + .aggregate 是否是公认的保证独特性的方式? (根据你的回答,我的回答中更详细的表述)
  • _id 是一个唯一键,因此暗示了整个文档并且是不同的。我还可以说如何根据其他人提供的内容获得“答案”?我会留下一般的,因为你需要从某个地方获得知识。第二点是如果其他答案(并且有很多)不符合您的要求,那么您的问题还不够清楚,人们无法理解。请学会在应得的地方给予信用。谁真正回答了您提出的问题?
  • 您的回答并没有完全给出我想要的东西,但我将其标记为正确,因为它提供了普遍适用的技术。关于_id - 有没有办法制作一个不是_id 的“自定义唯一键”,或者必须使用_id 来保证唯一性?附言我绝不是想把你的回答归功于我,我只是想对上述问题有一些了解,而评论空间似乎不够。
【解决方案2】:

您可以使用distinct() 返回来自特定作者的不同消息数组,如下所示:

db.collection.distinct('message.message', {"headers.from": <authorname>})

【讨论】:

  • 这似乎并不完全有效 - “distinct() 接受 2 个位置参数,但给出了 3 个”。
【解决方案3】:

您正在寻找的内容目前尚未实施(至少据我所知)。一种解决方法是this

【讨论】:

    【解决方案4】:
    db.mycoll.aggregate([
    {
      $match:{"headers.from": <authorname>}
    },{
       $group:{
       _id:"$headers.from",
       "message":{$addToSet:"$message.message"}
    }
    }
    
    ])
    

    【讨论】:

    • 重复的邮件不会从这个集合中排除。
    【解决方案5】:

    以 Neil Lunn 的上述回答为基础: 我觉得可以的

    db.collection.aggregate([{"$match": {"headers.from": <from email>} } ,
    {"$group": { "_id": "$message.message"}, 
    "headers": {"$first": "$headers"}, 
    "signature": {"$first": "$message.signature"},
     "message_id": "$_id" }}, 
    {"$project" : { "_id": "$message_id", 
    "headers": "$headers", 
    "message": { "message": "$_id", "signature": "$signature" } } }]) 
    

    由于_id 必须是唯一的,结果是重复的消息不会出现在列表中,然后$project 会将其重组为具有正确键名的原始对象结构。

    我想我在这方面只有一个问题 - 有没有办法在不聚合到 _id 的情况下强制唯一性,或者这通常被认为是在 MongoDB 中做到这一点的正确方法?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-02-03
      • 1970-01-01
      • 1970-01-01
      • 2016-07-29
      • 2014-12-12
      • 2021-05-21
      • 2016-10-16
      相关资源
      最近更新 更多