如何计算传入链接?

时间:2015-05-15 08:17:05

标签: mongodb aggregation-framework

我的文件看起来像这样:

{
    "url" : "http://example.com",
    "links" : [
        "http://example.com/foo",
        "http://example.com/bar"
    ]
},
{
    "url" : "http://example.com/foo",
    "links" : [
        "http://example.com/bar"
    ]
},
{
    "url" : "http://example.com/lost",
    "links" : [
        "http://example.com/bar"
    ]
},
{
    "url" : "http://example.com/bar",
    "links" : []
}

如何按网址汇总并计算传入链接的数量:

{
    "url": http://example.com,
    "count" : 0
},
{
    "url": http://example.com/lost,
    "count" : 0
},
{
    "url": http://example.com/foo,
    "count" : 1
},
{
    "url": http://example.com/bar,
    "count" : 3
}

你知道我怎么做吗?

2 个答案:

答案 0 :(得分:2)

使用 aggregation framework 来获得所需的结果。以下聚合管道将为您提供:

db.test.aggregate([
    {
        "$unwind": "$links"
    },
    {
        "$group": {
            "_id": "$url",
            "count": { "$sum": 1 }
        }
    },
    {
        "$project": {
            "_id": 0,
            "url": "$_id",
            "count": 1
        }
    }
])

<强>输出

/* 0 */
{
    "result" : [ 
        {
            "count" : 1,
            "url" : "http://example.com/lost"
        }, 
        {
            "count" : 1,
            "url" : "http://example.com/foo"
        }, 
        {
            "count" : 2,
            "url" : "http://example.com"
        }
    ],
    "ok" : 1
}

- 更新 -

由于我没有看到您想要计算传入链接的数量,上面的聚合将无效。但是,要根据该条件获取聚合,请将分组更改为links数组元素的组,并使用$out运算符创建输出集合作为最终聚合管道。这对于查询原始集合的空传入链接数组以及相应地更新结果集合是必要的。例如:

db.test.aggregate([
    {
        "$unwind": "$links"
    },
    {
        "$group": {
            "_id": "$links",
            "count": { "$sum": 1 }
        }
    },
    {
        "$project": {
            "_id": 0,
            "url": "$_id",
            "count": 1
        }
    },
    {
        "$out": "results"
    }
])

在结果集合中,您可以使用map()forEach()光标方法的组合来更新文档,以获取具有计数并迭代原始集合的URL的数组,以查找文档url不是前面提到的数组:

var urlsWithCount = db.results.find().map(function(u){ return u.url });
db.test.find({"url": {"$nin": urlsWithCount}}).forEach(function(doc){
    var obj = {};
    obj.url = doc.url;
    obj.count = 0;
    db.results.save(obj);   
});    


db.results.find();
/* 0 */
{
    "_id" : ObjectId("5555c1c49cd8fa39c7971e54"),
    "count" : 3,
    "url" : "http://example.com/bar"
}

/* 1 */
{
    "_id" : ObjectId("5555c1c49cd8fa39c7971e55"),
    "count" : 1,
    "url" : "http://example.com/foo"
}

/* 2 */
{
    "_id" : ObjectId("5555c3829bbec0dd0344e4ac"),
    "url" : "http://example.com",
    "count" : 0
}

/* 3 */
{
    "_id" : ObjectId("5555c3829bbec0dd0344e4ad"),
    "url" : "http://example.com/lost",
    "count" : 0
}

答案 1 :(得分:1)

因为你想要计算传入链接,这个很棘手。您可以使用map-reduce

来获得所需的结果

地图阶段将为当前检查的URL所针对的每个链接发出“1”。另外,为确保每个 URL都在结果集中,我为源链接发出“0”:

map = function() {
    for (var idx = 0; idx < this.links.length; idx++) {
        emit(this.links[idx], 1)
    }
    emit(this.url, 0) // this ensure that all URL are in the output set
}

之后,你的简化步骤只是总结多个值的问题,如果有的话:

reduce = function(key, values) {
    return values.reduce(function(a, b){return a+b;});
}

给出样本数据集:

> db.test.mapReduce(map, reduce, {out:{inline:1}})
{
    "results" : [
        {
            "_id" : "http://example.com",
            "value" : 0
        },
        {
            "_id" : "http://example.com/bar",
            "value" : 3
        },
        {
            "_id" : "http://example.com/foo",
            "value" : 1
        },
        {
            "_id" : "http://example.com/lost",
            "value" : 0
        }
    ],
    "timeMillis" : 1,
    "counts" : {
        "input" : 4,
        "emit" : 8,
        "reduce" : 2,
        "output" : 4
    },
    "ok" : 1
}