Mongoose/mongo store findOne into variable - node.js

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:

Related

Why mongoose doesn't update?

I am learning Node.js. I am stuck in this Mongoose section because of this problem.
findById(id) doesn't get my result back and update() doesn't work. I don't know why..
const mongoose = require("mongoose").set("debug", true);
mongoose
.connect("mongodb://localhost/exercise")
.then(() => console.log("Connected Successfully!"))
.catch(err => console.error("Error: ", err.message));
const courseSchema = new mongoose.Schema({
tags: [String],
date: { type: Date, default: Date.now() },
name: String,
author: String,
isPublished: Boolean,
price: Number
});
const Courses = mongoose.model("Course", courseSchema);
async function updateCourse(id) {
const course = await Courses.findById(id);
course.author = "Muhib";
const result = await course.save();
console.log(result);
}
updateCourse("5a68fde3f09ad7646ddec17e");
// console.log(Courses);
I get this error :
TypeError: Cannot set property 'author' of null
This is a snip of my record:
btw find() works..
Thanks in advance :)
I solved the issue..
there is nothing wrong with the code...
first thing first WHY IT DOESN'T WORK
This problem occurs with the imported collections only. Because the
imported collection contains _id property as a string
for example {"_id":"5a6900fff467be65019a9001","tags":["angular","frontend"],"date":"2018-01-24T21:56:15.353Z","name":"Angular Course","author":"Mosh","isPublished":true,"price":15,"__v":0}
but what mongoose need is _id wrapped in ObjectId
SOLUTION
when importing json object always make sure that _id is not a string but an object with a $oid as a key and _id as a value for example
{"_id":"5a6900fff467be65019a9001","tags":["angular","frontend"],"date":"2018-01-24T21:56:15.353Z","name":"Angular Course","author":"Mosh","isPublished":true,"price":15,"__v":0}
should be {"_id":{"$oid":"5c91a66d079f4807847fcde3"},"tags":["angular","frontend"],"date":"2018-01-24T21:56:15.353Z","name":"Angular Course","author":"Mosh","isPublished":true,"price":{"$numberInt":"15"},"__v":{"$numberInt":"0"}}
Try to add try/catch. I'm not sure course..save() work without err
async function updateCourse(id) {
try {
const course = await Courses.findById(id);
course.author = "Muhib";
const result = await course.save();
console.log(result);
} catch (err) {
console.log('err' + err);
}
}
Edit 1: I found a solution a this post
async function updateCourse(id) {
try {
const course = await Courses.findById(id).exec();//fixed at here
course.author = "Muhib";
const result = await course.save();
console.log(result);
} catch (err) {
console.log('err' + err);
}
}
Another solution using findOneAndUpdate
try {
var course = await Courses.findOneAndUpdate(
{"id" : id},
{$set: {"author" : "Muhib"}},
{new : true}
);
res.status(200).json(course);
} catch (error) {
console.log('err' + err);
}

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.

sailsjs use mongodb without ORM

I want to use mongodb with sails but without any ORM. So below is my service to connect mongodb.
Service:
//DbService.js
const MongoClient = require('mongodb').MongoClient;
module.exports = {
db:function(req, res){
var connect=MongoClient.connect("mongodb:***********").then(function (err, database) {
if(err) console.log(err);
else{
database=database.db('*****');
return connect;
}
});
}
}
After connection i have called it in controller, But getting TypeError: Cannot read property 'then' of undefined.
controller:
//HomeControlelr.js
module.exports = {
index:function(req, res){
DbService.db().then(function(err,db) {
console.log(db);
})
}
};
First npm i mongodb because you'll need to wrap any ID's with new ObjectID(idStr).
Then you can do this:
const collection = Pet.getDatastore().manager.collection(Pet.tableName);
const res = await collection.find({ name: { $regex: /blue/ } });
const dataWithObjectIds = await res.toArray();
const dataWithIds = JSON.parse(JSON.stringify(rawDataArr).replace(/"_id"/g, '"id"'));
I created a helper function to do all of this for us:
/**
* Use by chaining as if you were acting on a collection. So can use .find .aggregate etc.
* Returns json searializable data.
*
* #param {class} model A model
* #param {number} cnt - Number of chains on this, so I know when it reached the end
*/
function nativeMongoQuery(model, cnt) {
const collection = model.getDatastore().manager.collection(model.tableName);
let callCnt = 0;
let req;
const proxy = new Proxy({}, {
get: (_, method) => (...args) => {
if (!req) req = collection[method](...args);
else req = req[method](...args);
callCnt++;
if (callCnt === cnt) {
return (async function() {
const rawDataArr = await req.toArray();
return JSON.parse(JSON.stringify(rawDataArr).replace(/"_id"/g, '"id"'));
})();
} else {
return proxy;
}
}
});
return proxy;
}
module.exports = nativeMongoQuery;
I don't like the JSON parse and stringify and global replace. But if I don't do the stringify, then mongo _id's are all ObjectIds.
Use it like this:
const { ObjectId } = require('mongodb');
function makeObjectId(id) {
return new ObjectId(id);
}
const ownerIds = ['5349b4ddd2781d08c09890f4', '5349b4ddd2781d08c09890f5']
const ownerObjectIds = ownerIds.map(makeObjectId);
await nativeMongoQuery(Pet, 2).find({ owner: { $in: ownerObjectIds } }).sort({ dueAt: 1 });
Here is another example:
const mostRecentlyCreatedPets = await nativeMongoQuery(Pet, 1).aggregate([
{ $match: { owner: { $in: ownerObjectIds } } },
{ $sort: { createdAt: -1 } },
{ $limit: 1 }
]);
The cnt argument tells you how many things you have chained off of there.
As you can see in docs MongoClient.connect() doesn't return Promise object. Instead of this use callback function
module.exports = {
db:function(){
var connect = MongoClient.connect("mongodb:***********", function (err, database) {
//...
}
});
}
}
btw. Your call DbService.db function in controller will also fail, cuz your service function also doesn't return Promise
Before you go on, read something about Promises and callback functions

Return partial documents from Node API querying Mongo

I have a Node API that is querying my Mongo database.
I'm able to return all documents within a collection. Now, I want to return partial documents.
My model (i.e. Player.js) looks like this:
var mongoose = require('mongoose');
// Create schema for players
var playerSchema = mongoose.Schema(
{
player_name:String,
goals:Number,
clubs:
{
club_name:String,
season:String
}
}
);
var Player = module.exports = mongoose.model('Player',playerSchema);
// get all players
module.exports.getPlayers = function(callback, limit){
Player.find(callback).limit(limit);
};
My application looks like this:
//...
app.get('/api/players',function(req,res){
Player.getPlayers(function(err,players){
if(err){
throw err;
}
res.json(players);
});
});
//...
I'd like to query all documents within my players collection but return only player_name when I go to ".../api/players/player_names"
I thought adding the code below would work but it doesn't...
This on my model:
// [previous code]
// get all player names
module.exports.getPlayerNames = function(callback,limit){
Player.find(callback,player_name:1).limit(limit)
}
This on my app.js:
//...
app.get('api/players/player_names',function(req,res){
Player.getPlayerNames(function(err,players){
if(err){
throw err;
}
res.json(players);
});
});
//...
You'll probably want something that looks like this:
var mongoose = require('mongoose');
// Create schema for players
var playerSchema = mongoose.Schema(
{
player_name:String,
goals:Number,
clubs:
{
club_name:String,
season:String
}
}
);
//Its most common to pass the callback last, as this is what others do
playerSchema.statics.getPlayers = function(limit, callback){
this.find({})
.select('player_name')
.limit(limit)
.exec(callback);
};
module.exports = mongoose.model('Player',playerSchema);
Which will return results like [{_id: ObjectId, player_name: "player1"}, ...].
If you want to return just an array of player names:
var mongoose = require('mongoose');
// Create schema for players
var playerSchema = mongoose.Schema(
{
player_name:String,
goals:Number,
clubs:
{
club_name:String,
season:String
}
}
);
//Its most common to pass the callback last, as this is what others do
playerSchema.statics.getPlayers = function(limit, callback){
this.find({})
.select('player_name')
.limit(limit)
.exec(function(err, docs) {
if(err) return callback(err); //first argument should be reserved for passing any errors
callback(null, docs.map(function(doc) {
return doc.player_name;
});
});
};
module.exports = mongoose.model('Player',playerSchema);

How could I bind `save` method using `Q` with mongoose

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.

Resources