Update multiple documents in MongoDB by altering object in array - javascript

I have a simple application with registration/login and it is basically a coursera/udemy type, where the app lists specific courses and users can like them or enroll in them. I have been trying to make a mongodb function that updates a user in the database and since users can like the courses it has to update all courses too (courses have a field "usersLiked", which is an array and keep all user documents which have liked it).
The course structure is the following:
{
"_id" : ObjectId("5977662564aac9f6c8d48884"),
"title" : "Title",
"lecturer" : "Lecturer",
"length" : 30,
"coverPhoto" : "Photo",
"usersLiked": [
{
"_id" : ObjectId("597763e346a7a463cbb8f529"),
"fullname" : "Name",
"username" : "Username",
"password" : "Hash",
"city" : "City",
"street" : "Street",
"website" : "Website"
}
],
"lectures" : [
{
"title" : "Introduction",
"number" : 1,
"url" : "someURL"
}
]
}
And the user structure:
{
"_id" : ObjectId("597763e346a7a463cbb8f529"),
"fullname" : "Name",
"username" : "Username",
"password" : "Hash",
"enrolledCourses" : [
...
]
}
],
"city" : "City",
"street" : "Street",
"website" : "Website"
}
So now I am calling this function when I want to change. It changes the userCollection but in the courseCollection it does nothing, while it should get all courses and if some of them have an object with username(the user's username) in the "usersLiked" array it should modify the user there too.
const updateUser = (username, details) => {
usersCollection
.update({
username: username,
}, {
$set: {
fullname: details.fullname,
city: details.city,
street: details.street,
website: details.website,
},
});
coursesCollection
.updateMany(
{
usersLiked: {
$elemMatch: {
username: username,
},
},
},
{
$set: {
'usersLiked.username': details.username,
'usersLiked.city': details.city,
'usersLiked.street': details.street,
'usersLiked.website': details.website,
},
}
);
};

Your match on the course update looks valid but you are trying to set values into an array and according to the Mongo docs you need to provide an array indexer or a positional operator.
The following will allow the set command to operate on the first element within the usersLiked array which matches the given username.
coursesCollection.updateMany(
{
usersLiked: {
$elemMatch: {
username: username,
},
},
},
{
$set: {
'usersLiked.$.username': details.username,
'usersLiked.$.city': details.city,
'usersLiked.$.street': details.street,
'usersLiked.$.website': details.website
},
}
)
You could also choose which element in the usersLiked array to update e.g. usersLiked.1.username but I suspect each course only has one element in usersLiked for a given username in which case using the $ operator (which means: the first matching array element) should suffice.

Related

how to find both the conditions using $or operator in mongodb

I want to get emailaddress or phoneNumber matching records.I used the below query to get the result.
user.find({ $or:[{emailAddress: "testemail#gmail.com" , phoneNumber: "123456789"}]})
But when i use the query result is showing 0 records.
What i want is :
{
"phoneNumber" : "123456789",
"emailAddress" : "testemail#gmail.com"
},
{
"phoneNumber" : "8008465488",
"emailAddress" : "testemail#gmail.com"
}
How can i achieve it?
Your Query with a slight modification in it will do the trick
user.find({ $or:[{emailAddress: "testemail#gmail.com"} , {phoneNumber: "123456789"}]})
Syntax for $or - { $or: [ { <expression1> }, { <expression2> }, ... , { <expressionN> } ] }
Your nesting is off; if we add some indenting, we can see that the $or is being given a list with one item in, and that item has two properties:
$or: [
{
emailAddress: "testemail#gmail.com" ,
phoneNumber: "123456789"
}
]
What you want is a list of two possibilities, each with one property:
$or: [
{
emailAddress: "testemail#gmail.com"
},
{
phoneNumber: "123456789"
}
]

Meteor collection find and update value within object in subarray

I'm having some trouble determining how to find a document within a collection, and a value within an object in a subarray of that document — and then update a value within an object in that array.
I need to do the following:
find by _id
find object in ratings array that matches the user + post keys
update the report value within that object
For example, the documents in my collection are set up like below.
{
"_id" : "mz32AcxhgBLoviRWs",
"ratings" : [
{
"user" : "mz32AcxhgBLoviRWs",
"post" : "SMbR6s6SaSfsFn5Bv",
"postTitle" : "fdsfasdf",
"date" : "2017-09-27",
"rating" : "4",
"review" : "sdfa",
"report" : "a report"
},
{
"user" : "mz32AcxhgBLoviRWs",
"post" : "iZbjMCFR3cDNMo57W",
"postTitle" : "today",
"date" : "2017-09-27",
"rating" : "4",
"review" : "sdfa",
"report" : "some report"
}
]
}
It seems that you want just one update, not three separated queries.
Collection.update({
_id: <id>,
ratings: {
$elemMatch: {
user: <user>,
post: <post>
}
}
}, {
$set: {
'ratings.$.report': <report>
}
});
Documentation: $elemMatch, <array>.$.

How to sort mongodb records based on its position in another array

I am querying MongoDB using Mongoose and getting the record along with populated records, However i would like to sort the populated records i am receiving.
const record = Model.findOne({ '_id': '10b9ad30be4c3c0e98cf199q' });
{
"_id" : 10b9ad30be4c3c0e98cf199q,
"name" : "Hello World",
"_order" : [ "28b802cd0588741258d01120", 28b802cd0588741258d01130"", "28b802cd0588741258d01140" ],
}
And, After populating It looks like
const record = Model.findOne({ '_id': '10b9ad30be4c3c0e98cf199q' }).populate('items');
{
"_id" : 10b9ad30be4c3c0e98cf199q,
"name" : "Hello World",
"_order" : [ "28b802cd0588741258d01120", 28b802cd0588741258d01130"", "28b802cd0588741258d01140" ],
"items" : [
{ "_id" : 28b802cd0588741258d01140, name: "Games" },
{ "_id" : 28b802cd0588741258d01120, name: "Movies" }
{ "_id" : 28b802cd0588741258d01130, name: "Science?" }
]
}
How can i effectively sort the populated records items based on its _id in the _order field?
Is it even possible or do i have to do it client side/in server after getting the record.

Firebase data structuring - accessing jobIDs for each user

In my database, I currently have two kinds of objects, users and jobs. I am already storing userIDs in jobs. Do I also need to store jobIDs in each user?
A typical user:
"-JqzUjcOfddBNd_HtjKb" : {
"contact" : {
"-JqzWcIyD77ZwatEKALp" : {
"email" : "someguy#yahoo.com"
},
"-JqzWrtyni3ZGOKooNF7" : {
"email" : "someguy#outlook.com"
}
},
"country" : "234",
"cv" : "https://linktourl.com",
"dateAdded" : 1433436879708,
"ethnicity" : "0",
"firstName" : "John",
"lastName" : "Smith",
"notes" : {
"-JqzhvtNcueUsPr8xwh8" : {
"date" : 1433440599702,
"user" : "iwrotethisnote#example.com",
"value" : "interested in job; need to interview"
}
},
"roles" : [ true ]
},
And a typical job:
"-Jqz5mOr-DmLcxmTVRPi" : {
"age" : [ "2" ],
"city" : "0",
"clientID" : "-Jqz7goZC76vl94VT0dq",
"dateModified" : 1433431226687,
"longDesc" : "Teacher should have experience",
"notes" : {
"-Jqz6SO74OJOESwOVfkG" : {
"date" : 1433430513294,
"notevalue" : "bill spoke with her",
"userid" : "name#gmail.com"
}
},
"schedule" : "Evening or weekend",
"status" : "needDetail",
"subjects" : {
"15" : true,
"42" : true
},
"title" : "She wants a native speaker"
},
As you can see, jobs have a field for clientID, which is a foreign key (so to speak) of a user's id. When I access a user's information, I want to know those jobs that they are associated with (i.e. for which they have supplied their client ID). How to do this in Firebase?
Should I:
Update both objects, and keep an array of jobIDs in the client object?
Query all jobs, then pass in those that have the user's ID as the client ID. Something like:
// user controller
var jobs = [];
jobsRef.$on('value', function(snapshot) {
snapshot.val().forEach(function(job) {
if (job.clientID = $scope.userID) {
jobs.push(job);
}
}
}
Option 1 makes the data redundant (which I guess is ok, because of the emphasis on denormalization), but it also makes it more likely to become out of sync, for example if one of the two updates I would be making fails.
Option 2 seems like it would run a lot slower.
You can use equalTo() of firebase query
var jobsRef = new Firebase();
jobsRef.orderByChild("clientID").equalTo($scope.userID)
.on("value", function(snapshot) {
console.log(snapshot.key());
});
For more info, please check out Query.equalTo()

MongoDB Retrieve a subset of an array in a collection by specifying two fields which should match

I'm using this structure to store conversations & messages:
{ "_id" : ObjectId( "4f2952d7ff4b3c36d700000d" ),
"messages" : [
{ "_id" : ObjectId( "4f2952d7ff4b3c36d700000c" ),
"sender" : "4f02f16f0364c024678c0e5f",
"receiver" : "4f02f16f0364c024678c0e61",
"receiver_deleted" : "true",
"sender_deleted" : "true",
"body" : "MSG 1",
"timestamp" : "2012-02-01T14:57:27Z" },
{ "_id" : ObjectId( "4f2952daff4b3c36d700000e" ),
"sender" : "4f02f16f0364c024678c0e61",
"receiver" : "4f02f16f0364c024678c0e5f",
"body" : "MSG 2",
"timestamp" : "2012-02-01T14:57:30Z" },
{ "_id" : ObjectId( "4f295305ff4b3c36d700000f" ),
"sender" : "4f02f16f0364c024678c0e5f",
"receiver" : "4f02f16f0364c024678c0e61",
"body" : "TEST",
"timestamp" : "2012-02-01T14:58:13Z" } ],
"participants" : [
"4f02f16f0364c024678c0e5f",
"4f02f16f0364c024678c0e61" ],
"type" : "chat" }
When one of the sender or receiver does delete a specific message, receiver_deleted or sender_deleted gets added to the message (as you see in the first message).
Now how can I fetch a conversation with only the messages in it which haven't the sender/receiver deleted flag set?
First I tried like this:
db.conversations.find({
"_id": ObjectId("4f2952d7ff4b3c36d700000d"),
"participants": {"$in": ["4f02f16f0364c024678c0e5f"]},
"$or": [
{
"$and": [{"messages.sender": "4f02f16f0364c024678c0e5f"}, {"messages.sender_deleted": {"$exists": false}}]
},
{
"$and": [{"messages.receiver": "4f02f16f0364c024678c0e5f"}, {"messages.receiver_deleted": {"$exists": false}}]
}
]
})
But this doesn't work. I also tried with $elemMatch like this:
db.conversations.find({
"_id": ObjectId("4f2952d7ff4b3c36d700000d"),
"participants": {"$in": ["4f02f16f0364c024678c0e5f"]},
"$or": [
{
"messages": {
"$elemMatch": {"sender": "4f02f16f0364c024678c0e5f", "sender_deleted": {"$exists": False}}
}
},
{
"messages": {
"$elemMatch": {"receiver": "4f02f16f0364c024678c0e5f", "receiver_deleted": {"$exists": False}}
}
}
]
})
And a couple of other options with trying $and instead of $or etc. but it doesn't work.. Either it returns nothing or the whole conversation regardless of the receiver/sender deleted fields.
Thank you,
Michael
It is not currently possible to retrieve a subset of an array with mongodb. You will always get the whole document back if there is a match, or nothing if there is no match. $slice allows you to return a subset, but that's based on a starting and stopping index (which is not what you want - as you want to return only the matching messages in the array).
The feature you are describing has been logged and requested here: https://jira.mongodb.org/browse/SERVER-828
Since version 2.2 Aggregation Framework is available. You could perform your query like this:
db.expose.aggregate(
//Find the Documents which contains the desired criteria (document level)
{
$match: {
$or: [
{
"messages.sender_deleted": "true"
},
{
"messages.receiver_deleted": "true"
}]}},
//Peels off the elements of messages array individually
{
$unwind: "$messages"
},
//Perform the same find method, now in the embed collection level
{
$match: {
$or: [
{
"messages.sender_deleted": "true"
},
{
"messages.receiver_deleted": "true"
}]}},
//Select what to show as the result: in this case the Document id and the messages array
{
$group: {
_id: "$_id",
messages: {
$push: "$messages"
}}});
The first match is not required, but is better to filter out as much as possible in the beginning.

Categories

Resources