Mongodb在所有集合项上获得了arrayitems的数量

时间:2017-12-05 16:42:15

标签: javascript mongodb

我有一个像这样的mongodb集合:

{
    _id: 123,
    name: 'some name',
    category: 17,
    sizes: ['XS', 'S', 'XL']
},
{
    _id: 124,
    name: 'another name',
    category: 17,
    sizes: ['S', 'L', '2XL']
}

我需要两种不同的方法。第一个:特定类别中每种尺寸有多少项?

{
    17: {
        XS: 0,
        S:  19,
        M:  100
    },
    39: {
        XS: 5,
        ...
    }
}

一个解决方案,只显示,如果大小的项目可用,也可以:

{
    17: {
        XS: false,
        S:  true,
        M:  true,
        ...
    },
    39: {
        XS: true,
        ...
    }
}

第二个问题:我需要相同的,但在对名称进行全文搜索之后。

我试过aggregate字段,但我对如何在数组上执行该操作感到有点迷失。

任何帮助表示感谢。

更新

this answer的帮助下,我更近了一步:

db.so.aggregate(
    [
        // First, filter by name or something else
        // this could also include the category
        { 
            $match: {
                'name': {
                    $regex: /other.*/i
                }
            }
        },

        // explode the sizes-array into single documents
        { '$unwind': '$sizes' },

        // group and count
        { '$group': {
            '_id': '$sizes',
            'count': { '$sum': 1 }
        }}
    ]
)

仍然缺席:按类别

执行此操作

3 个答案:

答案 0 :(得分:2)

以下是我插入的一些示例数据:

/* 1 */
{
    "_id" : 123,
    "name" : "some name",
    "category" : 17,
    "sizes" : [ 
        "XS", 
        "S", 
        "XL"
    ]
}

/* 2 */
{
    "_id" : 124,
    "name" : "another name",
    "category" : 17,
    "sizes" : [ 
        "S", 
        "L", 
        "2XL"
    ]
}

/* 3 */
{
    "_id" : 125,
    "name" : "name",
    "category" : 35,
    "sizes" : [ 
        "S", 
        "L", 
        "2XL"
    ]
}

用例1

您在第一个用例中似乎想要的是按大小和类别进行分组。您实际上可以按多个键进行分组,这是一个示例:

db.so.aggregate([
    // add your match here...
    {
        '$unwind': '$sizes' // flatten your array
    },
    // group and count
    {
        '$group': {
            '_id': {
                sizes: '$sizes',
                category: '$category'
            }, // group by both sizes and category
            'count': {
                '$sum': 1
            },

        }
    },
    {
        '$group': {
            '_id': '$category', // group by category now
            sizeCount: { // create an array that includes the size and the count for that size
                $push: { 
                    size: "$sizes",
                    count: "$count"
                }
            }

        }
    }
])

此管道会创建此结果:

{
    "_id" : 17,
    "sizeCount" : [ 
        {
            "size" : "2XL",
            "count" : 1.0
        }, 
        {
            "size" : "XS",
            "count" : 1.0
        }, 
        {
            "size" : "S",
            "count" : 2.0
        }, 
        {
            "size" : "XL",
            "count" : 1.0
        }, 
        {
            "size" : "L",
            "count" : 1.0
        }
    ]
}

这对你来说是否可以接受?

用例2

现在关于你的第二个用例,你想如何分组这个类别甚至不存在的大小? 但一般来说,您可以使用$cond

来操纵结果

因此,如果您应用此管道,则在同一示例中:

db.so.aggregate([
    // add your match here ...
    {
        '$unwind': '$sizes' // flatten your array
    },
    // group and count
    {
        '$group': {
            '_id': {
                sizes: '$sizes',
                category: '$category'
            }, // group by both sizes and category
            'count': {
                '$sum': 1
            },

        }
    },
    {
        '$project': {
            _id: 0,
            'count': {
                $cond: [{
                    $eq: ["$count", 1.0]
                }, "Limited", "Many"]
            },
            category: "$_id.category",
            sizes: "$_id.sizes"
        }
    },
    {
        '$group': {
            '_id': '$category',
            sizeCount: {
                $push: {
                    size: "$sizes",
                    count: "$count"
                }
            }

        }
    }
])

它会产生以下结果(一个例子):

{
    "_id" : 17,
    "sizeCount" : [ 
        {
            "size" : "2XL",
            "count" : "Limited"
        }, 
        {
            "size" : "XS",
            "count" : "Limited"
        }, 
        {
            "size" : "S",
            "count" : "Many"
        }, 
        {
            "size" : "XL",
            "count" : "Limited"
        }, 
        {
            "size" : "L",
            "count" : "Limited"
        }
    ]
}

所以基本上在这一行$cond: [{$eq: ["$count", 1.0]}, "Limited", "Many"]} 我们说如果count字段只有1.0,那么这个尺码的衬衫是有限的,否则我们有很多。您可以应用任何比较运算符,因此您还可以执行以下操作:$cond: [{$lte: ["$count", 2.0]}, "Limited", "Many"]}

答案 1 :(得分:1)

注意:即将添加投影。

你可以

unwind - > group on category and size - > group on category and push - > project

请参阅以下查询。这将给出没有任何投影的结果。我会尽快添加投影以符合您的要求。

var group_by_category_and_sizes = { 
  "$group": { 
    "_id": { 
        "category": "$category", 
        "size": "$sizes"
      }, 
      "count": { 
        "$sum": 1 
      } 
    } 
  }

var group_by_category_and_push = {
  "$group": {
    "_id": {
      "category": "$_id.category"
    }, 
    "combine": {
      "$push": { "size": "$_id.size", "count": "$count" }
    }
  }
}

db.clothings.aggregate([{ "$unwind": "$sizes" }, group_by_category_and_sizes, group_by_category_and_push])

对于文件

{ name: 'some name', category: 17, sizes: ['XS', 'S', 'XL'] }

{ name: 'another name', category: 17, sizes: ['S', 'L', '2XL'] }

{ name: 'another name', category: 18, sizes: ['M', 'S', 'L'] }

这将产生

{
    "_id": {
        "category": 18
    },
    "combine": [{
        "size": "L",
        "count": 1
    }, {
        "size": "S",
        "count": 1
    }, {
        "size": "M",
        "count": 1
    }]
} {
    "_id": {
        "category": 17
    },
    "combine": [{
        "size": "2XL",
        "count": 1
    }, {
        "size": "S",
        "count": 2
    }, {
        "size": "XL",
        "count": 1
    }, {
        "size": "L",
        "count": 1
    }, {
        "size": "XS",
        "count": 1
    }]
}

答案 2 :(得分:1)

Here is how you can pretty much get to the exact output document you suggested:

db.so.aggregate({
    $unwind: "$sizes" // flatten the sizes array
}, {
    $group: {
        _id: { // group by both category and sizes
            category: "$category",
            size: "$sizes"
        },
        count: {
           $sum: 1 // count number of documents per bucket
        }
    }
}, {
    $group: {
        _id: "$_id.category", // second grouping to get entries per category
        sizes: {
           $push: { k: "$_id.size", v: "$count" } // create an array of key/value pairs which we will need in this exact shape in the next stage
        }
    }
}, {
    $project: {
        "magic": {
            $arrayToObject: // transform the key/value pair we generate below into a document
            [[{
                // the $substr is a hack to transform the numerical category (e.g. 17) 
                // into a string (not nice, probably not supported but working for now...)
                // which is needed for the above $arrayToObject to work
                k: { $substr: [ "$_id", 0, -1 ] }, 
                v: {
                    $arrayToObject: "$sizes" // turn the key/value pairs we created in the previous pipeline stage into a document
                }
            }]]
        }
    }
}, {
    $replaceRoot: {
        newRoot: "$magic" // promote our "magic" field to the document root
    }
})

Please note that while this gives you the right output, I would not necessarily suggest going down that route since the aggregation pipeline is pretty hefty with quite some magic built into it for very little measurable benefit. So if you can accept an output structure like the one suggested by @Alex P. then this will certainly be easier to understand and maintain and faster as well.

With respect to your second scenario: You can add any number of preliminary $match stages before the $unwind stage to filter out any superfluous data.