I'm trying to update all users from an array in mongoose, but I don't know how to do that.
I'm parsing all users from MongoDB database to an array, then I'm editing values in this array and I need to save array back to the database.
Parsing all users from the database:
let users = []
async function loadUser() {
users = await User.find({}) // User is Schema
}
Then I'm trying to save all of them, but it isn't working.
async function saveUsers () {
users.forEach(user => { // maybe there's other way to do save, but I don't found it.
User.updateOne(user, user, function (err) {
if (err) throw err
})
})
}
You can use forEach as follow:
users.forEach(function(user)){
User.update({"_id": user._id}, {"$set": {"value": user.value }}, callback);
});
Related
I have a local postgres database running on my machine. I use node.js to access it. I have a table called 'friends' where every row is a user and a friend. I also have a table called 'users' where every row has all basic info about a user(e.g name and such).
When I want to remove a friendship between two users I have to remove two rows from the 'friends' table. I do this with this function:
const removeFriend = async (clientId, friendId) => {
// initiate transaction
return db
.transaction((trx) => {
// remove friendship from client
trx('friends')
.where({ user_id: clientId, friend_id: friendId })
.del()
.then(() => {
// remove friendship from friend
return trx('friends').where({ user_id: friendId, friend_id: clientId }).del();
})
// if all good then commit
.then(trx.commit)
// if bad then rollback
.catch(trx.rollback);
})
.catch(() => 'error');
};
I call the removeFriend function this way removeFriend(clientId, friendId)
Then when i want to get a list of all friends with their names from the database i use this function:
const getUserFriends = async (clientId) => {
// get friends
return db('friends')
.where({ user_id: clientId })
.join('users', 'users.id', 'friends.friend_id')
.select('friends.friend_id', 'users.name')
.then((friends) => friends)
.catch(() => 'error');
};
I call the getUserFriends function this way await getUserFriends(clientId)
The problem is that when I use removeFriend function and then directly use the getUserFriends function i get a list where the users are still friends. However, If i look in the database the rows have been deleted so naturaly i should get a list where the users are not friends. Do I use the await wrong or something?
I'd like to update some data in Mongoose by using array value that I've find before.
Company.findById(id_company,function(err, company) {
if(err){
console.log(err);
return res.status(500).send({message: "Error, check the console. (Update Company)"});
}
const Students = company.students;
User.find({'_id':{"$in" : Students}},function(err, users) {
console.log(Students);
// WANTED QUERY : Update company = null from Users where _id = Students[];
});
});
Students returns users._id in array with object inside, and I use that to find users object, and then I want to set null a field inside users object, that field named as "company". How I can do that? Thank you.
From what you posted (I took the liberty to use Promises but you can roughly achieve the same thing with callbacks), you can do something like:
User.find({'_id':{"$in" : Students}})
.then( users =>{
return Promise.all( users.map( user => {
user.company = null;
return user.save()
}) );
})
.then( () => {
console.log("yay");
})
.catch( e => {
console.log("failed");
});
Basically, what I'm doing here is making sure .all() user models returned by the .find() call are saved properly, by checking the Promised value returned for .save()ing each of them.
If one of these fails for some reasons, Promise.all() return a rejection you can catch afterhand.
However, in this case, each item will be mapped to a query to your database which is not good. A better strategy would be to use Model.update(), which will achieve the same, in, intrinsically, less database queries.
User.update({
'_id': {"$in": Students}
}, {
'company': <Whatever you want>
})
.then()
use .update but make sure you pass option {multi: true} something like:
User.update = function (query, {company: null}, {multi: true}, function(err, result ) { ... });
Let us assume that we have two collections say "users" and "usersList"
Upon creating a new user document in users collection with following object
{username: Suren, age:31}
The function should read the above data and update other collection i.e. "usersList" with the username alone like below
{username: Suren}
Let me know the possibility
The code I have tried is
exports.userCreated =
functions.firestore.document('users/{userId}').onCreate((event) => {
const post = event.data.data();
return event.data.ref.set(post, {merge: true});
})
I have done it using below code
exports.userCreated = functions.firestore.document('users/{userId}')
.onCreate((event) => {
const firestore = admin.firestore()
return firestore.collection('usersList').doc('yourDocID').update({
name:'username',
}).then(() => {
// Document updated successfully.
console.log("Doc updated successfully");
});
})
If all you want to do is strip the age property from the document, you can do it like this:
exports.userCreated = functions.firestore.document('users/{userId}').onCreate((event) => {
const post = event.data.data();
delete post.age;
return event.data.ref.set(post);
})
I am having trouble with a simple findById with mongoose.
Confirmed the item exists in the DB
db.getCollection('stories').find({_id:'572f16439c0d3ffe0bc084a4'})
With mongoose
Story.findById(topic.storyId, function(err, res) {
logger.info("res", res);
assert.isNotNull(res);
});
won't find it.
I also tried converting to a mongoId, still cannot be found (even though mongoose supposedly does this for you)
var mid = mongoose.Types.ObjectId(storyId);
let story = await Story.findOne({_id: mid}).exec();
I'm actually trying to use this with typescript, hence the await.
I also tried the Story.findById(id) method, still cannot be found.
Is there some gotcha to just finding items by a plain _id field?
does the _id have to be in the Schema? (docs say no)
I can find by other values in the Schema, just _id can't be used...
update: I wrote a short test for this.
describe("StoryConvert", function() {
it("should read a list of topics", async function test() {
let topics = await Topic.find({});
for (let i = 0; i < topics.length; i ++) {
let topic = topics[i];
// topics.forEach( async function(topic) {
let storyId = topic.storyId;
let mid = mongoose.Types.ObjectId(storyId);
let story = await Story.findOne({_id: mid});
// let story = await Story.findById(topic.storyId).exec();
// assert.equal(topic.storyId, story._id);
logger.info("storyId", storyId);
logger.info("mid", mid);
logger.info("story", story);
Story.findOne({_id: storyId}, function(err, res) {
if (err) {
logger.error(err);
} else {
logger.info("no error");
}
logger.info("res1", res);
});
Story.findOne({_id: mid}, function(err, res) {
logger.info("res2", res);
});
Story.findById(mid, function(err, res) {
logger.info("res3", res);
// assert.isNotNull(res);
});
}
});
});
It will return stuff like
Testing storyId 572f16439c0d3ffe0bc084a4
Testing mid 572f16439c0d3ffe0bc084a4
Testing story null
Testing no error
Testing res1 null
Testing res2 null
Testing res3 null
I noticed that topic.storyId is a string
not sure if that would cause any issues mapping to the other table.
I tried also adding some type defs
storyId: {
type: mongoose.Schema.Types.ObjectId,
required: false
}
Because this query finds the doc in the shell:
db.getCollection('stories').find({_id:'572f16439c0d3ffe0bc084a4'})
That means that the type of _id in the document is actually a string, not an ObjectId like Mongoose is expecting.
To find that doc using Mongoose, you'd have to define _id in the schema for Story as:
_id: { type: String }
If your Mongo schema is configured to use Object Id, you query in nodeJS using
models.Foo.findById(id)
where Foo is your model and id is your id.
here's a working example
router.get('/:id', function(req, res, next) {
var id = req.params.id
models.Foo.findById(id)
.lean().exec(function (err, results) {
if (err) return console.error(err)
try {
console.log(results)
} catch (error) {
console.log("errror getting results")
console.log(error)
}
})
})
In Mongo DB your query would be
{_id:ObjectId('5c09fb04ff03a672a26fb23a')}
One solution is to use mongoose.ObjectId()
const Model = require('./model')
const mongoose = require('mongoose')
Model.find({ id: mongoose.ObjectId(userID) })
It works, but it is weird because we are using id instead of _id
This is how we do it now:
const { mongoose } = require("mongoose");
YourModel.find({ _id: mongoose.Types.ObjectId("572f16439c0d3ffe0bc084a4") });
I got into this scenario too. This was how I solved it;
According to the mongoose documentation, you need to tell mongoose to
return the raw js objects, not mongoose documents by passing the lean option and setting it to true. e.g
Adventure.findById(id, 'name', { lean: true }, function (err, doc) {});
in your situation, it would be
Story.findById(topic.storyId, { lean: true }, function(err, res) {
logger.info("res", res);
assert.isNotNull(res);
});
If _id is the default mongodb key, in your model set the type of _id as this:
_id: mongoose.SchemaTypes.ObjectId
Then usind mongoose you can use a normal find:
YourModel.find({"_id": "5f9a86b77676e180c3089c3d"});
models.findById(id)
TRY THIS ONE .
REF LINK : https://www.geeksforgeeks.org/mongoose-findbyid-function/
Try this
Story.findOne({_id:"572b19509dac77951ab91a0b"}, function(err, story){
if (err){
console.log("errr",err);
//return done(err, null);
}else{
console.log(story);
}
});
I've got myself a question regarding associations in Sails.js version 0.10-rc5. I've been building an app in which multiple models are associated to one another, and I've arrived at a point where I need to get to nest associations somehow.
There's three parts:
First there's something like a blog post, that's being written by a user. In the blog post I want to show the associated user's information like their username. Now, everything works fine here. Until the next step: I'm trying to show comments which are associated with the post.
The comments are a separate Model, called Comment. Each of which also has an author (user) associated with it. I can easily show a list of the Comments, although when I want to display the User's information associated with the comment, I can't figure out how to populate the Comment with the user's information.
In my controller i'm trying to do something like this:
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments') // I want to populate this comment with .populate('user') or something
.exec(function(err, post) {
// Handle errors & render view etc.
});
In my Post's 'show' action i'm trying to retrieve the information like this (simplified):
<ul>
<%- _.each(post.comments, function(comment) { %>
<li>
<%= comment.user.name %>
<%= comment.description %>
</li>
<% }); %>
</ul>
The comment.user.name will be undefined though. If I try to just access the 'user' property, like comment.user, it'll show it's ID. Which tells me it's not automatically populating the user's information to the comment when I associate the comment with another model.
Anyone any ideals to solve this properly :)?
Thanks in advance!
P.S.
For clarification, this is how i've basically set up the associations in different models:
// User.js
posts: {
collection: 'post'
},
hours: {
collection: 'hour'
},
comments: {
collection: 'comment'
}
// Post.js
user: {
model: 'user'
},
comments: {
collection: 'comment',
via: 'post'
}
// Comment.js
user: {
model: 'user'
},
post: {
model: 'post'
}
Or you can use the built-in Blue Bird Promise feature to make it. (Working on Sails#v0.10.5)
See the codes below:
var _ = require('lodash');
...
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments')
.then(function(post) {
var commentUsers = User.find({
id: _.pluck(post.comments, 'user')
//_.pluck: Retrieves the value of a 'user' property from all elements in the post.comments collection.
})
.then(function(commentUsers) {
return commentUsers;
});
return [post, commentUsers];
})
.spread(function(post, commentUsers) {
commentUsers = _.indexBy(commentUsers, 'id');
//_.indexBy: Creates an object composed of keys generated from the results of running each element of the collection through the given callback. The corresponding value of each key is the last element responsible for generating the key
post.comments = _.map(post.comments, function(comment) {
comment.user = commentUsers[comment.user];
return comment;
});
res.json(post);
})
.catch(function(err) {
return res.serverError(err);
});
Some explanation:
I'm using the Lo-Dash to deal with the arrays. For more details, please refer to the Official Doc
Notice the return values inside the first "then" function, those objects "[post, commentUsers]" inside the array are also "promise" objects. Which means that they didn't contain the value data when they first been executed, until they got the value. So that "spread" function will wait the acture value come and continue doing the rest stuffs.
At the moment, there's no built in way to populate nested associations. Your best bet is to use async to do a mapping:
async.auto({
// First get the post
post: function(cb) {
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments')
.exec(cb);
},
// Then all of the comment users, using an "in" query by
// setting "id" criteria to an array of user IDs
commentUsers: ['post', function(cb, results) {
User.find({id: _.pluck(results.post.comments, 'user')}).exec(cb);
}],
// Map the comment users to their comments
map: ['commentUsers', function(cb, results) {
// Index comment users by ID
var commentUsers = _.indexBy(results.commentUsers, 'id');
// Get a plain object version of post & comments
var post = results.post.toObject();
// Map users onto comments
post.comments = post.comments.map(function(comment) {
comment.user = commentUsers[comment.user];
return comment;
});
return cb(null, post);
}]
},
// After all the async magic is finished, return the mapped result
// (or an error if any occurred during the async block)
function finish(err, results) {
if (err) {return res.serverError(err);}
return res.json(results.map);
}
);
It's not as pretty as nested population (which is in the works, but probably not for v0.10), but on the bright side it's actually fairly efficient.
I created an NPM module for this called nested-pop. You can find it at the link below.
https://www.npmjs.com/package/nested-pop
Use it in the following way.
var nestedPop = require('nested-pop');
User.find()
.populate('dogs')
.then(function(users) {
return nestedPop(users, {
dogs: [
'breed'
]
}).then(function(users) {
return users
}).catch(function(err) {
throw err;
});
}).catch(function(err) {
throw err;
);
Worth saying there's a pull request to add nested population: https://github.com/balderdashy/waterline/pull/1052
Pull request isn't merged at the moment but you can use it installing one directly with
npm i Atlantis-Software/waterline#deepPopulate
With it you can do something like .populate('user.comments ...)'.
sails v0.11 doesn't support _.pluck and _.indexBy use sails.util.pluck and sails.util.indexBy instead.
async.auto({
// First get the post
post: function(cb) {
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments')
.exec(cb);
},
// Then all of the comment users, using an "in" query by
// setting "id" criteria to an array of user IDs
commentUsers: ['post', function(cb, results) {
User.find({id:sails.util.pluck(results.post.comments, 'user')}).exec(cb);
}],
// Map the comment users to their comments
map: ['commentUsers', function(cb, results) {
// Index comment users by ID
var commentUsers = sails.util.indexBy(results.commentUsers, 'id');
// Get a plain object version of post & comments
var post = results.post.toObject();
// Map users onto comments
post.comments = post.comments.map(function(comment) {
comment.user = commentUsers[comment.user];
return comment;
});
return cb(null, post);
}]
},
// After all the async magic is finished, return the mapped result
// (or an error if any occurred during the async block)
function finish(err, results) {
if (err) {return res.serverError(err);}
return res.json(results.map);
}
);
You could use async library which is very clean and simple to understand. For each comment related to a post you can populate many fields as you want with dedicated tasks, execute them in parallel and retrieve the results when all tasks are done. Finally, you only have to return the final result.
Post
.findOne(req.param('id'))
.populate('user')
.populate('comments') // I want to populate this comment with .populate('user') or something
.exec(function (err, post) {
// populate each post in parallel
async.each(post.comments, function (comment, callback) {
// you can populate many elements or only one...
var populateTasks = {
user: function (cb) {
User.findOne({ id: comment.user })
.exec(function (err, result) {
cb(err, result);
});
}
}
async.parallel(populateTasks, function (err, resultSet) {
if (err) { return next(err); }
post.comments = resultSet.user;
// finish
callback();
});
}, function (err) {// final callback
if (err) { return next(err); }
return res.json(post);
});
});
As of sailsjs 1.0 the "deep populate" pull request is still open, but the following async function solution looks elegant enough IMO:
const post = await Post
.findOne({ id: req.param('id') })
.populate('user')
.populate('comments');
if (post && post.comments.length > 0) {
const ids = post.comments.map(comment => comment.id);
post.comments = await Comment
.find({ id: commentId })
.populate('user');
}
Granted this is an old question, but a much simpler solution would be to loop over the comments,replacing each comment's 'user' property (which is an id) with the user's full detail using async await.
async function getPost(postId){
let post = await Post.findOne(postId).populate('user').populate('comments');
for(let comment of post.comments){
comment.user = await User.findOne({id:comment.user});
}
return post;
}
Hope this helps!
In case anyone is looking to do the same but for multiple posts, here's one
way of doing it:
find all user IDs in posts
query all users in 1 go from DB
update posts with those users
Given that same user can write multiple comments, we're making sure we're reusing those objects. Also we're only making 1 additional query (whereas if we'd do it for each post separately, that would be multiple queries).
await Post.find()
.populate('comments')
.then(async (posts) => {
// Collect all comment user IDs
const userIDs = posts.reduce((acc, curr) => {
for (const comment of post.comments) {
acc.add(comment.user);
}
return acc;
}, new Set());
// Get users
const users = await User.find({ id: Array.from(userIDs) });
const usersMap = users.reduce((acc, curr) => {
acc[curr.id] = curr;
return acc;
}, {});
// Assign users to comments
for (const post of posts) {
for (const comment of post.comments) {
if (comment.user) {
const userID = comment.user;
comment.user = usersMap[userID];
}
}
}
return posts;
});