Finding multiple docs using same id not working, using meteor + react and mongoDB - mongodb

How do I get the email address of the students in the same class_id, take it as there are more then 2 students in different class in the DB as well?
I have this but it return empty array []
Meteor.users.find({"course_learn_list.$.class_id": {$in: [classId]}},
{field: {"emails.address": 1}}
).fetch()
Collections
{
"_id": "LMZiLKs2MRhZiiwoS",
"course_learn_list": [
{
"course_id": "M8EiKfxAAzy25WmFH",
"class_id": "jePhNgEuXLM3ZCt98"
},
{
"course_id": "5hbwrfbfxAAzy2nrg",
"class_id": "dfbfnEuXLM3fngndn"
}
],
"emails": [
{
"address": "student1#gmail.com",
"verified": false
}
]
},
{
"_id": "JgfdLKs2MRhZJgfNgk",
"course_learn_list": [
{
"course_id": "M8EiKfxAAzy25WmFH",
"class_id": "jePhNgEuXLM3ZCt98"
},
{
"course_id": "5hbwrfbfxAAzy2nrg",
"class_id": "dfbfnEuXLM3fngndn"
}
],
"emails": [
{
"address": "student2#gmail.com",
"verified": false
}
]
}

I think you want:
Meteor.users.find({ "course_learn_list.class_id": classId },
{ "course_learn_list.$": 1, "emails.address": 1 }).fetch()
This should find the first instance in each course_learn_list array where the classId is your classId.

In this case you probably don't need to use a projection to get the right answer. Here's an example of extracting the verified email addresses using only the . operator in the selector:
const ids = ['jePhNgEuXLM3ZCt98', 'some-other-id'];
const emails =
Meteor.users.find({ 'course_learn_list.class_id': { $in: ids } })
.fetch()
.map(user => _.findWhere(user.emails, { verified: true }).address);

This works for me!
Meteor.publish("getMyClassStudents", function(classId) {
console.log("Publish getMyClassStudents")
var self = this
if (self.userId) {
var data = Meteor.users.find({
"course_learn_list.class_id": classId
}, {
"fields": {
"emails.address": 1
}
})
return data
}
else {
return self.ready()
}
})

Related

Search MongoDB autocomplete filtered

I want to search autocomplete on the following fields :contactfirstname, contactlastname and name
Also, want to filter based on userid(s) first then perform autocomplete search
Issue:
Without filter criteria, autocomplete is working fine
With filter criteria in compound query not working as getting empty array
Can anyone help please?
exports.userNameCitySearchAutocomplete = async function (req, res) {
try {
const { userNameCityQueryparam } = req.query;
console.log("search query param", userNameCityQueryparam);
const agg = [
{
$search: {
index: 'userNameCity',
'compound': {
"filter": [{
"text": {
"query": ["6271f2bb79cd80194c81f631"],
"path": "_id",
}
}],
"should": [
{
//search on user name
autocomplete: {
query: userNameCityQueryparam,
path: 'name',
fuzzy: {
maxEdits: 2,
prefixLength: 3
}
}},
//search on user city
{
autocomplete: {
query: userNameCityQueryparam,
path: 'city',
fuzzy: {
maxEdits: 2,
prefixLength: 3
}
},
}
,
//search on user contact first name
{
autocomplete: {
query: userNameCityQueryparam,
path: 'contactfirstname',
fuzzy: {
maxEdits: 2,
prefixLength: 3
}
},
}
,
//search on user contact last name
{
autocomplete: {
query: userNameCityQueryparam,
path: 'contactlastname',
fuzzy: {
maxEdits: 2,
prefixLength: 3
}
},
}
],
"minimumShouldMatch": 1
}
}
}
]
const response = await User.aggregate(agg);
return res.json(response);
// res.send(response);
} catch (error) {
console.log("autocomplete search error", error);
return res.json([]);
}
};
Index details in mongodb:
{
"mappings": {
"dynamic": false,
"fields": {
"_id": {
"type": "string"
},
"city": {
"type": "autocomplete"
},
"contactfirstname": {
"type": "autocomplete"
},
"contactlastname": {
"type": "autocomplete"
},
"name": {
"type": "autocomplete"
}
}
}
}
Image of collection in mongodb
image of empty array
for anyone looking for solution to this,
You can use the MQL where clause in your pipeline stage definition.
{
$match: {
email:"email#domain.com"
}
}
check here for an example

Updating a nested array object in Mongo

I have a StudentClass collection that looks like this:
[
{
"_id": ObjectId("60923c997b4d3205009a981a"),
"attended": [
{
"in": "2021-05-05T06:35:05.226+00:00",
"out": "2021-05-05T06:45:05.226+00:00"
},
{
"in": "2021-05-05T06:47:05.226+00:00",
"out": "2021-05-05T06:55:05.226+00:00"
},
{
"in": "2021-05-05T06:56:05.226+00:00"
},
{
"in": "2021-03-03T07:10:00.628Z"
}
],
"studentId": ObjectId("608a42e8224c549ad9a9ab51"),
"active": true
},
{
"_id": ObjectId("6098f6f974af29682772fbe6"),
"attended": [
{
"in": "2021-05-05T06:35:05.226+00:00",
"out": "2021-05-05T06:55:05.226+00:00"
},
{
"in": "2021-05-05T06:59:05.226+00:00",
"out": "2021-05-05T07:20:05.226+00:00"
}
],
"studentId": ObjectId("608a42e8224c549ad9a9ab51"),
"active": true
},
{
"_id": ObjectId("6098f6f974af29682772fbe7"),
"attended": [],
"studentId": ObjectId("608a42e8224c549ad9a9ab51"),
"active": true
}]
For a give _id, I need to update the attended array as per the following conditions:
Need to find the last array element with a missing out key. That array element of the attended should get updated with the out key just like other array elements.
This is what I've tried:
const markStudentExitFromClass = (studentClassId, exitTime) => {
return new Promise((resolve, reject) => {
StudentClasses.updateOne(
{ _id: new mongoose.Types.ObjectId(studentClassId), "attended.out" : {$exists: false}},
{ $set: { "attended.$.out": new Date(exitTime) } },
function (err, updatedData) {
if (err) {
reject(err);
} else {
resolve(updatedData);
}
}
)
})
}
This is not updating any array element of attended array for the give _id. Just like $ positional operator finds the first array element, is there anything for the last array element?
What am I doing wrong?
Updated:
So, I got the update part working by changing the match clause of updateOne to:
const markStudentExitFromClass = (studentClassId, exitTime) => {
return new Promise((resolve, reject) => {
StudentClasses.updateOne(
{ _id: new mongoose.Types.ObjectId(studentClassId), "attended" :{"$elemMatch":{"out":{$exists: false}}} },
{ $set: { "attended.$.out": new Date(exitTime) } },
function (err, updatedData) {
if (err) {
reject(err);
} else {
resolve(updatedData);
}
}
)
})
}
But, I still cant figure out how to get the last matching element of the attended array instead of the first. Any pointers?

Mongo: Multiple $inc doesn't work in Mongoose

I'm trying to make an $inc to 2 fields, points and sports.wins:
When I try to make it through the mongo shell, it works:
db.getCollection('userStats').update({
uid: ObjectId("5ed1bd8313955cbfc60df96f"),
"sports._id": ObjectId("5ed533c44dcb3efcfe8cb0ec")
},
{
"$inc": { "sports.$[sports].wins" : 1, "points": 10 },
},
{
"arrayFilters": [
{ "sports._id": ObjectId("5ed533c44dcb3efcfe8cb0ec") }
]
}
);
However, when I try to make it using bulkWrite (via Mongoose), it only updates the wins field:
let bulkArray = [];
bulkArray.push({
updateOne: {
filter: {
uid: usersWon[i].uid,
"sports._id": eventData.reference.sport
},
update: {
"$inc": {
"points": 10,
"sports.$[sport].wins": 1,
}
},
arrayFilters: [
{
"sport._id": sportId
}
]
}
});
await UserStats.bulkWrite(bulkArray);
What am I doing wrong? thanks!

Change capital letters in mongo to camel casing?

I have a collection named User, which contains the the fields firstName and secondName. But the data is in capital letters.
{
firstName: 'FIDO',
secondName: 'JOHN',
...
}
I wanted to know whether it is possible to make the field to camel case.
{
firstName: 'Fido',
secondName: 'John',
...
}
You can use a helper function to get your desired answer.
function titleCase(str) {
return str.toLowerCase().split(' ').map(function(word) {
return word.replace(word[0], word[0].toUpperCase());
}).join(' ');
}
db.User.find().forEach(function(doc){
db.User.update(
{ "_id": doc._id },
{ "$set": { "firstName": titleCase(doc.firstName) } }
);
});
Run an update operation with aggregate pipeline as follows:
const titleCase = key => ({
$concat: [
{ $toUpper: { $substrCP: [`$${key}`,0,1] } },
{ $toLower: {
$substrCP: [
`$${key}`,
1,
{ $subtract: [ { $strLenCP: `$${key}` }, 1 ] }
]
} }
]
});
db.User.updateMany(
{},
[
{ $set: {
firstName: titleCase('firstName'),
secondName: titleCase('secondName')
} }
]
)
Mongo Playground

Meteor Mongo add subdocument

I have a collection documents MasterPropinsis like this :
{
"_id": "4HSb7bbjFBzRSftXu",
"nama": "Yogyakarta",
"kabupaten": [
{
"id": "KulonProgo",
"nama": "Kulon Progo",
"kecamatan": [{ "nama": "Kalibawang" },{ "nama": "Nanggulan" }]
},
{
"id": "Sleman",
"nama": "Sleman",
"kecamatan": [{ "nama": "Depok" },{ "nama": "Berbah" }]
},
{
"id": "Bantul",
"nama": "Bantul",
"kecamatan": []
}
]
}
At kabupaten:Bantul, I want to Add subdocument kecamantan:XXX, with this code :
Masterpropinsis.update(
{
_id: Session.get('idKabupaten').toString(),
'kabupaten.id': Session.get('idKecamatan').replace(" ", "")
},
{
$addToSet: {
'kabupaten.kecamatan': {
nama: nama,
createdAt: new Date(),
createBy: CreateBy,
createByID: CreateByid
}
}
},
{
validate: true
});
But I get this error:
Uncaught Error: Not permitted. Untrusted code may only update documents by ID. [403]
Here is an example try this
var docId = Session.get('idKabupaten'); // usally toString is not nesserry unless you do something unusual
Masterpropinsis.update({_id: docId }, {
$push: {
'kabupaten.kecamatan': {
nama: nama,
createdAt: new Date(),
createBy: CreateBy,
createByID: CreateByid
}
}
}); // validate true also is the default unless again you do something unusual
you can see we only supply {_id: docId} in the selector the error says in client you can only update by the ID in the server you can do any selector. Also hopefully you can have Allow/Deny rules to allow updates from the client? http://docs.meteor.com/#/full/allow