Query array of dictionary in mongodb - mongodb

I have a collection db.orders in mongodb, this is a example:
{
"_id" : ObjectId("5ed269e0f18b5c0d33150ce1"),
"user" : ObjectId("5ed16ace5b16a6100c1ae744"),
"cart" : {
"items" : [
{
"item" : {
"_id" : "5ed16847fb395528f07f2d19",
"tipo" : "anillo",
"imagePath" : "../images/anillos/anillo1.png",
"title" : "Anillo Geo",
"description" : "Anillo en bronce con baño en oro de 24k",
"price" : 25,
"qty" : 1
}
},
{
"item" : {
"_id" : "5ed16847fb395528f07f2d2e",
"tipo" : "collar",
"imagePath" : "../images/collares/collar10.png",
"title" : "Cadenita Pendulo cuarzos naturales",
"description" : "Cadenita en bronce con baño en oro de 18k.",
"price" : 20,
"qty" : 1
}
},
{
"item" : {
"_id" : "5ed16847fb395528f07f2d26",
"tipo" : "anillo",
"imagePath" : "../images/anillos/anillo14.png",
"title" : "Anillo mandala de cristales",
"description" : "Anillo en bronce con baño en oro de 24k, incluye estuche de madera ",
"price" : 20,
"qty" : 1
}
}
],
"totalQty" : 3,
"totalPrice" : 65
},
"address" : "carrera 9/ Madrid",
"city" : "Madrid",
"name" : "Marcela R",
"paymentId" : "ch_1GoIGGByvh8EqLzfNGV02e3e",
"oderdate" : ISODate("2020-05-30T14:12:48.624Z"),
"__v" : 0
}
I need to know how many title: "Anillo geo" and generally how many cart.items.item.title have been sold. I try different methods but I end up with something like this:
{
"_id" : ObjectId("5ed27fee825300b7d182355d"),
"cart" : {
"items" : [
{
"title" : "Anillo Geo",
"price" : 25,
"qty" : 1,
"tipo" : "anillo"
},
{
"title" : "Cadenita Pendulo cuarzos naturales",
"price" : 29,
"qty" : 1,
"tipo" : "collar"
},
{
"title" : "Anillo mandala de cristales",
"price" : 29,
"qty" : 1,
"tipo" : "anillo"
}
]
}
}
But I need a query that give me something like:
{"_id" : Anillo Geo, count: 5}
{"_id" : Cadenita Pendulo cuarzos naturales, count: 8}...
Anyone have any idea of how to do this query?
Kind regards!!

Basically you need to unwind cart.items array & group on _id + title to count no.of titles got repeated for an _id document.
db.collection.aggregate([
{
$unwind: "$cart.items"
},
{
$group: {
_id: { _id: "$_id", title: "$cart.items.item.title" },
count: { $sum: 1 }
}
},
/** transform fields the way you want in output */
{
$project: { _id: "$_id._id", title: "$_id.title", count: 1 }
}
])
Test : mongoplayground
Note :
If possible try to use $match as first stage to do this operation on a single doc i.e; for a single user or at-least lesser docs rather than on an entire collection.

Related

Find MongoDB docs where all sub-docs match criteria

I have some Product documents that each contain a list of ProductVariation sub-documents. I need to find all the Product docs where ALL their child ProductVariation docs have zero quantity.
Schemas look like this:
var Product = new mongoose.Schema({
name: String,
variations: [ProductVariation]
});
var ProductVariation = new mongoose.Schema({
type: String,
quantity: Number,
price: Number
});
I am a little new to mongodb, so even sure where to start here.
Try using $not wrapped around { "$gt" : 0 }:
> db.products.find()
{ "_id" : ObjectId("5b7cae558ff28edda6ba4a67"), "name" : "widget", "variations" : [ { "type" : "color", "quantity" : 0, "price" : 10 }, { "type" : "size", "quantity" : 0, "price" : 5 } ] }
{ "_id" : ObjectId("5b7cae678ff28edda6ba4a68"), "name" : "foo", "variations" : [ { "type" : "color", "quantity" : 2, "price" : 15 }, { "type" : "size", "quantity" : 0, "price" : 5 } ] }
{ "_id" : ObjectId("5b7cae7f8ff28edda6ba4a69"), "name" : "bar", "variations" : [ { "type" : "color", "quantity" : 0, "price" : 15 }, { "type" : "size", "quantity" : 1, "price" : 5 } ] }
> db.products.find({"variations.quantity": { "$not" : { "$gt" : 0 } } })
{ "_id" : ObjectId("5b7cae558ff28edda6ba4a67"), "name" : "widget", "variations" : [ { "type" : "color", "quantity" : 0, "price" : 10 }, { "type" : "size", "quantity" : 0, "price" : 5 } ] }
It can also take advantage of an index on { "variations.quantity" : 1 }.

MongoDB Aggregation - return default value for documents that don't match query

I'm having trouble figuring out the right aggregation pipe operations to return the results I need.
I have a collection similar to the following :-
{
"_id" : "writer1",
"Name" : "writer1",
"Website" : "website1",
"Reviews" : [
{
"Film" : {
"Name" : "Jurassic Park",
"Genre" : "Action"
},
"Score" : 4
},
{
"Technology" : {
"Name" : "Mad Max",
"Genre" : "Action"
},
"Score" : 5
}
]
}
{
"_id" : "writer2",
"Name" : "writer2",
"Website" : "website1",
"Reviews" : [
{
"Technology" : {
"Name" : "Mad Max",
"Genre" : "Action"
},
"Score" : 5
}
]
}
And this is my aggregation so far : -
db.writers.aggregate([
{ "$unwind" : "$Reviews" },
{ "$match" : { "Reviews.Film.Name" : "Jurassic Park" } },
{ "$group" : { "_id" : "$Website" , "score" : { "$avg" : "$Reviews.Score" },
writers :{ $push: { name:"$Name", score:"$Reviews.Score" } }
}}
])
This returns only writers who have a review of the matching film and also only websites that have at least 1 writer who has reviewed the film,
however, I need to return all websites containing a list of their all writers, with a score of 0 if they haven't written a review for the specified film.
so, I am currently getting : -
{ "_id" : "website1", "score" : 4, "writers" : [ { "name" : "writer1", "score" : 4 } ] }
When I actually need : -
{ "_id" : "website1", "score" : 2, "writers" : [ { "name" : "writer1", "score" : 4 },{ "name" :"writer2", "score" : 0 } ] }
Can anyone point me in the right direction?
Cheers

Get document based on multiple criteria of embedded collection

I have the following document, I need to search for multiple items from the embedded collection"items".
Here's an example of a single SKU
db.sku.findOne()
{
"_id" : NumberLong(1192),
"description" : "Uploaded via CSV",
"items" : [
{
"_id" : NumberLong(2),
"category" : DBRef("category", NumberLong(1)),
"description" : "840 tag visual",
"name" : "840 Visual Mini Round",
"version" : NumberLong(0)
},
{
"_id" : NumberLong(7),
"category" : DBRef("category", NumberLong(2)),
"description" : "Maxi",
"name" : "Maxi",
"version" : NumberLong(0)
},
{
"_id" : NumberLong(11),
"category" : DBRef("category", NumberLong(3)),
"description" : "Button",
"name" : "Button",
"version" : NumberLong(0)
},
{
"_id" : NumberLong(16),
"category" : DBRef("category", NumberLong(4)),
"customizationFields" : [
{
"_class" : "CustomizationField",
"_id" : NumberLong(1),
"displayText" : "Custom Print 1",
"fieldName" : "customPrint1",
"listOrder" : 1,
"maxInputLength" : 12,
"required" : false,
"version" : NumberLong(0)
},
{
"_class" : "CustomizationField",
"_id" : NumberLong(2),
"displayText" : "Custom Print 2",
"fieldName" : "customPrint2",
"listOrder" : 2,
"maxInputLength" : 17,
"required" : false,
"version" : NumberLong(0)
}
],
"description" : "2 custom lines of farm print",
"name" : "Custom 2",
"version" : NumberLong(2)
},
{
"_id" : NumberLong(20),
"category" : DBRef("category", NumberLong(5)),
"description" : "Color Red",
"name" : "Red",
"version" : NumberLong(0)
}
],
"skuCode" : "NF-USDA-XC2/SM-BC-R",
"version" : 0,
"webCowOptions" : "840miniwithcust2"
}
There are repeat items.id throughout the embedded collection. Each Sku is made up of multiple items, all combinations are unique, but one item will be part of many Skus.
I'm struggling with the query structure to get what I'm looking for.
Here are a few things I have tried:
db.sku.find({'items._id':2},{'items._id':7})
That one only returns items with the id of 7
db.sku.find({items:{$all:[{_id:5}]}})
That one doesn't return anything, but it came up when looking for solutions. I found about it in the MongoDB manual
Here's an example of a expected result:
sku:{ "_id" : NumberLong(1013),
"items" : [ { "_id" : NumberLong(5) },
{ "_id" : NumberLong(7) },
{ "_id" : NumberLong(12) },
{ "_id" : NumberLong(16) },
{ "_id" :NumberLong(2) } ] },
sku:
{ "_id" : NumberLong(1014),
"items" : [ { "_id" : NumberLong(5) },
{ "_id" : NumberLong(7) },
{ "_id" : NumberLong(2) },
{ "_id" : NumberLong(16) },
{ "_id" :NumberLong(24) } ] },
sku:
{ "_id" : NumberLong(1015),
"items" : [ { "_id" : NumberLong(5) },
{ "_id" : NumberLong(7) },
{ "_id" : NumberLong(12) },
{ "_id" : NumberLong(2) },
{ "_id" :NumberLong(5) } ] }
Each Sku that comes back has both a item of id:7, and id:2, with any other items they have.
To further clarify, my purpose is to determine how many remaining combinations exist after entering the first couple of items.
Basically a customer will start specifying items, and we'll weed it down to the remaining valid combinations. So Sku.items[0].id=5 can only be combined with items[1].id=7 or items[1].id=10 …. Then items[1].id=7 can only be combined with items[2].id=20 … and so forth
The goal was to simplify my rules for purchase, and drive it all from the Sku codes. I don't know if I dug a deeper hole instead.
Thank you,
On the part of extracting the sku with item IDs 2 and 7, when I recall correctly, you have to use $elemMatch:
db.sku.find({'items' :{ '$all' :[{ '$elemMatch':{ '_id' : 2 }},{'$elemMatch': { '_id' : 7 }}]}} )
which selects all sku where there is each an item with _id 2 and 7.
You can use aggregation pipelines
db.sku.aggregate([
{"$unwind": "$sku.items"},
{"$group": {"_id": "$_id", "items": {"$addToSet":{"_id": "$items._id"}}}},
{"$match": {"items._id": {$all:[2,7]}}}
])

MongoDB flatten embedded array

i'd like to create a report of a collection. Its schema is :
(I simplified the schema, to focus on the problematic)
Mongoose Schema
var MobilHomeSchema = new Schema({
id: Schema.Types.ObjectId,
region: String,
equipments:[
{ id: ObjectId, label: String }
]
});
It contains lots of mobilhomes. These mobilhomes are in a campsite, on a region (I chose this group, it could be country, ...). Each mobilhome has some equipments, not always the sames.
I'd like to create a spreadsheet with these columns, to count the number of each equipments in a region (it's just an example)
Expected generic result format
region | equipments.label 1 | equipments.label 2 | equipments.label 3 | ....
Example with "real" values :
region|terrace|pergola|shower
Spain | 30 | 15 |150
France| 55 | 32 |540
...
in json format, it could be :
EDIT
[{
region: "Spain",
terrace: 30,
pergola: 15,
shower: 150
},
{
region: "France",
terrace: 55,
pergola: 32,
shower: 540
}]
/EDIT
How can I do ?
(map-reduce ? a most Business Intelligence tool ?)
Many Thanks !
Don't use map/reduce. Use aggregation. In the mongo shell,
> db.mobile.aggregate([
{ "$unwind" : "$equipments" },
{ "$group" : { "_id" : { "region" : "$region", "label" : "$equipments.label" }, "count" : { "$sum" : 1 } } }
])
On the documents
{ "region" : "France", "equipments" : [ { "_id" : 0, "label" : "terrace" }, { "_id" : 1, "label" : "pergola" } ] },
{ "region" : "France", "equipments" : [ { "_id" : 0, "label" : "shower" }, { "_id" : 1, "label" : "pergola" } ] },
{ "region" : "Spain", "equipments" : [ { "_id" : 0, "label" : "terrace" }, { "_id" : 1, "label" : "shower" } ] },
{ "region" : "Spain", "equipments" : [ { "_id" : 0, "label" : "veranda" }, { "_id" : 1, "label" : "pergola" } ] }
the result is
{ "_id" : { "region" : "Spain", "label" : "veranda" }, "count" : 1 }
{ "_id" : { "region" : "Spain", "label" : "terrace" }, "count" : 1 }
{ "_id" : { "region" : "Spain", "label" : "shower" }, "count" : 1 }
{ "_id" : { "region" : "France", "label" : "shower" }, "count" : 1 }
{ "_id" : { "region" : "France", "label" : "pergola" }, "count" : 2 }
{ "_id" : { "region" : "Spain", "label" : "pergola" }, "count" : 1 }
{ "_id" : { "region" : "France", "label" : "terrace" }, "count" : 1 }
Since you're using an array, presumably you don't know all the possible types of equipment ahead of time, which makes shoving the above results back into one object per region in the aggregation an unwieldy thing to attempt. Better to work with these results in the client.

mongodb Embedded document search on parent and child field

I have a nested embedded document CompanyProduct below is structure
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca3"),
"CompanyID" : 90449,
"Name" : Company1,
"CompanyDepartment" : [
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca4")
"DepartmentID" : 287,
"DepartmentName" : "Stores",
"DepartmentInventory" : [
{
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 1,
"ProductName" : "abc",
"Quantity" : 100
},
{
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 2,
"ProductName" : "xyz",
"Quantity" : 1
}
],
}
],
}
There can be N no of companies and each company can have N number of departments and each department can have N number of products.
I want to do a search to find out a particular product quantity under a particular company
I tried below query but it does not work. It returns all the products for the specific company, the less than 20 condition doesn't work.
db.CompanyProduct.find({$and : [{"CompanyDepartment.DepartmentInventory.Quantity":{$lt :20}},{"CompanyID":90449}]})
How should the query be?
You are searching from companyProduct's sub documents. So it will return you companyProduct whole document, it is NoSQL database , some how we do not need to normalize the collection , but your case it has to be normalize , like if you want to EDIT/DELETE any sub document and if there are thousand or millions of sub document then what will you do ... You need to make other collection with the name on CompanyDepartment and companyProduct collection should be
productCompany
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca3"),
"CompanyID" : 90449,
"Name" : Company1,
"CompanyDepartment" : ['53d213c5ddbb1912343a8ca4'],
}
and other collection companyDepartment
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca4")
"DepartmentID" : 287,
"DepartmentName" : "Stores",
"DepartmentInventory" : [
{
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 1,
"ProductName" : "abc",
"Quantity" : 100
},
{
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 2,
"ProductName" : "xyz",
"Quantity" : 1
}
],
}
after this you got array of companyDeparment' ID and only push and pull query will be used on productCompany
A Solution can be
db.YourCollection.aggregate([
{
$project:{
"CompanyDepartment.DepartmentInventory":1,
"CompanyID" : 1
}
},{
$unwind: "$CompanyDepartment"
},{
$unwind: "$CompanyDepartment.DepartmentInventory"
},{
$match:{$and : [{"CompanyDepartment.DepartmentInventory.Quantity":{$lt :20}},{"CompanyID":90449}]}
}
])
the result is
{
"result" : [
{
"_id" : ObjectId("53d213c5ddbb1912343a8ca3"),
"CompanyID" : 90449,
"CompanyDepartment" : {
"DepartmentInventory" : {
"_id" : ObjectId("53b7b92eecdd765430d763bd"),
"ProductID" : 2,
"ProductName" : "xyz",
"Quantity" : 1
}
}
}
],
"ok" : 1
}