Related
I have created a complex MongoDB document like this :
{
"_id": {
"$oid": "6354129e0f5b15991649fd10"
},
"orderId": "NEK-2209-06215614-79553",
"user": {
"$oid": "634d11565f254092fd666fd1"
},
"shippingAddress": {
"$oid": "6353aaf0fa6a1b0124c22532"
},
"billingAddress": {
"$oid": "6353aaf0fa6a1b0124c22532"
},
"productInfo": [
{
"seller": {
"$oid": "634d784c723ee32fc178aa7a"
},
"products": [
{
"productId": {
"$oid": "6353951e001ff50ea1a92602"
},
"quantity": 2,
"variation": "M",
"tax": 111
}
],
"price": 850,
"status": "Pending"
},
{
"seller": {
"$oid": "6354112f0f5b15991649fcfc"
},
"products": [
{
"productId": {
"$oid": "635411940f5b15991649fd02"
},
"quantity": 2,
"variation": "M",
"tax": 111
}
],
"price": 850,
"status": "Pending"
}
],
"total": 1671,
"shippingFees": 60,
"couponDiscount": 10,
"subtotal": 1721,
"paymentInfo": {
"paymentType": "Cash on Delivery"
},
"paymentMethod": "Home Delivery",
"createdAt": {
"$date": {
"$numberLong": "1666454174641"
}
},
"updatedAt": {
"$date": {
"$numberLong": "1666454174641"
}
},
"__v": 0
}
Here you can see that ProductInfo is an array. My Document structure is
id: "id"
productInfo: [
{seller: "id", ....},
{seller: "id", ....},
]
Now I have two things- id and seller
Actually here I want to do this- first, find by id, then filter productInfo by seller and update status to this particular seller. How can I do that ?
mydocument.findByIdAndUpdate(id,
//Here I have to write an update value to a particular seller from productInfo array.
, {new: true})
Please help me to do this. Can anyone help me?
**Note: Here I want to update only status value from a particular seller when matched find by document id.
You can do it with positional operator - $:
db.collection.update({
_id: ObjectId("6354129e0f5b15991649fd10"),
"productInfo.seller": ObjectId("6354112f0f5b15991649fcfc"),
},
{
"$set": {
"productInfo.$.status": "New status"
}
})
Working example
I'm having an issue with making count for items returned from an array without assuming or using those fields in my aggregration.
Data structure looks like this:
[
{
"_id": "1",
"title": "Vanella Icream",
"contain": "sugar",
"details": [
{
"flavour": "Vanella"
},
{
"weight": "10KG"
},
{
"sugar": "15KG"
}
]
},
{
"_id": "2",
"title": "Pretzels",
"contain": "salt",
"details": [
{
"flavour": "Wheat"
},
{
"weight": "10KG"
},
{
"sugar": "15KG"
}
]
},
{
"_id": "3",
"title": "Rasmalai Icream",
"contain": "sugar",
"details": [
{
"flavour": "Vanella"
},
{
"weight": "15KG"
},
{
"sugar": "12KG"
}
]
},
{
"_id": "4",
"title": "Vanella Icream",
"contain": "sugar",
"details": [
{
"flavour": "Vanella"
},
{
"weight": "15KG"
},
{
"sugar": "12KG"
}
]
}
]
Output I want:
[
{
"details": {
"flavour": {
"Vanella": 3, //Number of times Vanella present in each document.
"Wheat": 1,
},
"weight": {
"10KG": 2,
"15KG": 2
},
"sugar": {
"12KG": 2,
"15KG": 2
}
}
}
]
Query:
db.collection.aggregate([
{
"$unwind": {
"path": "$details"
}
},
{
"$replaceRoot": {
"newRoot": {
"$mergeObjects": [
"$details",
"$$ROOT"
]
}
}
},
{
"$facet": {
"flavour": [
{
"$group": {
"_id": "$flavour",
"sum": {
"$sum": 1
}
}
},
{
"$addFields": {
"flavour": "$_id"
}
},
{
"$project": {
"_id": 0
}
}
],
"weight": [
{
"$group": {
"_id": "$weight",
"sum": {
"$sum": 1
}
}
},
{
"$addFields": {
"weight": "$_id"
}
},
{
"$project": {
"_id": 0
}
}
]
}
},
{
"$addFields": {
"flavour": {
"$reduce": {
"input": {
"$filter": {
"input": {
"$map": {
"input": "$flavour",
"as": "w",
"in": {
"$cond": [
{
"$ne": [
"$$w.flavour",
null
]
},
{
"$let": {
"vars": {
"o": [
[
"$$w.flavour",
"$$w.sum"
]
]
},
"in": {
"$arrayToObject": "$$o"
}
}
},
null
]
}
}
},
"as": "f",
"cond": {
"$ne": [
"$$f",
null
]
}
}
},
"initialValue": {},
"in": {
"$let": {
"vars": {
"d": "$$value",
"p": "$$this"
},
"in": {
"$mergeObjects": [
"$$d",
"$$p"
]
}
}
}
}
},
"weight": {
"$reduce": {
"input": {
"$filter": {
"input": {
"$map": {
"input": "$weight",
"as": "w",
"in": {
"$cond": [
{
"$ne": [
"$$w.weight",
null
]
},
{
"$let": {
"vars": {
"o": [
[
"$$w.weight",
"$$w.sum"
]
]
},
"in": {
"$arrayToObject": "$$o"
}
}
},
null
]
}
}
},
"as": "f",
"cond": {
"$ne": [
"$$f",
null
]
}
}
},
"initialValue": {},
"in": {
"$let": {
"vars": {
"d": "$$value",
"p": "$$this"
},
"in": {
"$mergeObjects": [
"$$d",
"$$p"
]
}
}
}
}
}
}
},
{
"$project": {
"details": "$$ROOT"
}
}
])
Here I'm trying to get the flavour and weight with their count, with manually adding those fields in $filter stage. I want to do it without assuming those keys. So, even if there is 20 items present in array details it will map those items and shows me output with their counts respectively.
I hope you guys understand.
Playground:https://mongoplayground.net/p/j1mzgWvcmvd
You need to change the schema, the thing you want to do is easy, and both those queries are so complicated and slow, even the second that is much smaller has 2 $unwind and 3 $group with 3 $arrayToObject and 8 stages total because of the schema and the schema of the answer.
Don't store data in the keys of the documents, people that are new to MongoDB do those, i was doing it also, but it makes all things harder.(i can't say like never do it but you dont need it here)
Your schema should be something like
{
"_id": "2",
"title": "Pretzels",
"contain": "salt",
"details": [
{
"type" : "flavour",
"value" : "Wheat"
},
{
"type" : "weight",
"value" : "10KG"
},
{
"type" : "sugar",
"value" : "15KG"
}
]
}
See this example
Converts your schema, to the new schema and produce the results you
want but without data in keys (the first part you wouldnt need it you would need only the bellow query if you had that schema from start)
Query with the new Schema (no data in keys)
[{"$unwind": { "path": "$details"}},
{"$replaceRoot": {"newRoot": "$details"}},
{
"$group": {
"_id": {
"type": "$type",
"value": "$value"
},
"sum": {"$sum": 1}
}
},
{
"$replaceRoot": {
"newRoot": {"$mergeObjects": ["$_id","$$ROOT"]}
}
},
{"$project": {"_id": 0}},
{
"$group": {
"_id": "$type",
"values": {
"$push": {
"value": "$value",
"sum": "$sum"
}
}
}
},
{"$addFields": {"type": "$_id"}},
{"$project": {"_id": 0}}
]
MongoDB operators are not made to support for data in keys or dynamic keys(uknown keys) (to do it you do complicated things like the above)
If you want to change your schema, either do it with update in the database,
Or take the documents to the application and do it with javascript, and re-insert.
Even if you solve this question in the next one, you will have again problems.
I'm the guy from Mongodb Forum:
Try this out https://mongoplayground.net/p/tfyfpIkHilQ
I have an array like this
{
"activities": [
{
"activityId": "60f672b20f38640008a6c29b",
"_id": "610cb887b149cb00095e1eb6",
"trackingProgress": [
{
"_id": "610cb8ee541fea0009189c36",
"dateRecorded": "2021-07-16T00:00:00.000Z",
"items": [
{
"_id": "610cb8ee541fea0009189c37",
"feelingType": "Satisfied",
"times": 2
}
]
},
{
"_id": "611358300fea490008d56ea7",
"dateRecorded": "2021-08-10T00:00:00.000Z",
"items": [
{
"_id": "6113583b0fea490008d56eae",
"feelingType": "Neutral",
"times": 1
}
]
},
{
"_id": "611b6068fed9100009a4f689",
"dateRecorded": "2021-08-17T00:00:00.000Z",
"items": [
{
"_id": "611b6068fed9100009a4f68a",
"feelingType": "Satisfied",
"times": 4
}
]
}
],
"title": "Losing & Managing Weight",
"description": "Losing weight",
"order": null,
"daysOfWeek": [
"tuesday"
],
"reminderTimeAt": "09:00 am",
"type": "Custom",
"progressOverview": {
"notSatisfied": 0,
"neutral": 1,
"satisfied": 6
}
}
]
}
I use JSONPATH to get _id in items
$.activities.[*].trackingProgress.[*].items[*].[_id]
it return an array like this
[
"610cb8ee541fea0009189c37",
"6113583b0fea490008d56eae",
"611b6068fed9100009a4f68a"
]
But, i want to keep the "_id", something like :
[
"_id":"610cb8ee541fea0009189c37",
"_id":"6113583b0fea490008d56eae",
"_id":"611b6068fed9100009a4f68a"
]
How can i do that?? Thank you a lots, i search internet but i found nothing
If you use map, you can get an array of items that each have an _id and value.
let result = [
"610cb8ee541fea0009189c37",
"6113583b0fea490008d56eae",
"611b6068fed9100009a4f68a"
].map(i => ({"_id": i}))
Results in:
[{ _id: "610cb8ee541fea0009189c37" },
{ _id: "6113583b0fea490008d56eae" },
{ _id: "611b6068fed9100009a4f68a" }]
In my collection I have a category array as below.
I receive another array to my API like below
array = ['Chess','Rugby'];
I want to add a condition to my database query such that catName field from category objects exists in array.
currently I'm using the below code to get the results:
postSchemaModel.aggregate([{
"$geoNear": {
"near": { "type": "Point", "coordinates": [parseFloat(long), parseFloat(lat), ] },
"distanceField": "dist.calculated",
"maxDistance": parseInt(maxDistance),
"includeLocs": "dist.location",
"spherical": true
}
},
{ "$match": { "$or": [{ "typology": "post" }, { "typology": "chat_group" }] } },
{
"$match": {
"createdAt": {
"$gte": '2020-07-15 23:54:38.673665',
"$lt": '2020-06-15 23:54:38.673665'
}
}
},
{ "$limit": limit },
{ "$skip": startIndex },
{ "$sort": { "createdAt": -1 } },
{
"$lookup": {
"from": userSchemaModel.collection.name,
"localField": "user_id",
"foreignField": "_id",
"as": "user_id"
}
},
{
"$project": {
"post_data": 1,
"likes": 1,
"commentsCount": 1,
"post_img": 1,
"isUserLiked": 1,
"usersLiked": 1,
'exp_date': 1,
"has_img": 1,
"user_id": {
"img": "$user_id.img",
"_id": "$user_id._id",
"user_name": "$user_id.user_name",
"bday": "$user_id.bday",
"imagesource": "$user_id.imagesource",
"fb_url": "$user_id.fb_url",
},
"typology": 1,
"geometry": 1,
"category": 1,
"created": 1,
"createdAt": 1,
"updatedAt": 1,
}
},
]).then(async function(posts) {
//some code here
}
});
UPDATE : Sample Output
{
"_id": "5f0bd1b7d6ed4f0017e5177c",
"post_data": "bitch boy sudesh",
"likes": 2,
"commentsCount": 1,
"post_img": null,
"isUserLiked": true,
"usersLiked": [
"5f0bfa296ee76f0017f13787",
"5ef60bba10e9090017e2c935"
],
"exp_date": "2020-07-16T00:00:00.000Z",
"has_img": false,
"user_id": [
{
"img": [
"default-user-profile-image.png"
],
"_id": [
"5ef9a7a2922eba0017ce47e0"
],
"user_name": [
"Sudesh"
],
"bday": [
"1997-05-02T00:00:00.000Z"
],
"imagesource": [
"fb"
],
"fb_url": [
"https://platform-lookaside.fbsbx.com/platform/profilepic/?asid=1846836948784193&width=400&ext=1596011605&hash=AeRsB0QJQH7edpRT"
]
}
],
"typology": "post",
"geometry": {
"pintype": "Point",
"_id": "5f0bd1b7d6ed4f0017e5177d",
"coordinates": [
79.9200017,
6.7088167
]
},
"category": [
{
"_id": "5f0bd1b7d6ed4f0017e5177e",
"catID": "5eef80cc5de48230887f3aa8",
"catName": "Chess"
},
{
"_id": "5f0bd1b7d6ed4f0017e5177e",
"catID": "5eef80cc5de48230887f3aa8",
"catName": "Rugby"
}
],
"created": 1594610103626,
"createdAt": "2020-07-13T03:15:03.629Z",
"updatedAt": "2020-07-18T14:02:35.080Z"
}
You can use some method if you only want to get true/false result:
category.some(element => array.includes(element.catName))
If you want to get an array of all the category objects with cat names that also exist in the array then you can filter method:
category.filter(element => array.includes(element.catName))
If you have an object called array in your code and you want to find at array of categories where cat names are in the array then you can add the condition to your $match stage:
{ "$match": { "$or": [{ "typology": "post" }, { "typology": "chat_group" }] }, "category.catName": { $in: array } }
Using another $match with "$elemMatch" solved the problem
"$match": {
"category": { "$elemMatch": { "catName": "Rugby", "catName": "Carrom" } },
}
I need to add a value present in the "value" property of each document that createdAt is the current day, that the document has a "finished" property as true, and that the document has a "value" and "period.check_out" property.
So far, I have been able to select all documents that have the "period.check_out" property and sum the values. The problem is selecting the documents of the current day. I've tried to sort by day in many ways, but I'm not getting.
I enabled nas database models, so whenever I create a new document, it automatically adds me a createdAt, updatedAt and _v.
document example:
[
{
"_id": "1",
"client": {
"name": "raul germano"
},
"parking": {
"_id": "5d8bfe395e0a822b143fd68d",
"value": 2.59,
"name": "pro parking oficial"
},
"finished": false,
"createdAt": "2019-09-25T23:54:32.802Z",
"updatedAt": "2019-09-25T23:54:32.802Z"
},
{
"_id": "2",
"client": {
"name": "raul vitor"
},
"parking": {
"_id": "5d8bfe395e0a822b143fd68d",
"value": 2.32,
"name": "pro parking oficial"
},
"period": {
"check_in": {
"user": "caboco",
"moment": "2019-09-25T00:05:36.273Z"
},
"check_out": {
"user": "outro caboco",
"moment": "2019-09-25T00:09:56.506Z"
}
},
"hours": 0.07,
"value": 0.17,
"finished": true,
"createdAt": "2019-09-26T23:54:33.463Z",
"updatedAt": "2019-09-26T23:54:33.463Z"
},
{
"_id": "3",
"client": {
"name": "raul germano"
},
"parking": {
"_id": "5d8bfe395e0a822b143fd68d",
"value": 2.60,
"name": "pro parking oficial"
},
"finished": true,
"createdAt": "2019-09-25T23:54:33.463Z",
"updatedAt": "2019-09-25T23:54:33.463Z",
"period": {
"check_in": {
"user": "caboco",
"moment": "2019-09-25T00:05:36.273Z"
},
"check_out": {
"user": "outro caboco",
"moment": "2019-09-25T00:09:56.506Z"
}
},
"hours": 0.09,
"value": 0.23
}
]
Current query:
collection.aggregate([
{
$match: {
'period.check_out': {
$exists: true
},
createdAt: {
$gte: new Date("2019-09-24")
$lt: new Date("2019-09-25")
}
}
},
{
$unwind: '$parking'
},
{
$match: {
'parking._id': Types.ObjectId(parking_id)
}
},
{
$group: {
_id: Types.ObjectId(parking_id),
total: {
$sum: '$value'
}
}
}
])
That way he selects the last 24 hours
Based on the documents presented above, I need only the _id "1" and "2" documents to be selected. Thus resulting
Try replacing your $match with the following considering '2019-09-26' is the current date:
{
$match: {
'period.check_out': {
$exists: true
},
createdAt: {
$gte: new Date('2019-09-26'),
$lte: new Date(),
}
}
},
What you are matching is: $gte: 2019-09-24T00:00:00.000Z, $lte: 2019-09-25T00:00:00.000Z. And I don't think that would be the current date.
Instead you need to match is '2019-09-24T00:00:00.000Z' - current time.