Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongo aggregate query for facets navigation

I have a collection products with documents (products) like this (relevant code only):

{ "_id": "prod1", ... "colors": ["red","blue"], "size": ["S", "M"], ...}
{ "_id": "prod2", ... "colors": ["red","green"], "size": ["S", "XL"], ...}
{ "_id": "prod3", ... "colors": ["yellow","blue"], "size": ["XL", "XXL"], ...}

I would like to offer the user only the available selection, as a faceted search, like the name of the option and the number of options available.

colors:

red 2
blue 2
green 1
yellow 1

sizes:

S 2
M 1
XL 2
XXL 1

If I had to do it in SQL, I would probably have to run a query for each facet group.

SELECT colors, count(*) AS number FROM products GROUP BY colors

than the second query

SELECT size, count(*) AS number FROM test GROUP BY sizes

If somebody chose color "red", than I would probably have to add to each query the "WHERE" clause:

WHERE colors = 'red'

Could somebody more experienced than me, help me with this example in MongoDB? Do I have to run two queries or is there something that I am missing and it could be done in one query? I assume I will have a great number of products. Any help appreciated. Especially if there are any tricks to speed it up. I would like to get it right from the beginning, hence asking the question. Thanks.

like image 298
Vincent Avatar asked May 30 '26 20:05

Vincent


2 Answers

Here is the solution for your query with MongoDB aggregation. Below is my dummy collection.

    {
        "_id" : ObjectId("584b82055855b8ea7ea29d65"),
        "colors" : [ 
            "Red", 
            "Yellow"
        ],
        "size" : [ 
            "S", 
            "M"
        ]
    }
    {
        "_id" : ObjectId("584b82185855b8ea7ea29d66"),
        "colors" : [ 
            "Red", 
            "Orange"
        ],
        "size" : [ 
            "S", 
            "XL"
        ]
    }
.
.

After running below query.

    db.getCollection('products').aggregate([
    {$unwind : "$colors"},
    {$group : {
        _id : "$colors",
        "sum": {$sum : 1}
    }},
    {
        $project : {
            _id : 0,
            "color":"$_id",
            "count":"$sum"
        }
    }
])

Then the output is :

{
    "color" : "Green",
    "count" : 2
}
{
    "color" : "Orange",
    "count" : 1
}
{
    "color" : "Yellow",
    "count" : 2
}
{
    "color" : "Red",
    "count" : 2
}

If you want to find sum only for Red Color then below query for this

db.getCollection('products').aggregate([
    {$unwind : "$colors"},
    {$match : {"colors":"Red"}},
    {$group : {
        _id : "$colors",
        "sum": {$sum : 1}
    }},
    {
        $project : {
            _id : 0,
            "color":"$_id",
            "count":"$sum"
        }
    }
])

The output of above query is :

{
    "color" : "Red",
    "count" : 2
}
like image 117
Anish Agarwal Avatar answered Jun 02 '26 20:06

Anish Agarwal


db.entry.aggregate([
{$unwind : "$size" },
{$group: { "_id" : "$size", count : { $sum : 1 } } }
]).pretty()

Hope this helps, you can customize as you wish for the 'count' field and $unwind definition:

Deconstructs an array field from the input documents to output a document for each element. Each output document is the input document with the value of the array field replaced by the element.

https://docs.mongodb.org/manual/reference/operator/aggregation/unwind/

like image 23
ninu Avatar answered Jun 02 '26 19:06

ninu



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!