mongodb:具有最大匹配目标数的文档

时间:2016-02-04 21:52:36

标签: mongodb mongoose mongodb-query aggregation-framework

我需要帮助才能解决以下问题。我的收藏品有一个“目标”字段。

每个用户可以拥有0个或更多目标。

当我运行查询时,我想检索具有最大匹配目标数的文档。

例如:

documents=[{
    targets:{
        "cluster":"01",
    }
},{
    targets:{
        "cluster":"01",
        "env":"DC",
        "core":"PO"
    }
},{
    targets:{
        "cluster":"01",
        "env":"DC",
        "core":"PO",
        "platform":"IG"
    }
}];

userTarget={
    "cluster":"01",
    "env":"DC",
    "core":"PO"
}

1 个答案:

答案 0 :(得分:0)

您似乎要求返回满足最多条件的文档,并且可能不是所有条件。基本过程是$or查询,用于返回可以匹配任一条件的文档。那么你基本上需要一个语句来计算"文档中满足多少个术语" ,并返回最匹配的术语。

所以这里的组合是一个.aggregate()语句,使用$or的初始结果来计算然后对结果进行排序:

// initial targets object
var userTarget = {
    "cluster":"01",
    "env":"DC",
    "core":"PO"
};

// Convert to $or condition
// and the calcuation condition to match
var orCondition = [],
    scoreCondition = []

Object.keys(userTarget).forEach(function(key) {
    var query = {},
        cond = { "$cond": [{ "$eq": ["$target." + key, userTarget[key]] },1,0] };

    query["target." + key] = userTarget[key];
    orCondition.push(query);
    scoreCondition.push(cond);
});

// Run aggregation
Model.aggregate(
    [
        // Match with condition
        { "$match": { "$or": orCondition } },

        // Calculate a "score" based on matched fields
        { "$project": {
            "target": 1,
            "score": {
                "$add": scoreCondition
            }
        }},

        // Sort on the greatest "score" (descending)
        { "$sort": { "score": -1 } },

        // Return the first document
        { "$limit": 1 }
    ],
    function(err,result) {
       // check errors

       // Remember that result is an array, even if limitted to one document
       console.log(result[0]);
    }
)

因此,在处理聚合语句之前,我们将根据userTarget对象中的输入生成管道操作的动态部分。这会产生orCondition,如下所示:

{ "$match": {
    "$or": [
        { "target.cluster" : "01" },
        { "target.env" : "DC" },
        { "target.core" : "PO" }
    ]
}}

scoreCondition会扩展为这样的编码:

            "score": {
                "$add": [
                    { "$cond": [{ "$eq": [ "$target.cluster", "01" ] },1,0] },
                    { "$cond": [{ "$eq": [ "$target.env", "DC" ] },1,0] },
                    { "$cond": [{ "$eq": [ "$target.core", "PO" ] },1,0] },
                ]
            }

这些将用于选择可能的文件,然后用于计算可能匹配的术语。特别是"得分"通过评估$cond三元运算符中的每个条件,然后将得分1归因于匹配,或0得出该字段上没有匹配的结果。

如果需要,可以很容易地改变逻辑以分配更高的重量"根据所认为的比赛重要性,向具有不同值的每个场进行得分。无论如何,您只需$add将这些得分结果一起用于整个"得分"的每个字段。

然后,只需将$sort应用于返回的"得分",然后使用$limit返回顶部文档。

它不是超级高效的,因为即使所有三个条件都匹配,您要求数据的基本问题也不能假设存在,因此需要查看的所有数据"至少有一个" 条件匹配,然后从那些可能的结果中找出"最佳匹配"

理想情况下,我会亲自运行一个额外的查询"首先"查看是否满足所有三个条件,如果没有,则查找其他情况。这仍然是两个单独的查询,并且不同于简单地只是推动"和"所有字段的条件作为$or中的第一个语句。

所以我认为首选的实现应该是:

  1. 查找与所有给定字段值匹配的文档;如果不是那么

  2. 运行每个/或每个字段并计算条件匹配。

  3. 这样,如果所有字段匹配,那么第一个查询最快,只需要回退到列表中显示的较慢但必需的实现,如果没有实际结果。