validate if documents have same data as I send - mongodb

I've the following documents in database: (each document is a room with members)
[
{
"id": "room1",
"members": [{
"id": "member1",
"acks": "XXX",
},{
"id": "member2",
"acks": "XXX",
},{
"id": "member3",
"acks": "XXX",
},{
"id": "member4",
"acks": "XXX",
}]
},
{
"id": "room2",
"members": [{
"id": "memberA",
"acks": "XXX",
},{
"id": "memberB",
"acks": "XXX",
},{
"id": "memberC",
"acks": "XXX",
},{
"id": "memberD",
"acks": "XXX",
},{
"id": "memberE",
"acks": "XXX",
},{
"id": "memberF",
"acks": "XXX",
}]
},
{
"id": "room3",
"members": [{
"id": "memberX",
"acks": "XXX",
},{
"id": "memberY",
"acks": "XXX",
},{
"id": "memberZ",
"acks": "XXX",
}]
}
]
Sending from frontend next data:
[{
"id": "room1"
"members": ["member1", "member2"]
},{
"id": "room2"
"members": ["memberA", "memberB", "memberC", "memberF"]
},{
"id": "room3"
"members": ["memberX", "memberY"]
}]
I want to check if all members match for each room and return an unique boolean as a response --> True if all match and False if is someone that doesn't match.
This data should return false:
[{
"id": "room1"
"members": ["memberA", "member2"] //Because memberA is not in room1
},{
"id": "room2"
"members": ["memberA", "memberB", "memberC", "memberF"]
},{
"id": "room3"
"members": ["memberX", "memberY"]
}]
Playground
Is it possible to achieve it with one query?
Thank you very much in advance.

You can try a query,
Prepare your query request from frontend request
// consider this is your request parameter
let request = [{
"id": "room1",
"members": ["member1", "member2"]
},{
"id": "room2",
"members": ["memberA", "memberB", "memberC", "memberF"]
},{
"id": "room3",
"members": ["memberX", "memberY"]
}];
// prepare query
let query = request.map((r) => {
return {
id: r.id,
members: r.members.map((m) => {
return { "members.id": m }
})
}
});
console.log(query)
Try countDocuments function and get total count of rooms with request
async function hasRooms (query) {
let count = await Schema.countDocuments({ $or: query });
return (count === request.length);
}
Playground
Use your function
console.log(await hasRooms(query));

Related

Having issues matching local and foreign fields in a $lookup pipeline for a MongoDB aggregate query

I have a two different collections, one for emailtemplates, and one for emails. And what I'm trying to do is write an aggregate pipeline that will show how many times each template has been sent using the templates name from the emailtemplates collection. I know I could just execute a pipeline on the emails collection and count each one by the template name, but that will exclude templates that have never been sent.
Here are some example documents I worked up...
// Example emailtemplate documents:
[
{
"name": "WELCOME-01",
"channel": "email",
"status":"active",
"title": "Hello, welcome {firstname} {lastnane}",
"parameters":[{
"name": "firstname",
"type": "string",
"required": true
},{
"name": "lastname",
"type": "string",
"required": true
}],
"body":"Dear {firstname}, Welcome to the club"
},
{
"name": "GOODBYE-01",
"channel": "email",
"status":"active",
"title": "Hello, welcome {firstname} {lastnane}",
"parameters":[{
"name": "firstname",
"type": "string",
"required": true
},{
"name": "lastname",
"type": "string",
"required": true
}],
"body":"Dear {firstname}, were sorry to see you go"
},
{
"name": "YOURE-FIRED-01",
"channel": "email",
"status":"active",
"title": "Hello, welcome {firstname} {lastnane}",
"parameters":[{
"name": "firstname",
"type": "string",
"required": true
},{
"name": "lastname",
"type": "string",
"required": true
}],
"body":"Dear {firstname}, Pack your stuff and go"
}
]
// Example email documents:
[
{
"templateName": "WELCOME-01",
"recipient": "john.doe#gmail.com",
"parameters": {
"firstName": "John",
"lastName": "Doe"
}
},
{
"templateName": "WELCOME-01",
"recipient": "david.chappelle#gmail.com",
"parameters": {
"firstName": "David",
"lastName": "Chappelle"
},
},
{
"templateName": "GOODBYE-01",
"recipient": "the.joker#gmail.com",
"parameters": {
"firstName": "The",
"lastName": "Joker"
}
}
]
So you can see how each email document has the templateName value which matches with the name from each emailtemplates document. And what I'm trying to do is select from the templateName collection and show how many emails documents are associated to it.
I know how to do it using the localField and foreignFIeld options:
db.notificationtemplates.aggregate([
{
$lookup:{
from: "notifications",
localField: "name",
foreignField: "templateName",
as: "notifications"
}
},
{
$project:{
_id: 0,
templateName: "$name",
amountSent: { $size: "$notifications"}
}
}
]);
Which gives me the results:
[
{ templateName: 'WELCOME-01', amountSent: 2 },
{ templateName: 'GOODBYE-01', amountSent: 1 },
{ templateName: 'YOURE-FIRED-01', amountSent: 0 }
}
And that works just fine, but I need to add some logic to the $lookup, which means I need a $pipeline in there, which means I can't simply use the localField and foreignField. Otherwise I get the error:
MongoServerError: $lookup with 'pipeline' may not specify 'localField' or 'foreignField'
Here's the query I've written thus far to try to do the same thing:
db.emailtemplates.aggregate([
{ $match:{channel: 'email'} },
{
$lookup: {
from: "emails",
let: {
templateName: "$templateName",
name: "$name"
},
pipeline: [
{ $match: { $expr: {$eq: [ "$$templateName","$name"] } } },
{ $project:{
"templateName":"$templateName",
"name":"$name"
} }
],
as: "emails"
}
}
])
Here are the results of the query above:
[
{
"name": "WELCOME-01",
"channel": "email",
"status":"active",
"title": "Hello, welcome {firstname} {lastnane}",
"parameters":[{
"name": "firstname",
"type": "string",
"required": true
},{
"name": "lastname",
"type": "string",
"required": true
}],
"body":"Dear {firstname}, Welcome to the club",
"emails":[
{
"templateName": "WELCOME-01"
},
{
"templateName": "WELCOME-01",
},
{
"templateName": "GOODBYE-01"
}
]
},
{
"name": "GOODBYE-01",
"channel": "email",
"status":"active",
"title": "Hello, welcome {firstname} {lastnane}",
"parameters":[{
"name": "firstname",
"type": "string",
"required": true
},{
"name": "lastname",
"type": "string",
"required": true
}],
"body":"Dear {firstname}, were sorry to see you go",
"emails":[
{
"templateName": "WELCOME-01"
},
{
"templateName": "WELCOME-01",
},
{
"templateName": "GOODBYE-01"
}
]
},
{
"name": "YOURE-FIRED-01",
"channel": "email",
"status":"active",
"title": "Hello, welcome {firstname} {lastnane}",
"parameters":[{
"name": "firstname",
"type": "string",
"required": true
},{
"name": "lastname",
"type": "string",
"required": true
}],
"body":"Dear {firstname}, Pack your stuff and go",
"emails":[
{
"templateName": "WELCOME-01"
},
{
"templateName": "WELCOME-01",
},
{
"templateName": "GOODBYE-01"
}
]
}
]
Note: I'm only outputting the templateName now so I can see what documents get matched for the emails value.
If you look at the emails value for each document that's output, it doesn't at all only look for the emails with the templateName matching the local name value of the emailtemplate documents.
The output I'm expecting to see would be something more along the line of:
[
{
"name": "WELCOME-01",
"channel": "email",
"status":"active",
"title": "Hello, welcome {firstname} {lastnane}",
"parameters":[{
"name": "firstname",
"type": "string",
"required": true
},{
"name": "lastname",
"type": "string",
"required": true
}],
"body":"Dear {firstname}, Welcome to the club",
"emails":[
{
"templateName": "WELCOME-01"
},
{
"templateName": "WELCOME-01"
}
}
]
},
{
"name": "GOODBYE-01",
"channel": "email",
"status":"active",
"title": "Hello, welcome {firstname} {lastnane}",
"parameters":[{
"name": "firstname",
"type": "string",
"required": true
},{
"name": "lastname",
"type": "string",
"required": true
}],
"body":"Dear {firstname}, were sorry to see you go",
"emails":[
{
"templateName": "GOODBYE-01"
}
]
},
{
"name": "YOURE-FIRED-01",
"channel": "email",
"status":"active",
"title": "Hello, welcome {firstname} {lastnane}",
"parameters":[{
"name": "firstname",
"type": "string",
"required": true
},{
"name": "lastname",
"type": "string",
"required": true
}],
"body":"Dear {firstname}, Pack your stuff and go",
"emails":[]
}
]
You are very close actually. You just mixed up the variables and the field names. Note that $$ indicates variable in MongoDB aggregation pipeline. In your $let clause, you are using the value from emailtemplates.name to create variable name. So for the $lookup sub-pipeline you should compare $$name with $templateName, which refers to emails.templateName as you are looking up the emails collection.
The correct syntax should be like this:
db.emailtemplates.aggregate([
{
$match: {
channel: "email"
}
},
{
$lookup: {
from: "emails",
let: {
templateName: "$templateName",
name: "$name"
},
pipeline: [
{
$match: {
$expr: {
$eq: [
"$templateName",
"$$name"
]
}
}
},
{
$project: {
"templateName": "$templateName",
"name": "$name"
}
}
],
as: "emails"
}
}
])
Mongo Playground

Nested lookup in mongodb

{
"_id": "445454",
"students": ["7894556", "5454454"]
}
This is my actual data. After first lookup I got a result like:
{
"_id": "445454",
"students": [{
"name": "Jose",
"parent": "45566622"
},{
"name": "Mathew",
"parent": "7889665"
}]
}
but actual answer I required is something like this
{
"_id": "445454",
"students": [{
"name": "Jose",
"parent": {
"_id": "45566622",
"name": "Cristy"
}
}, {
"name": "Mathew",
"parent": {
"_id": "7889665",
"name": "Abraham"
}
}]
}
somebody please help me. I not expert in mongodb.

MongoDB Aggregation Error Returning wrong result

I have my json object like this
{
"_id": "5c2e811154855c0012308f00",
"__pclass": "QXRzXFByb2plY3RcTW9kZWxcUHJvamVjdA==",
"id": 44328,
"name": "Test project via postman2//2",
"address": "some random address",
"area": null,
"bidDate": null,
"building": {
"name": "Health Care Facilities",
"type": "Dental Clinic"
},
"collaborators": [],
"createdBy": {
"user": {
"id": 7662036,
"name": "Someone Here"
},
"firm": {
"id": 2520967,
"type": "ATS"
}
},
"createdDate": "2019-01-03T21:39:29Z",
"customers": [],
"doneBy": null,
"file": null,
"firm": {
"id": 1,
"name": "MyFirm"
},
"leadSource": {
"name": "dontknow",
"number": "93794497"
},
"location": {
"id": null,
"city": {
"id": 567,
"name": "Bahamas"
},
"country": {
"id": 38,
"name": "Canada"
},
"province": {
"id": 7,
"name": "British Columbia"
}
},
"modifiedBy": null,
"modifiedDate": null,
"projectPhase": {
"id": 1,
"name": "pre-design"
},
"quotes": [{
"id": 19,
"opportunityValues": {
"Key1": 100,
"Key2 Key2": 100,
"Key3 Key3 Key3": 200,
}
}],
"specForecast": [],
"specIds": [],
"tags": [],
"valuation": "something"
}
I am trying to aggregate using this query in MongoDB. My aggregation key is 4 level deep and also contains spaces. On all online examples shows me the aggregation at the first level. Looking to the online codes, I tried to re-iterate the same with my 4th level deep key.
db.mydata.aggregate([
{$match: {"id": 44328 } } ,
{$group: { _id: "$quotes.id",
totalKey2:{ $sum: "$quotes.opportunityValues.Key2 Key2"},
totalKey3:{ $sum: "$quotes.opportunityValues.Key3 Key3 Key3"}
}
}
]);
This should return
_id totalKey2 totalKey3
0 19 100 300
But it is returning
_id totalKey2 totalKey3
0 19 0 0
What am I doing Wrong?
Although it's not recommended to use space in field names in Mongo, it works as expected.
The problem with your query is that "quotes" is an array and you should first unwind it before grouping it.
This works as expected:
db.mydata.aggregate([
{ $match: { "id": 44328 } } ,
{ $unwind: "$quotes" },
{ $group: { _id: "$quotes.id",
totalKey2:{ $sum: "$quotes.opportunityValues.Key2 Key2" },
totalKey3:{ $sum: "$quotes.opportunityValues.Key3 Key3 Key3" } }
}
]);

Mongo Aggs and total count

I experiencing some trouble in mongodb to do a query on collection to get all documents filtered, paginate(with skip and limit) and get the total count like that
The chat document look like that:
{
"_id": "c603db3e75ec76f5a4dff559bc525e29",
"members": [{
"user": "5b2b0d5135cfd60185034bf6",
"status": "creator"
}, {
"user": "5b31dc3f88284a009fa847e5",
"status": "member"
}],
"messages": [{
"_id": 2,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "fffasdfsa",
"attachment": null,
"time": 1529995684119,
"type": "text"
},
{...},
{...}
]}
And i would like to get result like that
{
total_count: 3,
chats: [{
"_id": "c603db3e75ec76f5a4dff559bc525e29",
"members": [{
"user": "5b2b0d5135cfd60185034bf6",
"status": "creator"
}, {
"user": "5b31dc3f88284a009fa847e5",
"status": "member"
}],
"creator": {
"user": "5b2b0d5135cfd60185034bf6",
"status": "creator"
},
"lastMessage": {
"_id": 4,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "aasdas",
"attachment": null,
"time": 1529995698736,
"type": "text"
},
"messages": [{
"_id": 2,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "fffasdfsa",
"attachment": null,
"time": 1529995684119,
"type": "text"
}, {
"_id": 3,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "weqw",
"attachment": null,
"time": 1529995690015,
"type": "text"
}, {
"_id": 4,
"from": "5b2b0d5135cfd60185034bf6",
"parse_mode": "none",
"text": "aasdas",
"attachment": null,
"time": 1529995698736,
"type": "text"
}]
},
{...},
{...}
]
}
My query is
collection<DbChat>('chats').aggregate([
{
$match: {
messages: { $exists: true }
}
},
{ $unwind: '$messages' },
{ $unwind: '$members' },
{ $group: {_id: '$_id', members: { $push: '$members' }, member: { $last: '$members' }, creator: { $first: '$members' }, messages: {$push: '$messages' } , lastMessage: {$last: '$messages'}}},
{ $sort: this.sort},
]}.skip(skip).limit(size)
with this query i dont get the total_count I tried lot of things but i didnt found a way to get the result and the count in the same query.
Thanks

Get the first document using $in with mongodb

how can get the first element using in in mongo ?
if i've a list like ['car', 'house', 'cat', dog'], and a collection which contains many documents these element, i'd like to find the first document which contain cat, and first which contains dog etc.
I've tried to use limit() but in fact it gives me only one document, which can be either car, or dog or cat etc.
is there a way to combine a limit with $in ?
Thanks
EDIT:
example of data i've:
{
"_id": {
"$oid": "51d53ace9e674607e837d62d"
},
"sensors": [{
"name": "os-hostname",
"value": "yahourt"
}, {
"name": "os-domain-name",
"value": ""
}, {
"name": "os-platform",
"value": "Win32NT"
}, {
"name": "os-fullname",
"value": "Microsoft Windows XP Professional"
}, {
"name": "os-version",
"value": "5.1.2600.131072"
}],
"type": "os",
"serial": "2_os_os-hostname_yahourt"
} {
"_id": {
"$oid": "51d53ace9e674607e837d62e"
},
"sensors": [{
"name": "cpu-id",
"value": "_Total"
}, {
"name": "cpu-usage",
"value": 37.2257042
}],
"type": "cpu",
"serial": "2_cpu_cpu-id_total"
} {
"_id": {
"$oid": "51d53ace9e674607e837d62f"
},
"sensors": [{
"name": "cpu-id",
"value": "0"
}, {
"name": "cpu-usage",
"value": 48.90282
}],
"type": "cpu",
"serial": "2_cpu_cpu-id_0"
} {
"_id": {
"$oid": "51d53ace9e674607e837d630"
},
"sensors": [{
"name": "cpu-id",
"value": "1"
}, {
"name": "cpu-usage",
"value": 25.54859
}],
"type": "cpu",
"serial": "2_cpu_cpu-id_1"
} {
"_id": {
"$oid": "51d53ace9e674607e837d631"
},
"sensors": [{
"name": "volume-name",
"value": "C:"
}, {
"name": "volume-label",
"value": ""
}, {
"name": "volume-total-size",
"value": "52427898880"
}, {
"name": "volume-total-free-space",
"value": "20305170432"
}, {
"name": "volume-percent-free-space",
"value": "38"
}, {
"name": "volume-reads-per-second",
"value": 0.0
}, {
"name": "volume-writes-per-second",
"value": 9.324152
}, {
"name": "volume-read-bytes-per-second",
"value": 0.0
}, {
"name": "volume-write-bytes-per-second",
"value": 194141.6
}, {
"name": "volume-queue-length",
"value": 0.0
}],
"type": "disk",
"serial": "2_disk_volume-name_c"
}
You cannot add a limit to $in but you could cheat by using the aggregation framework:
db.collection.aggregate([
{$match:{serial:{$in:[list_of_serials]}}},
{$sort:{_id:-1}},
{$group:{_id:'$serial',type:{$first:'$type'},sensors:{$first:'$sensors'},id:{$first:'$_id'}}}
]);
Would get a list of all first found of each type.
Edit
The update will get the last inserted according to the _id.