【问题标题】:MongoDB find subdocument and sort the resultsMongoDB查找子文档并对结果进行排序
【发布时间】:2014-05-10 04:19:39
【问题描述】:

我在 MongoDB 中有一个具有复杂结构和子文档的集合。 该文档的结构如下:

doc1 = {
    '_id': '12345678',
    'url': "http//myurl/...",
    'nlp':{
        "status": "OK",
        "entities": {
            "0": {
                "type" : "Person",
                "relevance": "0.877245",
                "text" : "Neelie Kroes"
            },
            "1": {
                "type": "Company",
                "relevance": "0.36242",
                "text": "ICANN"
            },
            "2": {
                "type": "Company",
                "relevance": "0.265175",
                "text": "IANA" 
            }
        }
    }
}


doc2 = {
    '_id': '987456321',
    'url': "http//myurl2/...",
    'nlp':{
        "status": "OK",
        "entities": {
            "0": {
                "type": "Company",
                "relevance": "0.96",
                "text": "ICANN"
            },
            "1": {
                "type" : "Person",
                "relevance": "0.36242",
                "text" : "Neelie Kroes"
            },
            "2": {
                "type": "Company",
                "relevance": "0.265175",
                "text": "IANA" 
            }
        }
    }
}

我的任务是在子文档中搜索“类型”和“文本”,然后按“相关性”排序。 使用 $elemMatch 运算符,我可以执行查询:

db.resource.find({
    'nlp.entities': {
        '$elemMatch': {'text': 'Neelie Kroes', 'type': 'Person'}
    }
});

完美,现在我必须对所有类型为“Person”且值为“Neelie Kroes”的实体按相关性降序排序。

我尝试了正常的“排序”,但是,正如manual said 关于 $elemMatch 中的 sort(),结果可能无法反映排序顺序,因为 sort() 之前已应用于数组的元素$elemMatch 投影。

事实上,_id:987456321 将是第一个(相关性为 0.96,但参考 ICANN)。

如何按匹配的子文档的相关性对文档进行排序?

P.S.:我无法更改文档结构。

【问题讨论】:

  • 这是作为某个工具的转储出来的,还是您的文档在 mongo shell 中的实际外观?我这样说是因为您将“实体”表示为“子文档”而不是数组。这些无法通过任何标准方式进行排序。

标签: mongodb sorting mongodb-query aggregation-framework


【解决方案1】:

如前所述,我希望您的文档确实有一个数组,但如果 $elemMatch 对您有用,那么他们应该这样做。

无论如何,您都不能使用 find 对数组中的元素进行排序。但在某些情况下,您可以使用.aggregate():

db.collection.aggregate([

    // Match the documents that you want, containing the array
    { "$match": {
        "nlp.entities": {
            "$elemMatch": { 
                "text": "Neelie Kroes", 
                "type": "Person"   
            }
        }
    }},

    // Project to "store" the whole document for later, duplicating the array
    { "$project": {
        "_id": {
            "_id": "$_id",
            "url": "$url",
            "nlp": "$nlp"          
        },
        "entities": "$nlp.entities"
    }},

    // Unwind the array to de-normalize
    { "$unwind": "$entities" },

    // Match "only" the relevant entities
    { "$match": {
        "entities.text": "Neelie Kroes", 
        "entities.type": "Person"   
    }},

    // Sort on the relevance
    { "$sort": { "entities.relevance": -1 } },

    // Restore the original document form
    { "$project": {
        "_id": "$_id._id",
        "url": "$_id.url",
        "nlp": "$_id.nlp"
    }}
])

所以本质上,在对包含相关匹配的文档执行$match 条件后,您可以使用$project“存储”原始文档在_id 字段和$unwind“副本”中实体”数组。

下一个$match 将数组内容“过滤”为仅那些相关的内容。然后将$sort 应用于“匹配”的文档。

由于“原始”文档存储在_id 下,您可以使用$project 来“恢复”文档实际必须以开头的结构。

这就是您对数组的匹配元素“排序”的方式。

请注意,如果您在一个父文档的数组中有多个“匹配项”,那么您必须使用额外的 $group 阶段来获取“相关性”的 $max 值字段以完成您的排序。

【讨论】:

  • 谢谢它完美的工作。第一次速度很慢,但之后非常快。聚合函数是否存储在 RAM、缓存或其他加速系统中?再次感谢您。
猜你喜欢
  • 2013-09-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-05-15
  • 2023-03-16
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多