Mongoose: moving find() results to an array in the file - javascript

I created a module that contains a mongoose model (user) that I want to export. For now, this only contains properties name and age.
var mongoose = require('mongoose');
var db = mongoose.createConnection('localhost', 'moviemeter');
var schema = mongoose.Schema({name:String, age: Number});
var User = db.model('user', schema);
module.exports = User;
Here, I would like to acces this model and find all the objects in it. Then, I would like to be able to fill my userArr variable with all the users in my database, but even though the first console.log returns the name of this object it doesn't push it into the array. What's the reason for this and what is a way I can fix this?
// user module
var User = require('./modelModules/memberModel');
var userArr = [];
var users = User.find({}, function (err, users) {
console.log(users[0].name)
users.forEach(function(user) {
userArr.push = user;
});
});
console.log(userArr[0].name)

It seems wrong usage of array push, you should use it as following:
userArr.push(user);

It is because you are using forEach, which is asynchon.
And your push has to be a function
userArr.push(yourUser)
Can you try with a "for" like this ?
// user module
var User = require('./modelModules/memberModel');
var userArr = [];
User.find({}, function (err, users) {
console.log(users[0].name)
users.forEach(function(user) {
for (var i = 0; i < users.length; i++) {
userArr.push(user);
}
console.log(userArr[0].name);
});
Hope it helps.

Related

Get Key Value Given Other Key Value in FireBase

I am trying to find a way to find the value of the id given the email.
For example, If I had email2#gmail.com, It would give me the ID 108454568498950432898.
All emails are unique and there will be no repetition of emails.
This is my user tree:
Note: In the image it says email2 instead of email2#gmail.com. Ignore this
Here's my code so far:
(Code won't run obviously but it's easier to enter code using the embed)
var users;
var givenEmail = "email2#gmail.com";
var neededID;
var dataRef = firebase.database().ref('users');
dataRef.on('value', (snapshot) => {
const data = snapshot.val();
users = data;
});
var usersArray = Object.keys(users);
for(i = 0; i < usersArray.length; i++) {
if(users[i].email == givenEmail) {
neededID = i;
break;
}
}
I recommend using a query to perform the filtering on the server, instead of downloading the entire users node and filtering in your application code as you now do.
var givenEmail = "email2#gmail.com";
var dataRef = firebase.database().ref('users');
var query = dataRef.orderByChild('email').equalTo(givenEmail);
dataRef.once('value', (snapshot) => {
snapshot.forEach((userSnapshot) => {
console.log(userSnapshot.val().id);
});
});
Well, I think you are almost there.
users[i].email
you can retrieve the email using this method, and similarly you can do it with id too
users[i].id
Please note that you wanted to find email2#gmail.com but your firebase only have email2
Maybe you would want to change that

How to clone document to another collection? [duplicate]

My approach would be to get the document instance, and create a new one from the instance fields. I am sure there is a better way to do it.
You need to reset d1.isNew = true; as in:
Model.findById(yourid).exec(
function(err, doc) {
doc._id = mongoose.Types.ObjectId();
doc.isNew = true; //<--------------------IMPORTANT
doc.save(callback);
}
);
Can you clarify what you mean by "copy/clone"? Are you going trying to create a duplicate document in the database? Or are you just trying to have two vars in your program that have duplicate data?
If you just do:
Model.findById(yourid).exec(
function(err, doc) {
var x = doc;
Model.findById(yourid).exec(
function(err, doc2) {
var y = doc2;
// right now, x.name and y.name are the same
x.name = "name_x";
y.name = "name_y";
console.log(x.name); // prints "name_x"
console.log(y.name); // prints "name_y"
});
});
In this case, x and y will be two "copies" of the same document within your program.
Alternatively, if you wanted to insert a new copy of the doc into the database (though with a different _id I assume), that would look like this:
Model.findById(yourid).exec(
function(err, doc) {
var d1 = doc;
d1._id = /* set a new _id here */;
d1.isNew = true;
d1.save(callback);
}
);
Or if you're doing this from the outset, aka you created some document d1, you can just call save twice without setting the _id:
var d1 = new Model({ name: "John Doe", age: 54 });
d1.save(callback);
d1.save(callback);
There will now be two documents with differing _id's and all other fields identical in your database.
Does this clarify things a bit?
My two cents:
const doc = await DocModel.findById(id);
let obj = doc.toObject();
delete obj._id;
const docClone = new DocModel(obj);
await docClone.save();
So, a lot of these answers will work well for simple docs, but there could be an error case when you're trying to make a deep clone of complex documents.
If you have arrays of subdocs, for example, you can end up with duplicate _ids in your copied document, which can cause subtle bugs later.
To do a deep clone of a mongoose doc, I suggest trying something like:
//recursively remove _id fields
function cleanId(obj) {
if (Array.isArray(obj))
obj.forEach(cleanId);
else {
delete obj['_id'];
for (let key in obj)
if (typeof obj[key] == 'object')
cleanId(obj[key]);
}
}
let some_doc = await SomeModel.findOne({_id: some_id});
let new_doc_object = cleanId(some_doc.toObject());
let new_doc = new SomeModel(new_doc_object);
await new_doc.save();
This is going to be a pretty safe approach, and will ensure that every part of your object is cloned properly with newly generated _id fields on save.
The following code to clone documents:
Model.findById(yourid).exec(
function(err, doc) {
var newdoc = new Model(doc);
newdoc._id = mongoose.Types.ObjectId();
newdoc.save(callback);
}
);
For simply clone use this :
Context.findOne({
_id: context._id
})
.then(function(c) {
c._id = undefined;
c.name = context.name;
c.address = context.address;
c.created = Date.now();
return Context.create(c.toObject());
}).then(function(c) {
return res.json({
success: true,
context: context
});
}).catch(function(err) {
next(err, req, res);
});
const cloneDoc = (doc, model)=>{
const copyDoc = new Model({
...doc.toObject(),
_id: undefined,
});
copyDoc.isNew = true;
return copyDoc;
}
To copy a document into the same collection or different collection first get (query) the data and make a copy of the data. Afterwards remove the _id from the new list as you can't from the current data. This will allow you to insert a new record with new _id assigned from mongodb
change searchBy to what you are trying to find the document by.
change collectionA and collectionB to the name of the collection to create you copy to. Currently we are searching in collectionA and copying the data in collectionB
collectionA.find(searchBy).exec(function (err, doc) {
// create a new copy
let newDoc = { ...doc[0] }._doc;
// delete property from new copy (remove _id).
delete newDoc._id;
// insert copy into db
var newdoc = new collectionB(newDoc);
newdoc.save();
});
You can basically use .clone() to get a copy.
const cars = Cars.find();
const carsCopy = cars.clone();
await cars;
await carsCopy;
https://mongoosejs.com/docs/api.html#schema_Schema-clone

Understanding promises a little more

I'm struggling to make this works. i'm dealing with sequelize promisses and i want to return the queried elements to the view, but instead its returning null. I know its because promises are async requests and it does not return a result immediatly after you call it, ok, but how to return the values, put it into an array and than return the array?
this is the code i have so far.
router.post('/register', function(req, res, next) {
var sales = req.body.sales;
var person = req.body.personID;
var promisses = [];
var delivery = req.body.delivery;
for(var i =0;i<sales.length;i++){
var product_id = sales[i].product_id;
var amount = sales[i].amount;
var price = sales[i].produto_price;
var salePromisse = Sale.create({
'product_id': product_id,
'person_id': person,
'amount': amount,
'price': price,
'total': amount*price
});
//i couldnt find a word which describes what movimentacao means...lets keep it.
var movimentacao = Movimentacao.create({
"tipo": 0,
"id_product": product_id,
"id_provider": "",
"data": new Date(),
"price": amount*price
});
promisses.push(salePromisse);
promisses.push(movimentacPromisse);
}
Promise.all(promisses).then(function (promissesArray){
var name = "";
var suc = 0;
var total = 0;
var salesResult = [];
var salesObject = {};
if(!delivery){
res.send({msg: "aaaaa"});
}else{
promissesArray.forEach(function(pro){
if(pro!==null && pro !== undefined){
if(pro['$modelOptions'].tableName === undefined){
Product.findById(pro.product_id).then(function (product){
salesObject.product = product.name;
salesObject.price= pro.price;
salesObject.amount = pro.amount;
salesObject.total = pro.total;
total += salesObject.total;
salesResult.push(salesObject);
salesObject = {};
return Person.findById(per);
}).then(function (person) {
name = person.name;
});;
}
}
});
//here is where i would like to return to the view salesResult and name.
//res.render("folder/view", {sales: salesResult, person: name});
console.log(salesResult);
}
});
});
Well, the promisses array has the CREATE instance for each of my models, and i'm creating a few types of it.
I want to insert on the database, check if the promise resolved is dealing with an specific table (the field modelOptions is undefined on it, i already debugged), query all the other results because on the promisses array i have just the id, and than put into the array to be able to return to the view, but on the console.log on the last line, it returns null. How can i improve the code to be able to do all i want and than return to the view?
Dont worry, all the model related variables are beeing declared above.
Thanks.

SoundCloud API - create user profiles based on ID in system

I have this system that takes in an array of SoundCloud user IDs, iterates them through a series of SC.get functions to obtain information of each user (user ID, username, followings and genre preference).
<script src="//connect.soundcloud.com/sdk.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.0/jquery.min.js"></script>
<body>
<script>
//Initialize soundcloud API with client ID
SC.initialize({
client_id: "54cb0ff94ff9313ef6ca4674e9fe3026"
});
var userIds = [9110252, 55829847, 145189041, 4184647, 225458844, 22557004, 109447512];
var userids = [];
var usernames = [];
var userFollowingsE = [];
var profile =[];
var ready = 0
for(u = 0; u < userIds.length; u++){
var id = userIds[u];
getUserData(id);
function getUserData(userid){
//**************************** USER ***************************//
SC.get("/users/"+id, function(user){
userids.push(user.id);
usernames.push(user.username);
});
//********************** USER/FOLLOWINGS **********************//
SC.get('/users/'+id+'/followings',function(followings) {
var userFollowings = [];
for(i = 0; i < followings.collection.length; i++) {
userFollowings.push(followings.collection[i].username);
}
userFollowingsE.push(userFollowings);
ready++
if(ready >= userIds.length) onComplete();
});
}
}
function onComplete(){
console.log(usernames);
console.log(userIds);
console.log(userFollowingsE);
}
var users = [
{ userid: this.userids,
username: this.usernames,
genre: this.genres,
followings: this.followings,
}
]
</script>
</body>
What I want the system to do is associate these bits of information with their corresponding user in an object e.g.
var users = {
user9110252: {
userid = userid,
username = username,
genrePref = genre
followings = followings
}
}//etc...
However the arrays outputted from the system change order each time and I don't think are associated with one another.
I am unsure of how to do this and welcome any suggestions.
If you want the resulting array to look like you described, you should build the user Object as you go, instead of creating multiple arrays and then merging them.
Here is what I've tried, run it and see if it gives you the expected result. Also note that I did not include the genres, because I could not find them in the data provided.
//Initialize soundcloud API with client ID
SC.initialize({ client_id: "54cb0ff94ff9313ef6ca4674e9fe3026" });
var userIds = [9110252, 55829847, 145189041, 4184647, 225458844, 22557004, 109447512],
users = [];
for(var i=0; i<userIds.length; i++){ getUserData(userIds[i]); }
function getUserData(userid){
// The user Object you'll be building
var myUser = {};
// Grab the info for this user
SC.get("/users/"+userid, function(user){
myUser.userid = user.id;
myUser.username = user.username;
// Then get its followings
SC.get('/users/'+userid+'/followings',function(followings) {
myUser.followings = followings.collection.map(function(user){
return user.username;
});
// Push that user to the user list
users.push(myUser);
// Execute the callback if all users have been fetched
if(users.length == userIds.length){ onComplete(); }
});
});
}
function onComplete(){
console.log(users);
// Just for the demo:
document.body.innerHTML = '<pre>' + JSON.stringify(users,0,2) + '</pre>';
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://connect.soundcloud.com/sdk.js"></script>

closing mongodb connection and get correct results with multiple parallel asynchronous query

I am new to Mongo and Node. I am currently using Mongoskin and Bluebird to handle the db connection and queries (as suggested here: https://stackoverflow.com/a/23687958/2701348 ).
I have three collections: Users, Binders and Cards.
The Binders collection contains the information about Cards for each User.
Each document in Binders has as properties:
User Id <--- that refers to the User owning the Card
Card Code <--- that refers to a Card
Count <--- that refers to the number of cards owned by the User
I prefer to have a separate Cards collection so that when a Card changes, it changes for all the Users Binders.
Now I am willing to retrieve an array for a given user such as:
[{card: {card document}, count: 4}, ...]
I have the following problems:
the db connection should be closed after all the async db callbacks are called
the cards array should be returned after the last db.collection('cards').find gives back the results
I know my following code is wrong but can be a starting point for a discussion:
var getAllBinderCards = function(req, res){
var db = req.db;
var userId = req.userId;
var promiseBinders = db.collection('binders').find({userId: userId}).toArrayAsync();
promiseBinders.then(function(binderCards) {
if (binderCards) {
var promiseCards;
//console.log("------ binderCards: ", binderCards);
var cards = [];
for (var i = 0; i < binderCards.length; i++) {
var binderCard = binderCards[i];
promiseCards = db.collection('cards').find({Code: binderCard.Code}).toArrayAsync();
promiseCards.then(function(cardsDB){
if(cardsDB){
//console.log("Cards found: ",binderCard.Code, cardsDB);
for (var i = 0; i < cardsDB.length; i++) {
cardsDB[i].count = binderCard.count;
};
cards.concat(cardsDB);
}
});
}
promiseCards.then(function(){
db.close();
console.log("Binder Cards: " , cards);
res.json(cards);
});
}
});
}
I am struggling trying to figure out how to handle the promisfied asynchronous call correctly in order to send back the whole array and close the db connection.
I think I should try to build a promise before the for loop and use it to chain the query on Cards promises and lastly chain the db.close() and res.json(cards) statements.
[EDIT] Maybe the easiest solution is to simply use the $in filter inside a single db.collection('cards').find({Code: {$in: [bindersCodeArray] }}).toArrayAsync(); and avoid that for loop:
var getAllBinderCards = function(req, res){
var db = req.db;
var userId = req.userId;
var promiseBinders = db.collection('binders').find({userId: userId}).toArrayAsync();
promiseBinders.then(function(binderCards) {
if (binderCards) {
var binderCodes = binderCards.map(function(element){
return element.Code;
});
var promiseCards = db.collection('cards').find({Code: {$in: binderCodes} }).toArrayAsync();
promiseCards.then(function(cards){
var bindersDictionary = {};
for (var i = 0; i < binderCards.length; i++) {
bindersDictionary[binderCards[i].Code] = binderCards[i].count;
};
for (var i = 0; i < cards.length; i++) {
cards[i].count = bindersDictionary[cards[i].Code];
};
db.close();
console.log("Binder Cards: " , cards);
res.json(cards);
});
}
});
}
Still I am curious if there is an elegant way to solve this riddle using promises.
I would expect that using $in and array may have constraints on the number of binders you can pass and affect query performance. You can also try doing this with async#map. e.g.:
...
function(binders) {
async.map(binders, cardsForBinders, function(err, bindersWithCards) {
// TODO: close connection here.
}
function cardsForBinders(binder, callback) {
// 1. find cards for binder.
// 2. prepare transformed response: binderWithCards.
callback(null, binderWithCards);
}
}
...

Categories

Resources