How to have multiple nested arrays in an array - mongodb

I have the collection data from a csv file with header. When i run my query
db.ties.aggregate(
[
{
$group:
{
_id: { "SHOP": "$SHOP" },
isLinkedTo: { $push: { "PERSON": "$PERSON", "CITY": "$CITY", "ROOM": "$ROOM", "STYLE": "$STYLE", "hasDonated": {"DATE": "$DATE", "OBJECT": "$OBJECT", "COST": "$COST", "COLOR": "$COLOR", "PAYMENT": "$PAYMENT"}}}
}
},
{ $out: "ties"}
],
{ allowDiskUse: true }
)
I have like result:
{
"_id": {
"Shop": "FirstShopNameCovered"
},
"isLinkedTo": [{
"PERSON": "Carleen",
"CITY": "Rome",
"ROOM": "Kitchen",
"STYLEPREFERED": "Modern",
"hasDonated": {
"DATE": "2019-10-11",
"OBJECT": "Set of dishes",
"COST": 72,
"COLOR": "White",
"PAYMENT": "Credit card"
}
}, {
"PERSON": "Carleen",
"CITY": "Rome",
"ROOM": "Kitcher",
"STYLEPREFERED": "Modern",
"hasDonated": {
"DATE": "2018-10-26",
"OBJECT": "Set of chairs",
"COST": 353,
"COLOR": "Grey",
"PAYMENT": "Coupon"
}
}, {
"PERSON": "Pernick",
"CITY": "Venezia",
"ROOM": "Bathroom",
"STYLE": "Minimalist",
"hasDonated": {
"DATE": "2018-09-18",
"OBJECT": "Mirror",
"COST": 68,
"COLOR": "Brown",
"PAYMENT": "Credit card"
}
}
You can see that there is replicated the Person "PERSON": "Carleen" with all data with 2 different arrays hasDonated.
I wish have something like this result, with person not replicated that contains all hasDonated arrays where he is present:
"_id": {
"Shop": "NameCovered"
},
"isLinkedTo": [{
"PERSON": "Carleen",
"CITY": "Rome",
"ROOM": "Kitchen",
"STYLE": "RetrĂ²",
"hasDonated": {
"DATE": "2019-10-11",
"OBJECT": "Set of dishes",
"COST": 72,
"COLOR": "White",
"PAYMENT": "Credit card"
},
{
"DATE": "2018-10-26",
"OBJECT": "Chair",
"COST": 53,
"COLOR": "Grey",
"PAYMENT": "Coupon"
}
}, {
"PERSON": "Pernick",
"CITY": "Venezia",
"ROOM": "Bathroom",
"STYLE": "Minimalist",
"hasDonated": {
"DATE": "2018-09-18",
"OBJECT": "Mirror",
"COST": 68,
"COLOR": "Brown",
"PAYMENT": "Credit card"
}
How can I do to have the result like this?

First we need to $unwind to flat the array. Then group the hasDonated using $group where unique is found by combination of "_id" and "PERSON" as you mentioned.
[
{
"$unwind": "$isLinkedTo"
},
{
$group: {
_id: {
_id: "$_id",
per: "$isLinkedTo.PERSON"
},
isLinkedTo: {
$first: {
PERSON: "$isLinkedTo.PERSON",
CITY: "$isLinkedTo.CITY",
ROOM: "$isLinkedTo.ROOM",
STYLEPREFERED: "$isLinkedTo.STYLEPREFERED"
}
},
hasDonated: {
$addToSet: "$isLinkedTo.hasDonated"
}
}
},
{
$addFields: {
_id: "$_id._id",
"isLinkedTo.hasDonated": "$hasDonated"
}
},
{
$project: {
hasDonated: 0
}
},
{
$group: {
_id: "$_id",
isLinkedTo: {
$push: "$isLinkedTo"
}
}
}
]
Working Mongo playground

Related

MongoDB Select By Group along with that Count Unique match exclude array and object fields Get data sort by latest objects

I have a collection where from the backend user can input multiple same name bikes but with different registration number but in front-End I want them to be grouped by matching the same name but as user updates separately display image changes but I want only one display image as it is 1 vehicle
provided there is a node created I will implement it we can sort it by the latest and take the price and image of it
Activa -2 Count
KTM -1 Count
but there is a catch.
Activa 2 bikes but I want only count 2 and the price as it is the same in an array I want only 1 and the same applies to displayimage here display image file path is different but I want the latest one only Sharing data below
Data:
[
{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62e69ee3edfe4d0f3cb4994a",
"bikename": "KTM",
"bikenumber": "KA05HM2034",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"displayimage": {
"file": "bike-2020-honda-city-exterior-8-1659281111883.jpg",
"file_path": "https://www.example.com/images/upload/bike-2020-honda-city-exterior-8-1659281111883.jpg",
"idx": 1
}
},
{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62dba8418ef8f51f454ed757",
"bikename": "Activa",
"bikenumber": "KA05HM2033",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"displayimage": {
"file": "bike-v_activa-i-deluxe-1658562557459.jpg",
"file_path": "https://www.example.com/images/upload/bike-v_activa-i-deluxe-1658562557459.jpg",
"idx": 0
}
},
{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62d7ff7e70b9ab38c6ab0cb1",
"bikename": "Activa",
"bikenumber": "KA05HM2223",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"afterfreekmprice": 22,
"descreption": "Activa",
"displayimage": {
"file": "bike-v_activa-i-deluxe-1658322798414.jpg",
"file_path": "https://www.example.com/images/upload/bike-v_activa-i-deluxe-1658322798414.jpg",
"idx": 0
}
}
]
Expected:
[
{
"_id":{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62dba8418ef8f51f454ed757",
"bikename": "Activa",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"displayimage": {
"file": "bike-v_activa-i-deluxe-1658562557459.jpg",
"file_path": "https://www.example.com/images/upload/bike-v_activa-i-deluxe-1658562557459.jpg",
"idx": 0
}
},
"count": 2
},
{
"_id":{
"price": [
{
"Description": "Hourly",
"Price": "1"
},
{
"Description": "Daily",
"Price": "11"
},
{
"Description": "Monthly",
"Price": "111"
}
],
"_id": "62e69ee3edfe4d0f3cb4994a",
"bikename": "KTM",
"bikebrand": {
"id": 1,
"label": "Honda"
},
"freekm": 234,
"displayimage": {
"file": "bike-2020-honda-city-exterior-8-1659281111883.jpg",
"file_path": "https://www.example.com/images/upload/bike-2020-honda-city-exterior-8-1659281111883.jpg",
"idx": 1
}
}
"count": 1
}
]
You can use the aggregation pipeline,
$sort by _id in descending order
$group by bikename and get the first root document that is latest one in root and count total documents in count
$project to show required documents
db.collection.aggregate([
{ $sort: { _id: -1 } },
{
$group: {
_id: "$bikename",
root: { $first: "$$ROOT" },
count: { $sum: 1 }
}
},
{
$project: {
_id: "$root",
count: 1
}
}
])
Playground
You can use $group for this:
db.collection.aggregate([
{$group: {
_id: "$bikename",
count: {$sum: 1},
data: {$first: "$$ROOT"}
}
},
{$set: {"data.count": "$count"}},
{$replaceRoot: {newRoot: "$data"}}
])
See how it works on the playground example

How to group the documents which I just unwinded in mongodb?

This is the initial document. I applied $unwind on "colors" and another $unwind on "colors.sizes". But now am not able to group them back. How is that done?
{
"_id": ObjectId("5ef838255c959771c46fc917"),
"created": ISODate("2017-03-04T18:30:00.000Z"),
"updated": ISODate("2020-06-28T00:49:23.000Z"),
"status": "active",
"productId": "3828",
"tags": {
"colors": ["BEIGE", "BLACK", "BLUE", "GREEN", "GREY", "LIGHT BEIGE", "LIGHT PINK", "NAVY", "PINK", "WHITE"],
"size": ["39", "39.5", "40", "40.5", "41", "41.5", "42", "42.5", "43", "43.5", "44", "44.5", "45", "45.5", "46"],
"category": ["SHOES", "SNEAKERS", "MENS SHOES & ACCESSORIES"],
"price": ["3400.00", "1360.00", "2040.00", "1700.00", "45575.00"],
"season": ["SS(2020)", "AW(2019)", "SS(2019)", "AW(2018)", "SS(2017)", "AW(2016)", "SS(2016)", "SS(2015)"],
"hash": ["ACTIVE", "ANKLE", "BLUSH", "COLLAR", "COLOUR", "DESIGNER", "FOIL", "GOLD-TONE", "LACE-UP", "LEATHER", "LOW", "LOW TOP", "ORIGINAL", "PADDED", "ROUND", "ROUND TOE", "RUBBER", "SIGNATURE", "TONAL"]
},
"name": "ORIGINAL ARCHILLES LOW SNEAKER",
"description": "",
"brand": "COMMON PROJECTS",
"styleCode": "220036200",
"availableOnline": true,
"colors": [{
"images": [],
"_id": ObjectId("5ef838255c959771c46fc918"),
"colorId": "1",
"color": "WHITE",
"hexCode": "#FFFFFF",
"status": "active",
"sizes": [{
"extraInfo": [{
"title": "Size And Fit",
"text": "Product measures: Heel 3cm (1.2\"). True to size"
}, {
"title": "Information",
"text": "Designer colour: White. Leather. Rubber outsole. Made in Italy"
}],
"_id": ObjectId("5ef838255c959771c46fc919"),
"sizeId": "1",
"neo": "0220003258813",
"size": "39",
"originalPrice": "3400.00",
"sellingPrice": "3400.00",
"discountPercent": "0.00",
"status": "active"
}, {
"extraInfo": [{
"title": "Size And Fit",
"text": "Product measures: Heel 3cm (1.2\"). True to size"
}, {
"title": "Information",
"text": "Designer colour: White. Leather. Rubber outsole. Made in Italy"
}],
"_id": ObjectId("5ef838255c959771c46fc91a"),
"sizeId": "2",
"neo": "0220111220849",
"size": "39.5",
"originalPrice": "3400.00",
"sellingPrice": "3400.00",
"discountPercent": "0.00",
"status": "active"
}, {
"extraInfo": [{
"title": "Size And Fit",
"text": "Product measures: Heel 3cm (1.2\"). True to size"
}, {
"title": "Information",
"text": "Designer colour: White. Leather. Rubber outsole. Made in Italy"
}],
"_id": ObjectId("5ef838255c959771c46fc91b"),
"sizeId": "3",
"neo": "0220004218564",
"size": "40",
"originalPrice": "3400.00",
"sellingPrice": "3400.00",
"discountPercent": "0.00",
"status": "active"
}]
}, ]
}
db.getCollection('products').aggregate([
{$match:{"productId":"3828"}},
{$unwind:"$colors"},
{$unwind:"$colors.sizes"},
{
$lookup: {
from:"inventories",
localField: "colors.sizes.neo",
foreignField: "sku",
as:"colors.sizes.stores"
}
}
])
The opposite operator of $unwind is $group
Explanation
We $group by productId + colors[*]._id fields grouping root, colors and sizes separately.
Note: You can define each variable separately like this:
created: {$first:"$created"},
updated: {$first:"$updated"},
...
OR
use aggregation variable $$ROOT which contains entire document fields
We "merge" each variables into single object and move into root level.
Note: If we don't use $replaceRoot, the data structure will looks like this:
{
_id: "...",
root: {
_id: "...",
...
colors:{
...
sizes:[...]
}
}
}
Note: Unfortunately, MongoDB doesn't allow merge subkeys like this:
{
$mergeObjects:[
"$root",
{"colors":"$colors"},
{"colors.sizes":"$sizes"} <-- Not allowed
]
}
So we have to use $objectToArray + $map + $arrayToObject operators
$objectToArray - transforms object into array
{"hello": "world"} ---> [{"k":"hello", "v":"world"}]
$map - iterates array and transforms array content.
In this case, we replace `sizes` field with `sizes` from the 1st step
$objectToArray - transforms "special" array into object
[{"k":"hello", "v":"world"}] ---> {"hello": "world"}
Now we group only by productId and group colors and root values separately
We finish merging variables and transform into desired object (same as 2nd step)
Add these steps into your pipeline:
{
$group: {
"_id": {
productId: "$productId",
colors: "$colors._id"
},
"root": {
$first: "$$ROOT"
},
"colors": {
$first: "$colors"
},
"sizes": {
$push: "$colors.sizes"
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$root",
{
"colors": {
$arrayToObject: {
$map: {
input: { $objectToArray: "$colors" },
as: "color",
in: {
k: "$$color.k",
v: {
$cond: [
{ $eq: [ "$$color.k", "sizes" ] },
"$sizes",
"$$color.v"
]
}
}
}
}
}
}
]
}
}
},
{
$group: {
"_id": "$_id",
"root": {
$first: "$$ROOT"
},
"colors": {
$push: "$colors"
}
}
},
{
$replaceRoot: {
newRoot: {
$mergeObjects: [
"$root",
{ "colors": "$colors" }
]
}
}
}
MongoPlayground

Group items inside of another group

I want to group the following collection by category and sum its total value, then create a subcategory attribute, on same structure, summing subcategories if more than one equal.
[
{
"_id": 1,
"date": ISODate("2019-10-10T00:00:00.000Z"),
"description": "INTERNET BILL",
"credit": "",
"debit": "-100.00",
"category": "home",
"subcategory": "internet",
"__v": 0
},
{
"_id": 2,
"date": ISODate("2019-10-10T00:00:00.000Z"),
"description": "WATER BILL",
"credit": "",
"debit": "-150.00",
"category": "home",
"subcategory": "water",
"__v": 0
},
{
"_id": 3,
"date": ISODate("2019-10-10T00:00:00.000Z"),
"description": "MC DONALDS",
"credit": "",
"debit": "-30.00",
"category": "food",
"subcategory": "restaurants",
"__v": 0
},
{
"_id": 4,
"date": ISODate("2019-10-10T00:00:00.000Z"),
"description": "BURGER KING",
"credit": "",
"debit": "-50.00",
"category": "food",
"subcategory": "restaurants",
"__v": 0
},
{
"_id": 5,
"date": ISODate("2019-10-10T00:00:00.000Z"),
"description": "WALMART",
"credit": "",
"debit": "-20.00",
"category": "food",
"subcategory": "groceries",
"__v": 0
},
]
Desireble output:
[
{
"_id": "home",
"total": "-250.00",
"subcategory" : [
{"id": "internet", "total": "-100"},
{"id": "water", "total": "-150"}
]
},
{
"_id": "food",
"total": "-100.00",
"subcategory" : [
{"id": "restaurants", "total": "-80"},
{"id": "groceries", "total": "-20"}
]
}
]
With the following query, I've almost achieved it, but I haven't find a way to sum values on subcategories.
db.getCollection('expenses').aggregate([
{$match:
{"date" : { "$gte" : new Date("11-10-2019"), "$lte": new Date("2019-10-11") }}
},
{$group: {
_id: "$category",
total: { $sum: { $toDouble: { $cond: { if: { $ne: [ "$debit", ""] }, then: "$debit", else: "$credit" } } } },
subcategories: { $addToSet: {id: "$subcategory" }},
}}
])
You can to $group twice (by subcategory first):
db.collection.aggregate([
{
$group: {
_id: { category: "$category", subcategory: "$subcategory" },
total: { $sum: { $toDouble: "$debit" } }
}
},
{
$group: {
_id: "$_id.category",
total: { $sum: "$total" },
subcategories: { $push: { id: "$_id.subcategory", total: "$total" } }
}
}
])
Mongo Playground

Need to return matched data from mongo db JSON

I have Json which have values like state_city details this contains information like which city belongs to which state -
Need to query it for particular state name which will gives me all cities that belongs to that state.
db.collection.find({
"count": 10,
"state.name": "MP"
})
[
{
"collection": "collection1",
"count": 10,
"state": [
{
"name": "MH",
"city": "Mumbai"
},
{
"name": "MH",
"city": "Pune"
},
{
"name": "UP",
"city": "Kanpur"
},
{
"name": "CG",
"city": "Raipur"
}
]
},
{
"collection": "collection2",
"count": 20,
"state": [
{
"name": "MP",
"city": "Indore"
},
{
"name": "MH",
"city": "Bhopal"
},
{
"name": "UP",
"city": "Kanpur"
},
{
"name": "CG",
"city": "Raipur"
}
]
}
]
You have to use aggregate query to get only matching elements in array :
db.collection.aggregate([{
$unwind: "$content.state"
},
{
$match: {
"content.state.name": "MH",
"count": 10
}
},
{
$group: {
_id: "$content.state.city",
}
},
{
$addFields: {
key: 1
}
},
{
$group: {
_id: "$key",
cities: {
$push: "$_id"
}
}
},
{
$project: {
_id: 0,
cities: 1
}
}
])
This query will return :
{
"cities": [
"Pune",
"Mumbai"
]
}
The following query would be the solution.
db.collection.find({ "count": 10, "state":{"name": "MP"}})
For more complex queries, $elemMatch is also available.

Need to return sum of score more than 60 where is score is an element inside an array JSON objects

Documents which I have in my MongoDB collection are like below and the length of the array grades of JSON objects is varying:
{
"address": {
"building": "1007",
"coord": [-73.856077, 40.848447],
"street": "Morris Park Ave",
"zipcode": "10462"
},
"borough": "Bronx",
"cuisine": "Bakery",
"grades": [{
"date": {
"$date": 1393804800000
},
"grade": "A",
"score": 2
}, {
"date": {
"$date": 1378857600000
},
"grade": "A",
"score": 6
}, {
"date": {
"$date": 1358985600000
},
"grade": "A",
"score": 10
}, {
"date": {
"$date": 1322006400000
},
"grade": "A",
"score": 9
}, {
"date": {
"$date": 1299715200000
},
"grade": "B",
"score": 14
}],
"name": "Morris Park Bake Shop",
"restaurant_id": "30075445"
}
{
"address": {
"building": "469",
"coord": [-73.961704, 40.662942],
"street": "Flatbush Avenue",
"zipcode": "11225"
},
"borough": "Brooklyn",
"cuisine": "Hamburgers",
"grades": [{
"date": {
"$date": 1419897600000
},
"grade": "A",
"score": 8
}, {
"date": {
"$date": 1404172800000
},
"grade": "B",
"score": 23
}, {
"date": {
"$date": 1367280000000
},
"grade": "A",
"score": 12
}, {
"date": {
"$date": 1336435200000
},
"grade": "A",
"score": 12
}],
"name": "Wendy'S",
"restaurant_id": "30112340"
}
The task is to write a MongoDB query to return the collections which are having the sum of scores greater than 60.
Here is how this can be done:
db.collection.aggregate({
$unwind: "$grades" // flatten the "grades" array
}, {
$group: {
"_id": "$_id",
"sumOfScores": { // calculate the sum of all grades for all documents with the same "_id"
$sum: "$grades.score"
},
"docs": {
$push: "$$ROOT" // remember all affected documents per group
}
}
}, {
$match: {
"sumOfScores": {
$gt: 60 // filter out everything that we don't care about
}
}
}, {
$unwind: "$docs" // flatten the "docs" array
}, {
$group: { // restore the original document structure
"_id": "$docs._id",
"address": { $first: "$docs.address" },
"borough": { $first: "$docs.borough" },
"cuisine": { $first: "$docs.cuisine" },
"grades": {
$push: "$docs.grades"
},
"name": { $first: "$docs.name" },
"restaurant_id": { $first: "$docs.restaurant_id" }
}
})