I am trying to pull out data of 2 types from my BSON document using mongoose.
The 2 data are;
Intention 1 - I want to get objects from the items array where tagNo == 2
Intention 2 -I want to get objects from the items array where tagNo == 2 and tagID == kLawOURVpz1IIjoQ2fhCvy7NM.
For intention 1, I tried;
db.find(
{ "fileID": "0pdn3jdndndj3msms, "items.tagNo": 2 },
{
"items": {
"$elemMatch": { "tagNo": 2 }
}
}
);
and I tired the query below for intention 2
db.find(
{ "fileID": "0pdn3jdndndj3msms, "items.tagNo": 2 },
{
"items": {
"$elemMatch": { "tagNo": 2, "tagID": "kLawOURVpz1IIjoQ2fhCvy7NM", }
}
}
);
I keep getting the entire object back.
My BSON document is;
{
"_id": "ID_GOES_HERE",
"fileID": "0pdn3jdndndj3msms",
"fileName": "Item List",
"items": [
{
"tagNo": 2,
"status": 0,
"tagID": "kLawOURVpz1IIjoQ2fhCvy7NM",
"tagUnit": 5
},
{
"tagNo": 2,
"status": 0,
"tagID": "kLawOURVpz1IIjoQ2fhCvy7NM",
"tagUnit": 8
},
{
"tagNo": 2,
"status": 0,
"tagID": "pOawOURVpz1IIjoQ2fhCvy7w3",
"tagUnit": 81
},
{
"tagNo": 4,
"status": 0,
"tagID": "kLawOURVpz1IIjoQ2fhCvy7NM",
"tagUnit": 904
},
{
"tagNo": 3,
"status": 0,
"tagID": "pOawOURVpz1IIjoQ2fhCvy7w3",
"tagUnit": 24
},
{
"tagNo": 2,
"status": 0,
"tagID": "pOawOURVpz1IIjoQ2fhCvy7w3",
"tagUnit": 35
},
{
"tagNo": 1,
"status": 0,
"tagID": "kLawOURVpz1IIjoQ2fhCvy7NM",
"tagUnit": 11
},
{
"tagNo": 2,
"status": 0,
"tagID": "kLawOURVpz1IIjoQ2fhCvy7NM",
"tagUnit": 30
}
]
}
You need aggregation framework
db.collection.aggregate([
{
$match: { //Match condition to get the matching arrays
"fileID": "0pdn3jdndndj3msms",
"items.tagNo": 2
}
},
{
$project: {
"items": {
$filter: { //Project only matching array elements
input: "$items",
as: "item",
cond: {
$eq: [
"$$item.tagNo",
2
]
}
}
}
}
}
])
Playground
db.collection.aggregate([
{
$match: {
"items": {
"$elemMatch": {
"tagNo": 2,
"tagID": "kLawOURVpz1IIjoQ2fhCvy7NM",
}
}
}
},
{
$project: {
"items": {
$filter: {
input: "$items",
as: "item",
cond: {
"$and": [
{
$eq: [
"$$item.tagNo",
2
]
},
{
$eq: [
"$$item.tagID",
"kLawOURVpz1IIjoQ2fhCvy7NM"
]
}
]
}
}
}
}
}
])
Playground
Related
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
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'm working on a project in React and I'm having trouble mapping the JSON object that gets from the API. I want to get a table like this:
Image
and the JSON object looks like this:
{
"userName": "user1",
"memberCommunity": [
{
"userName": "user5",
"community": [
{
"level": 1,
"members": 3
},
{
"level": 2,
"members": 3
},
{
"level": 3,
"members": 1
}
]
},
{
"userName": "user18",
"community": []
},
{
"userName": "user2",
"community": [
{
"level": 1,
"members": 3
},
{
"level": 2,
"members": 4
},
{
"level": 3,
"members": 3
},
{
"level": 4,
"members": 6
},
{
"level": 5,
"members": 2
},
{
"level": 6,
"members": 1
}
]
}
]
}
The table image was created exactly on the data that I gave. The problem here is the proper use of the Map function, thanks to which I will get records with three users and for each of them the appropriate value in the level field. I hope I won't mix it up and you know what's going on :) Please help.
You can accomplish this with 2 reduce functions to iterate over the nested arrays.
const data = {
userName: "user1",
memberCommunity: [
{
userName: "user5",
community: [
{
level: 1,
members: 3
},
{
level: 2,
members: 3
},
{
level: 3,
members: 1
}
]
},
{
userName: "user18",
community: []
},
{
userName: "user2",
community: [
{
level: 1,
members: 3
},
{
level: 2,
members: 4
},
{
level: 3,
members: 3
},
{
level: 4,
members: 6
},
{
level: 5,
members: 2
},
{
level: 6,
members: 1
}
]
}
]
};
const format = data.memberCommunity.reduce((acc, curr) => {
return {
...acc,
[curr.userName]: curr.community.reduce((acc2, curr2) => {
return {
...acc2,
[`level ${curr2.level}`]: curr2.members
}
}, {}),
};
}, {});
console.log(format);
I have a query with find and aggregate that find in Schedule model, and calculate the sum of total of services and the sum of total value of services. But I need to make this sum (sum of totalServices) with a condition, where the ´status´ equal to 2. can i make this?
My query:
Schedule.find(findTerm)
.skip(req.body.page * req.body.limit)
.limit(Number(req.body.limit))
.select(
"service.name value scheduleStart scheduleEnd comissionValue status paymentMethod"
)
.exec((err, response) => {
if (err) res.status(500).send(err);
Schedule.find(findTerm)
.count()
.exec((error, count) => {
if (error)
res.status(500).send({
error,
code: 0,
message: "Erro."
});
Schedule.aggregate([{
$match: {
store: req.body.store,
}
},
{
$group: {
_id: {
id: "$store"
},
totalValue: {
$sum: "$value"
},
totalServices: {
$sum: {
$cond: [ {
$eq: [ "$status", 2 ]
}]
}
},
count: {
$sum: 1
}
}
}...
Result of my query:
...{
"service": {
"name": "CABELO + BARBA"
},
"comissionValue": 0,
"paymentMethod": 0,
"_id": "5bfec336c6f00d2e88f8d765",
"scheduleStart": "2018-11-28 14:35",
"scheduleEnd": "2018-11-28 15:45",
"status": 2,
"value": 75
},
{
"service": {
"name": "Barba"
},
"comissionValue": 0,
"paymentMethod": 0,
"_id": "5bfec3ffc6f00d2e88f8d766",
"scheduleStart": "2018-11-28 18:30",
"scheduleEnd": "2018-11-28 18:50",
"status": 2,
"value": 20
}
],
"count": 4299,
"group": [
{
"_id": {
"id": "5b16cceb56a44e2f6cd0324b"
},
"totalValue": 777780048281, //right value
"totalServices": 945, //wrong value
"count": 676
}
]
}
I need to filter the sum of totalServices to only objects if the status equal to 2 (I tried to use $cond but not worked).
You need to check the status conditionally($cond) i.e. if status is equal ($eq) to 2 then $sum the value field else pass 0
Schedule.aggregate([
{ "$match": { "store": req.body.store }},
{ "$group": {
"_id": { "id": "$store" },
"totalValue": { "$sum": "$value" },
"totalServices": {
"$sum": { "$cond": [{ "$eq": ["$status", 2] }, 1, 0] }
},
"count": { "$sum": 1 }
}}
])
I have a collection in mongodb and indexed on the field name , i do a free search using the following query to get matched results and limit to 5,
db.getCollection('_event').aggregate([
{
"$match": {
"$and": [
{
"$text": {
"$search": "liver"
}
},
{},
{}
]
}
},
{
"$group": {
"_id": null,
"count": {
"$sum": 1
},
"results": {
"$push": "$$ROOT"
}
}
},
{
"$project": {
"count": 1,
"results": {
"$slice": [
"$results",
5
]
}
}
}
])
but there is a data with liverpool . when i do replace it with "$search": "liverpool" it returns data.
what is the issue here?