How could I bind `save` method using `Q` with mongoose - node.js

Saying that I have the following schema in User.js
var mongoose = require('mongoose');
var Q = require('q');
var userSchema = new mongoose.Schema({
phone: {
type: Number,
require: true,
index: {
unique: true
}
}
});
module.exports = mongoose.model('User', userSchema);
module.exports.findOne = Q.nfbind(module.exports.findOne.bind(module.exports));
module.exports.find = Q.nfbind(module.exports.find.bind(module.exports));
And another file testuser.js
var mongoose = require('mongoose');
var Q = require('q');
var User = require('../User');
var connectionStr = "mongodb://localhost:27017/user-model-test";
mongoose.connect(connectionStr, function(error) {
if (error) {
throw(error);
}
console.log("Connect to MongoDB...");
var testuser = new User({
phone: 1008611,
});
testuser.save = Q.nfbind(testuser.save.bind(testuser));
testuser.save().then(function () {
return User.findOne({ phone: 1008611 }).then(function (user) {
console.log(user.phone);
mongoose.disconnect();
}, function (error) {
throw(error);
});
}, function (error) {
throw(error);
}).done();
});
Notice that currently I use testuser.save = Q.nfbind(testuser.save.bind(testuser)); to bind the save, but I want to do it in User.js, like module.exports.find = Q.nfbind(module.exports.find.bind(module.exports));, is there any way that I could do that?
Thank you.

If you insist on Q, I'd do something like:
User.create = function(data){
var u = new User(data);
u.save = Q.nfbind(testuser.save.bind(testuser));
// add other methods you want
return u;
};
However, I'd like to add that all other Mongoose methods already return a promise if you call .exec() on them (so .find(..).exec() returns an mpromise promise). That is, Mongoose already provides a promise API, save is one of the only methods that are not included yet and there are plans to include it.
It's also beneficial to know that this becomes a one liner with Bluebird, which has a promisifyAll function that creates promises that are easier to debug and are faster than Qs or Mongoose's.

Related

Cannot access mongoose schema attribute

So... I basically search an item in the database, find it, print it fine but I can t access the attributes. When I try to print them they show undefined.
I know that the attribute are in fact undefined because it doesn t break the loop and I do have both attributes in my mongoose schema. I also tried to stringify and parse it to json back and didn t work. (this is all the material I could find)
This is the script:
const name_to_find = 'Copac';
async function myFetch(){
const express = require('express');
const mongoose = require('mongoose');
const Item = require('./models/Item');
const mongoUrl = process.env.MONGO_URL;
const appsc = express();
var connectWithRetry = function() {
return mongoose.connect(mongoUrl, { useNewUrlParser: true, useUnifiedTopology: true }, function(err) {
if (err) {
console.error('Failed to connect to mongo on startup - retrying in 3 sec', err);
setTimeout(connectWithRetry, 3000);
}
});
};
connectWithRetry();
var date1 = new Date();
while(true){
var date2 = new Date();
if(date1.getTime() - date2.getTime() > 100000)
break;
try {
const response = await Item.find({name: name_to_find});
var mergi = JSON.parse(JSON.stringify(response));// doesn t work
//if (response['status'] == 1)
if(response.status == 1){
console.log("200");
break;
}
else {
console.log(JSON.stringify(response));
console.log(response.status);
console.log(mergi.name);
}
}
catch (err) {
console.error(err);
console.log(name_to_find);
}
}
}
myFetch();
this is the schema:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const ItemSchema = new Schema({
status: {
type: Number,
required: true
},
name: {
type: String,
required: true
}
});
module.exports = Item = mongoose.model('item', ItemSchema);
and this is the output:
[{"_id":"60fc235414d05a001a5fa630","status":1,"name":"Copac","__v":0}]
undefined undefined
As u see, it is indeed 1 and should exit the loop but it doesn t.
nothing here helped either link.
Ok so the problem was that mongoose treats the result of .find() function as an array and I should have casted it with results[0] or use .findOne(). I chose the former. The answer was actually in the link provided but u have to scroll a bit for it. Tell me if u want me to delete this

Mongoose/mongo store findOne into variable

I need do findOne and take the result to a variable to insert in another collection.
The problem are inside function i have results, and outsid just empty.
var rest = [];
function queryCollection(collection, callback, city){
collection.findOne({cidade: city},function(err, result) {
k = 1
if (err) {
console.log(err);
} else if (k > 0) {
console.log(result)//here have result
rest.push(result);
callback();
console.log(rest)//here have result
k - 1;
}
});
}
queryCollection(Location, function(){
console.log(rest);
}, 'FATIMA');
console.log('out', rest)//HERE IS EMPTY, HOW CAN I USE REST VALUE?
Here are a couple things to note about javascript and some examples of using mongoose. I hope it helps!
Javascript is not necessarily going to run your code in the order that your statements appear in the source code.
Any operation that you perform in mongoose that connects to the database is going to be asynchronous.
consider this contrived example loosely based on your question:
'use strict';
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
const Schema = mongoose.Schema;
const testSchema = new Schema({
city: String
});
const Test = mongoose.model('test', testSchema);
const test = new Test({
city: 'Tucson'
});
function queryCollection() {
console.log('queryCollection');
Test.findOne({ city: test.city }, function (err, result) {
console.log('findOne callback');
if (err) {
return console.error(err);
} else {
console.log(`result: ${result}`);
}
});
}
test.save(function (err, doc) {
console.log('save callback');
if (err) { return console.error(err); }
queryCollection();
});
console.log('console.log');
shell output
stack: node ./49738064.js
console.log
save callback
queryCollection
findOne callback
result: { _id: 5acbd819e89388d34ea8d6a1, city: 'Tucson', __v: 0 }
^C
stack:
Notice that the very first output from the script is 'console.log'
This is due to the fact that test.save() and Test.findOne() are asynchronous operations, meaning that they take time to complete that javascript isn't going to wait around for.
Functions that read from or write to the network, disk, or database are frequently referred to as 'async' unless they are documented/named otherwise. You need to account for this mixture of sync and async operations in your code.
Historically, this was accomplished with callbacks. If there was some operation that was async, a callback was passed into that operation that would handle the results from the async call.
returning to our contrived example from above:
'use strict';
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
const Schema = mongoose.Schema;
const testSchema = new Schema({
city: String
});
const Test = mongoose.model('test', testSchema);
const test = new Test({
city: 'Tucson'
});
function queryCollection(done) {
Test.findOne({ city: test.city }, function (err, result) {
if (err) {
return console.error(err);
} else {
done(result);
}
});
}
test.save(function (err, doc) {
if (err) { return console.error(err); }
queryCollection(console.log);
});
As you can see I passed in the console.log function as the callback to the async query operation. You can write your own functions and pass them in as well.
shell output
stack: node ./49738064.js
{ _id: 5acbd819e89388d34ea8d6a1, city: 'Tucson', __v: 0 }
^C
stack:
A more modern way of dealing with async operations is to use promises. A Promise is an object that represents the future outcome of the operation you are performing.
All of the mongoose async operations return a promise ( or a promise-like object ) by default if you don't pass in a callback. Because of this, they can be used very effectively with async/await.
here is an example of using async await with mongoose:
'use strict';
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
const Schema = mongoose.Schema;
const testSchema = new Schema({
city: String
});
const Test = mongoose.model('test', testSchema);
const test = new Test({
city: 'Tucson'
});
function queryCollection() {
return Test.findOne({ city: test.city });
}
async function run () {
await Test.remove({}); // remove all existing documents
await test.save(); // save the new document
let found = await queryCollection();
console.log(found);
}
run();
shell output
stack: node ./49738064.js
{ _id: 5acbdf5dd8dc39d6065c820c, city: 'Tucson', __v: 0 }
^C
stack:

Mongoose: Saving ref of another document into an array of objects document returns empty array

I'm trying to add specific document's ref id into another document's array of object. So that I can use populate method. But somehow the array remains empty even after pushing the ref.
Please help me. Thank you in advance.
Here is list.js:
const mongoose = require('mongoose');
const User = require('./user');
const Task = require('./task');
const Schema = mongoose.Schema;
// User Schema
const ListSchema = mongoose.Schema({
item:{
type: String,
required: true
},
purpose:{
type: String,
required: true
},
user: {
type: Schema.ObjectId,
ref:"User"
},
deadline:{
type: Date,
default: null
}
});
const List = module.exports = mongoose.model('List', ListSchema);
task.js:
const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const List = require('./list');
// User Schema
const TaskSchema = mongoose.Schema({
task:{
type: String,
required: true
},
list: [{
type: Schema.Types.ObjectId,
ref:'List'
}]
});
const Task = module.exports = mongoose.model('Task', TaskSchema);
Here is how I create and save new task instance:
const express = require('express');
const mongoose = require('mongoose');
const router = express.Router();
let List = require('../models/list');
let User = require('../models/user');
let Task = require('../models/task');
router.post('/add', isAuthenticated, function(req,res){
var tasker = req.body.task, listshash= [];
var startIndex = 0, index;
var x,y,listid= [];
while ((index = tasker.indexOf("#", startIndex)) > -1) {
var ind = tasker.indexOf(" ", index);
if(ind == -1)
listshash.push(tasker.substring(index+1));
else
listshash.push(tasker.substring(index+1,ind));
startIndex = index + 1;
}
//Instance of task
var taskIns = new Task({task: req.body.task});
List.find({user: req.session.passport.user}, function(err, lists){
for(x in lists){
for(y in listshash){
if(lists[x].item.toLowerCase().replace(/\s/g,'') == listshash[y]){
//lists[x] contains the document "list" that I want to store as
//ref in list property array of task
taskIns.list.push(lists[x]);
//console.log(taskIns.list.push(lists[x]));
}
}
}
});
taskIns.save(function(err, doc){
if(err) res.json(err);
else {
console.log("Saved");
res.redirect('/lists');
}
});
});
module.exports = router;
This is how the database collection of tasks look like after inserting data:
See the data
You should have to use async npm
List.find({user: req.session.passport.user}, function(err, lists){
async.each(lists ,function(x,callback){
async.each(listhash, function(y,callback){
if(x.item.toLowerCase().replace(/\s/g,'') == y){
taskIns.list.push(x);
}
});
});
//After that you can do save the data
taskIns.save(function(err, doc){
if(err) res.json(err);
else {
console.log("Saved");
res.redirect('/lists');
}
});
});
It's not saving the refs because you are dealing with asynchronous functions; you are pushing the lists within the find() callback and the taskIns model save event happens before the push.
You can move the taskIns save operation within the find() callback or use promises to tackle the issue.
For example, using callbacks:
List.find({ user: req.session.passport.user}, (err, lists) => {
const taskIns = new Task({task: req.body.task});
for(x in lists) {
for(y in listshash) {
if(lists[x].item.toLowerCase().replace(/\s/g,'') == listshash[y]) {
taskIns.list.push(lists[x]);
}
}
}
taskIns.save((err, doc) => {
if(err) res.json(err);
else {
console.log("Saved");
res.redirect('/lists');
}
});
});

Mongoose not calling callbacks of save() and find()

I'm not getting into the callbacks for either the save() or find() methods. It's also not getting into the callback for mongoose.connect. Any idea why?
Here's my code:
mongoose.connect("mongodb://localhost/blah", {
useMongoClient: true,
}, function (err) {
console.log('connect to database'); // Not called
if (err) {
throw err;
}
});
var Schema = mongoose.Schema
var User = new Schema({
author : String
, type : String
});
var MyUserModel = mongoose.model('User', User); //create and access the model User
var u = new MyUserModel();
u.author = 'authorname';
u.save(function(err){
console.log("saved") // not called
if (err) console.log(err);
});
MyUserModel.find({}, function (err,docs) {
console.log("found"); // not called
console.log(docs);
});
You need to "await" the connection before doing anything. It is an "async" function, and like all async functions you need to await their resolution before continuing other code that depends on the result. In this case the "connection" is going to be needed by later actions with the database.
Best done in modern environments with async/await:
// setup part
var Schema = mongoose.Schema
var User = new Schema({
author : String
, type : String
});
var MyUserModel = mongoose.model('User', User); //create and access the model User
// async parts - wrapped in a self executing closure with the async keyword
(async function() {
try {
const conn = await mongoose.connect("mongodb://localhost/blah", {
useMongoClient: true,
});
console.log('database connected');
// Add a user and wait for it to save
var u = new MyUserModel();
u.author = 'authorname';
await u.save();
console.log("saved");
// find the documents and await that too
let docs = await MyUserModel.find();
console.log(JSON.stringify(docs,undefined,2));
} catch(e) {
console.error(e);
} finally {
mongoose.disconnect();
}
})();
Or by chaining promises in older nodejs versions:
// setup part
var Schema = mongoose.Schema
var User = new Schema({
author : String
, type : String
});
var MyUserModel = mongoose.model('User', User); //create and access the model User
// Async part
mongoose.connect("mongodb://localhost/blah", { useMongoClient: true })
.then( () => {
var u = new MyUserModel();
u.author = 'authorname';
return u.save();
})
.then( () => MyUserModel.find() )
.then( docs => {
console.log(JSON.stringify(docs,undefined,2));
mongoose.disconnect();
})
.catch( e => console.error(e) );
Bottom line is that you need to wait for completion of all async calls and .connect() is now an async call in modern mongoose releases. It really should have always been handled like this, but now you "must" handle like this.

Mongoose not returning document from mongo

I am trying to get Mongoose to query my mongo instance for a specific document (using the _id attribute). I currently have the following route:
router.get('/document/', function (req, res) {
var getDocuments = function (callback) {
var options = { server: { socketOptions: { keepAlive: 1000 } } };
var connectionString = 'mongodb://user:password#server:27017/db';
var pID = req.query.id;
pID = pID.trim();
console.log(pID);
var documentArray = [];
// Connected handler
Mongoose.connect(connectionString, function (err) {
var db = Mongoose.connection.useDb('db');
var pCollection = db.collection("collection");
//grab all items from pCollection
pCollection.find({ '_id': pID }, function (error, pDocument) {
if (error) {
console.log(error);
}
if (pDocument) {
// res.send(JSON.stringify(pDocument));
console.log(pDocument);
}
else {
console.log("nothing");
}
});
db.close();
});
};
getDocuments(function () {
});
});
The result returned is not a json document and does not seem to return a usable value. What am I doing wrong? Thanks for any help in advance!
EDIT:
I went back and changed the route to the following:
router.get('/document/', function (req, res) {
var pID = req.query.id;
pID = pID.trim();
console.log(pID);
Document.findById(pID, function (error, document) {
if (error) {
console.log(error);
}
else {
console.log(document);
}
});
});
I also created the following model:
var mongoose = require('mongoose');
var DocumentSchema = require('../schemas/documents');
var Document = mongoose.model('documents', DocumentSchema, 'Documents');
module.exports = Document;
And used the following schema:
var mongoose = require('mongoose');
var DocumentSchema = new mongoose.Schema({
documenttPK: String,
locationID: String,
docName: String,
SerialNumber: String,
documentID: String,
dateEntered: Date,
activeFlag: Boolean
});
module.exports = DocumentSchema;
My app.js makes a single call to a db file:
var mongoose = require('mongoose');
mongoose.connect('mongodb://user:password#server:27017/db');
But the result is still null. Is something wrong with the above code?

Resources