【问题标题】:Mongoose return array with .count of each element alongside each element value?Mongoose返回数组,每个元素的.count以及每个元素值?
【发布时间】:2014-07-30 03:42:24
【问题描述】:

现在,这个 expressJS 调用将逗号分隔的值提供给一个导致 .find 的 mongoose .count,但分别返回计数和值..

我将如何将结果构造为一个数组,其中包含每个搜索的标签 + 有多少故事有该标签的计数,以一种看起来像这样的方式(使用模板引擎循环):

标签:[{标签:标签1,计数:4},{标签:标签2, 计数:2 }]

这是快递/猫鼬:

router.route('/tag/:tags')

    // get the list of tags for userGenerated tags
    // accessed by GET at http://localhost:4200/api/v1/tag/tag1,tag2
    .get(function(req, res) {

        var tags = req.params.tags.split(',');

        console.log(tags); // ['tag1', 'tag2']

        Story.count( { tags: { $in: tags } }, function (err, count) {

            if (err)
                res.send(err);

            console.log('there is/are %d story(ies) with this/these tag(s)', count);

            if (count >= 1) {

                Story.find( { tags: { $in: tags } }, 'tags', function(err, stories) {

                    if (err)
                        res.send(err);

                    res.json(storytags);

                }); // find and return tags of stories

            } // if count >= 1

            if (count < 1) {

                res.json({ message: count + ' results' });

            } // if count < 1 

        }); // .count

    }); // get list of tags

【问题讨论】:

  • 你所说的“各自计数”是什么意思?您能否解释更多您的预期输出中的内容tags : [ { element: element1, count: count1 }, { element: element2, count: count2 } ](故事是元素吗?计数是标签出现在故事​​中的次数吗?)
  • @dylants 一个更符合上下文的 json 输出将是 tags : [ { tag: tag1, count: 4 }, { tag: tag2, count: 2 } ] (故事是具有标签数组的数据库文档),其中将返回一个包含每个查询标签的数组,并且标签的计数(出现该标签的故事)
  • 那么对我来说第一个Story.count 不是必需的,因为它会计算包含这些标签的所有故事。您需要的是第二个查询,即上面的Story.find,然后可能会遍历那些故事,收集您需要在您描述的tags 块中输出的统计信息。

标签: javascript node.js mongodb mongoose aggregation-framework


【解决方案1】:

这里不是很清楚“标签”实际上是如何出现在您的文档中的,它要么只是字段中的单个值,要么是数组中的一个值。无论如何,如果您打算计算标签,那么您最好使用aggregation framework,而不是“后处理”您的.find() 代码。

首先将“标签”作为单个字段值:

    Story.aggregate(
        [
            // Match the documents with tags
            { "$match": { "tags": { "$in": tags } }},

            // Group the tags with counts
            { "$group": {
                "_id": "$tags",
                "count": { "$sum": 1 }
            }},

            // Group to single array with total count
            { "$group": {
                "_id": null,
                "tags": { 
                    "$push": {
                        "tag": "$_id",
                        "count": "$count"
                    }
                },
                "totalCount": { "$sum": "$count" }
            }},

            // Project to remove the _id
            { "$project": {
                "_id": 0,
                "tags": 1,
                "totalCount": 0
            }}
        ],
        function(err,result) {

            if (err)
                res.send(err);

            res.json( result[0] );

        }
    );

这几乎包含了您的整个列表,因此不需要额外的.count() 操作,但也不确定您是否真的需要它。

如果“标签”实际上是一个数组,那么您需要做一些不同的事情,因为您需要从数组中提取匹配项以获取计数:

    Story.aggregate(
        [
            // Match the documents with tags
            { "$match": { "tags": { "$in": tags } }},

            // Unwind the array
            { "$unwind": "$tags" },

            // Actually match the tags within the now unwound array
            { "$match": { "tags": { "$in": tags } }},

            // Group by each tag
            { "$group": {
                "_id": "$tags",
                "count": { "$sum": 1 }
            }},

            // Rename the _id for your output
            { "$project": {
               "_id": 0,
               "tag": "$_id",
               "count": 1
            }}

        ],
        function(err,result) {

            if (err)
                res.send(err);

            res.json({ "totalCount": count, "tags": result });

        }
    );

或者使用 MongoDB 2.6 或更高版本,您可以通过在 $unwind 使用 $map 运算符和其他操作符之前过滤数组来简化一点。

为了清楚起见,我将在这里扩展 tags

    Story.aggregate(
        [
            // Match the documents with tags
            { "$match": { "tags": { "$in": ["tag1","tag2" } }},


            // Filter the array for matches
            { "$project": {
                "tags": {
                   "$setDifference": [
                       { 
                           "$map": {
                               "input": "$tags",
                               "as": "el",
                               "in": {
                                   "$cond": [
                                       { "$or": [
                                           { "$eq": ["$$el", "tag1" ] },
                                           { "$eq": ["$$el", "tag2" ] },
                                       ]},
                                       "$$el",
                                       false
                                   ]
                               }
                           }
                       },
                       [false]
                   ]
                }
            }},

            // Unwind the array already filtered
            { "$unwind": "$tags" },

            // Group by each tag
            { "$group": {
                "_id": "$tags",
                "count": { "$sum": 1 }
            }},

            // Rename the _id for your output
            { "$project": {
               "_id": 0,
               "tag": "$_id",
               "count": 1
            }}

        ],
        function(err,result) {

            if (err)
                res.send(err);

            res.json({ "totalCount": count, "tags": result });

        }
    );

这样做的好处是在调用$unwind之前从数组中删除你不想要的“标签”,这可以根据数组元素的数量加快匹配速度。

当然,虽然上述示例在获取文档计数方面与第一个示例不同,但如果您更喜欢组合结果,您仍然可以将其包含在一些额外的分组中:

    Story.aggregate(
        [
            // Match the documents with tags
            { "$match": { "tags": { "$in": ["tag1","tag2" } }},


            // Filter the array for matches
            { "$project": {
                "tags": {
                   "$setDifference": [
                       { 
                           "$map": {
                               "input": "$tags",
                               "as": "el",
                               "in": {
                                   "$cond": [
                                       { "$or": [
                                           { "$eq": ["$$el", "tag1" ] },
                                           { "$eq": ["$$el", "tag2" ] },
                                       ]},
                                       "$$el",
                                       false
                                   ]
                               }
                           }
                       },
                       [false]
                   ]
                }
            }},

            // Unwind the array already filtered
            { "$unwind": "$tags" },

            // Group by each tag
            { "$group": {
                "_id": "$tags",
                "count": { "$sum": 1 },
                "docs": { "$addToSet": "$_id" }
            }},

            // Group to a single array response
            { "$group": {
                "_id": null,
                "tags": {
                    "$push": {
                        "tag": "$_id",
                        "count": "$count"
                    }
                },
                "totalCount": { "$sum": { "$size": "$docs" } }
            }

            // Rename the fields for your output
            { "$project": {
               "_id": 0,
               "tags": 1,
               "count": 1,
               "totalCount"
            }}

        ],
        function(err,result) {

            if (err)
                res.send(err);

            res.json( result[0] );

        }
    );

同样,可以在没有 $size 等运算符的帮助的情况下通过不同的分组来获得单个结果中的“totalCount”,实际上也可以在没有 $addToSet 的情况下完成。

但这里的总体方向是,如果您计算“标签”,您基本上想要去的地方,因为您可以让服务器为您完成工作。

【讨论】:

  • 非常感谢.. 非常感谢您提供的详细信息.. 很快就会整合它,并期待届时接受答案.. 再次感谢
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2015-08-15
  • 1970-01-01
  • 1970-01-01
  • 2017-10-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多