I have the following UserSchema
const userSchema = new Schema({
// Irrelevant properties
likedPrograms: [{type:Schema.Types.ObjectId, ref: 'Program'}],
});
I am trying to remove a program from this array with this query
const updatedUser = await User.findOneAndUpdate({_id: req.params.id}, {$pull: {likedPrograms: {_id: programToRemove._id}}}, {new: true});
However, this line of code is executing without removing the element from the array. Is it a problem with async/await? If I try to do it with callbacks it works, however, I don't like to use callbacks in my code as I don't want to run into callback hell.Any ideas on why this is not working?
There is no problem with async await or callbacks. If you can take a look at your schema you have not taken any field inside the likedPrograms, it is just simple array of ObjectIds not with array of object with key value pair.
It would have worked if you had taken like so
likedPrograms: [{ _id: { type: Schema.Types.ObjectId, ref: 'Program' }}]
Related
I'm currently learning MongoDB using mongoose and nodeJS. I'm trying to store notes to a database called 'notes'. For this, first I connected to the database like this:
mongoose.connect(`mongodb+srv://pedro_yanez:${password}#fsopen-2021-project.ngteq.mongodb.net/note-app?retryWrites=true&w=majority`,
{
useNewUrlParser: true,
useUnifiedTopology: true,
})
Then, I created a Note Schema and a Note Model:
const noteSchema = new mongoose.Schema({
content: String,
date: Date,
important: Boolean
})
const Note = mongoose.model('Note', noteSchema)
Then, I saved three documents to the database:
const note = new Note({
content: 'Note #N',
date: new Date(),
important: true
})
note.save().then(result => {
console.log('note saved!')
mongoose.connection.close()
})
This was successfull as I can see them on MongoDB Atlas' collections, but when I try to query the uploaded notes using mongoose's find() method the following way:
Note.find({}).then(result => {
result.forEach(note => {
console.log(note)
})
mongoose.connection.close()
})
I get the following error:
node_modules/mongoose/lib/query.js:2151
return cursor.toArray(cb);
^
TypeError: cursor.toArray is not a function
Note that the code that I attached is from HY's 'Full Stack Open 2021' course, from part3.c.
I also tried to use find() with a callback function as stated here:
Note.find({}, function (err, docs) {console.log(docs)});
mongoose.connection.close()
But I get 'undefined' and another error:
/node_modules/mongodb/lib/collection.js:238
throw new error_1.MongoInvalidArgumentError('Method "collection.find()" accepts at most two arguments');
^
MongoInvalidArgumentError: Method "collection.find()" accepts at most two arguments
I could really use a hint on what's wrong with my implementation, as I've been fighting with this all day!
I see we are in the same exercise on Fullstack open.
I managed to log all "notes" following the quick start from https://mongoosejs.com/
After mongoose.connect:
const db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function() {
// Here I used the Model.find() at the end of the page and closed
// the connection as they say in the lesson.
});
It worked here, hope it helps.
I have become slightly lost with react-query. Essentially, I have a useQuery to fetch a user from my database. Their details are added to a form and they can update and submit.
The problem I have is that the update is done to a different database. The main database will be batch updated at a later point. As such, instead of refetching the initial data, I need to use setQueryData to update the cache version.
queryClient = useQueryClient()
const { mutate } = useMutation(postUser, {
onSuccess: async (response) => {
console.log(response)
queryClient.cancelQueries('user');
const previousUser = queryClient.getQueryData('user');
console.log(previousUser)
queryClient.setQueryData('user', {
...previousUser,
data: [
previousUser.data,
{ '#status': 'true' },
],
})
return () => queryClient.setQueryData('user', previousUser)
}
})
At the moment I have something like the above. So it calls postUser and gets a response. The response looks something like so
data:
data:
user_uid: "12345"
status: "true"
message: "User added."
status: 1
I then getQueryData in order to get the cache version of the data, which currently looks like this
data:
#userUuid: "12345"
#status: ""
message: "User found."
status: 1
So I need to update the cached version #status to now be true. With what I have above, it seems to add a new line into the cache
data: Array(2)
0: {#userUuid: "12345", #status: ""}
1: {#status: "true"}
message: "User found."
status: 1
So how do I overwrite the existing one without adding a new row?
Thanks
This is not really react-query specific. In your setQueryData code, you set data to an array with two entries:
data: [
previousUser.data,
{ '#status': 'true' },
],
first entry = previousUser.data
second entry = { '#status': 'true' }
overriding would be something like this:
queryClient.setQueryData('user', {
...previousUser,
data: {
...previousUser.data,
'#status': 'true',
},
})
On another note, it seems like your mixing optimistic the onMutate callback with the onSuccess callback. If you want to do optimistic updates, you'd implement the onMutate function in a similar way like you've done it above:
cancel outgoing queries
set data
return "rollback" function that can be called onError
This is basically the workflow found here in the docs.
if you implement onSuccess, you're updating the cache after the mutation was successful, which is also a legit, albeit different, use-case. Here, you don't need to return anything, it would be more similar to the updates from mutation responses example.
I created a post Schema and I have trouble implementing the comment and comment reply schema since you can not predict how often one comment reply has it own reply.
I am using mongoose and express.
So how can I implement this type of schema design?
I think you're looking for something like this where you are referencing comments from within your comment schema.
I added a middleware to pre-populate the replies array when you call .find(). You can add more middleware for other calls like .findOne() etc.
const mongoose = require("mongoose");
const commentSchema = mongoose.Schema(
{
comment: {
type: String,
required: true
},
author: { // To reference the user that left the comment. If needed
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User',
},
replies:[{type: Schema.Types.ObjectId, ref: "Comment"}] // Array of comment replies
},
{
timestamps: true,
}
);
// Middleware to populate the replies when you call `find()`
commentSchema.pre('find', function() {
this.populate('replies');
});
module.exports = mongoose.model('Comment', commentSchema);
You can do more in-depth on this post which will show you how to pre-populate the replies field when returning comments etc.
https://www.makeschool.com/academy/track/standalone/reddit-clone-in-node-js/comments-on-comments
EDIT: Since I wasn't able to find a correct solution, I changed the
application's structure a bit and posted another question:
Mongoose - find documents not in a list
I have a MEAN app with three models: User, Task, and for keeping track of which task is assigned to which user I have UserTask, which looks like this:
const mongoose = require("mongoose");
const autopopulate = require("mongoose-autopopulate");
const UserTaskSchema = mongoose.Schema({
completed: { type: Boolean, default: false },
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: "User",
autopopulate: true
},
taskId: {
type: mongoose.Schema.Types.ObjectId,
ref: "Task",
autopopulate: true
}
});
UserTaskSchema.plugin(autopopulate);
module.exports = mongoose.model("UserTask", UserTaskSchema);
In my frontend app I have AngularJS services and I already have functions for getting all users, all tasks, and tasks which are assigned to a particular user (by getting all UserTasks with given userId. For example:
// user-task.service.js
function getAllUserTasksForUser(userId) {
return $http
.get("http://localhost:3333/userTasks/byUserId/" + userId)
.then(function(response) {
return response.data;
});
}
// task-service.js
function getAllTasks() {
return $http.get("http://localhost:3333/tasks").then(function(response) {
return response.data;
});
}
Then I'm using this data in my controllers like this:
userTaskService
.getAllUserTasksForUser($routeParams.id)
.then(data => (vm.userTasks = data));
...and because of autopopulate plugin I have complete User and Task objects inside the UserTasks that I get. So far, so good.
Now I need to get all Tasks which are not assigned to a particular User. I guess I should first get all Tasks, then all UserTasks for a given userId, and then make some kind of difference, with some "where-not-in" kind of filter.
I'm still a newbie for all the MEAN components, I'm not familiar with all those then()s and promises and stuff... and I'm really not sure how to do this. I tried using multiple then()s but with no success. Can anyone give me a hint?
You can do at server/API side that will more efficient.
In client side, if you want to do then try below
var userid = $routeParams.id;
userTaskService
.getAllTasks()
.then((data) => {
vm.userTasks = data.filter(task => task.userId !== userid)
});
How can I post an array of Schema.Types.ObjectId (s) to MongoDB? I'm trying to create User Groups, which is a group of the 'User' Model e.g.
var UserGroup = new Schema({
users: [{
type: Schema.Types.ObjectId,
ref: 'User'
}]
});
New UserGroup Function
module.exports.create = function(request, response) {
var group = new UserGroup({
users = request.body.users
});
group.save(function(error) {
if(error) { throw error; } else { response.send('Group Created Successfully.');
});
};
I'm currently using Postman to test the functionality, how exactly should the data be posted?
As a Javascript array i.e ['A_USER_ID', 'A_USER_ID'] ?
Thanks!
#Answer
I was using the older syntax of the select() function, and therefore was passing invalid parameters to the $push function. When sending the request, I simply pass the ObjectIds as id,id,id and once they get to the server, simply put it into an array using var my_array = request.body.users.split(','); and then push it to the database using the following:
$push: { users: { $each: my_array } }
I hope this was helpful, the documentation isn't particularly clear on this matter.