Mongoose can't find recent saved - node.js

I am trying to save the IP address of the client who connects to my script.
However, while I am not getting any errors, when I check the collection it is empty.
index.js (main app)
const Listeners = require('mongoose').model('listeners');
const userData = {"ipaddress":ip}
const Listener = new Listeners(userData);
Listener.save(function (err, userData) {
if (err) return console.error(err);
});
Mongoose index.js
const mongoose = require('mongoose');
module.exports.connect = (uri) => {
mongoose.connect(uri, {useCreateIndex: true, useFindAndModify: false , useNewUrlParser: true, useUnifiedTopology: true });
// plug in the promise library:
mongoose.Promise = global.Promise;
mongoose.connection.on('open',function() {
console.log('Mongoose connected. what did you think');
});
mongoose.connection.on('error', (err) => {
console.error(`Mongoose connection error: ${err}`);
process.exit(1);
});
// load models
require('./listener');
};
My listener file
const mongoose = require('mongoose');
// define the User model schema
const ListenerSchema = new mongoose.Schema({
ipaddress: {
type: String,
// index: { unique: true }
},
station: String,
start_time:{
type: Date
},
end_time:{
type: Date
}
}, { collection: 'listeners'});
/**
* The pre-save hook method.
*/
ListenerSchema.pre('save', function saveHook(next) {
const Listener = this;
console.log(this)
});
module.exports = mongoose.model('listeners', ListenerSchema);
when I run it I get { _id: 5e2bf98549ae2d5d6da52475, ipaddress: '127.0.0.1' }
However when I open the mongodb collection I see nothing.
There seems to not be an error, but there must be?

💡 The only one reason why you can't save your data into your collection it's because this code in your listener file.
👨‍🏫 Try to comment this code below 👇:
/**
* The pre-save hook method.
*/
ListenerSchema.pre('save', function saveHook(next) {
const Listener = this;
console.log(this)
});
👨‍🏫 or, the second option is add next function in there. So, your code will looks like this code below 👇:
/**
* The pre-save hook method.
*/
ListenerSchema.pre('save', function saveHook(next) {
const Listener = this;
console.log(this);
// add next function below
next();
});
And now, you can try again and I'm sure, you can see the collection in your mongodb.
I hope it's can help you 🙏.

Related

Why am I not able query ( User.findOne() ) my mongoDB after setting up a Schema?

I can't seem to query my MongoDB after setting up the MongoDB schema. I don't understand where I am going wrong with my MongoDB schema, any help is appreciated!
I WAS earlier successful in querying my mongodb before creating a schema, using queries like this:
const uri = process.env.MONGODB_URI;
const client = new MongoClient(uri);
const result = await client.db("inride_adverts").collection("adverts").findOne({OrgEmail: OrgEmailToSignIn});
However, according to a YouTube tutorial am following (10:40 mins), after setting up a mongodb schema, I am NOT successful in using the following query to interact with my mongodb:
User.findOne( {OrgEmail: signUpEmail} ).exec();
Find below my simple User Schema:
./models/User.js
import { mongoose} from 'mongoose';
const UserSchema = new mongoose.Schema({
OrgName: {
type: String,
required: true
},
OrgEmail: {
type: String,
required: true
},
OrgPwd: {
type: String,
required: true
}
}, { collection: 'adverts' });
export const User = mongoose.model('User', UserSchema);
Also, find below my server.js file
./server.js
import express from 'express';
import { User } from './models/User.js';
import mongodb from 'mongodb';
import { mongoose} from 'mongoose';
mongoose.connect(db, { useNewUrlParser: true })
mongoose.connect(db, { useNewUrlParser: true })
.then( () => console.log("MongoDB Connected..." ))
.catch(error => console.log(err))
app.route('/advertiserRegister')
.post( async (req, res) => {
let formData = req.body;
let signUpName = formData.signUpName;
let signUpEmail = formData.signUpEmail;
let signUpPwd = formData.signUpPwd;
console.log("signUpName: " +signUpName);
console.log("signUpEmail: " +signUpEmail);
console.log("signUpPwd: " +signUpPwd);
if(signUpPwd !== signUpPwdConfirm){
console.log("Passwords arent EQUAL!");
return 0;
} else {
try{
console.log("signUpEmail>>> : " + signUpEmail );
// Validation passed!
const testing = await User.findOne( {OrgEmail: signUpEmail} ).exec();
console.log("testing >>> : " ,testing );
res.redirect('/advertiserLogin')
} catch {
//console.error('error', code);
console.log("Error logging in ");
res.redirect('/advertiserRegister')
};
}
});
The server.js file yields:
MongoDB Connected...
signUpName: Merc Enterprise LTD
signUpEmail: hundredcent.a#gmail.com
signUpPwd: 555
signUpEmail>>> : hundredcent.a#gmail.com
testing >>> : **null**
Error logging in
Turns out that the reason I was NOT able to query my collection was due to the fact that I was unknowingly querying the incorrect collection being: adverts under the incorrect database being: inride_adverts.
I came to understand and realise that In my ./models/User.js, the mongoose.model('User', UserSchema); code creates a new database in Atlas MongoDB called test and creates a totally new collection called User.
Having understood this, I am able to populate and query this collection successfully!

Mongoose schema virtual attribute always returns undefined

I was trying to use mongoose schema virtual functions to implement flags, But I could not make them work The mongoose database is hosted on mongodb altas. I have tried deleting the whole collection and staring again.
Here is a simplified example:
Let us say I have a basic User Schema:
const mongoose = require('mongoose')
const UserSchema = new mongoose.Schema({
name: String,
email: String
}, {toObject: {virtuals: true, getters: true}})
UserSchema.virtual('status').get(() => {
return this.name
})
const User = mongoose.model('User', UserSchema)
module.exports = {
User,
UserSchema
}
In app.js I have the following:
const mongoose = require("mongoose");
const express = require("express");
const {User} = require("./models/User")
const app = express();
app.use(express.urlencoded({extended: true}));
app.use(express.json());
mongoose.connect(process.env.DB_URL, {
useNewUrlParser: true,
useUnifiedTopology: true
});
User.findById("600ae8001931ad49eae40c03", (err, doc) => {
console.log(doc.status)
})
const port = process.env.PORT || 5000;
app.listen(port, () => {
console.log(`server at http://localhost:${port}`);
});
I made sure the id exists, but the result is always undefined. What am I doing wrong here?
based on mongoose documentation
Do not declare methods using ES6 arrow functions (=>). Arrow functions
explicitly prevent binding this, so your method will not have access
to the document and the above examples will not work
so you can not use arrow function in get, do like this :
UserSchema.virtual('status').get(function() {
return this.name
})
It's because of how you are declaring your function being passed to get. The way you are doing it is changing the this reference.
UserSchema.virtual('status').get(function() {
return this.name
})
The above would maintain it
To future users peaple that looking for the answer, beside this answer - if it's not solve the problem and still get undefined -
the problem maybe because you didn't selec the attribute that used in the virtual middleware
Our Schema
const monthSchema = new mongoose.Schema(
{
name: String,
numOfDays: Number,
},
{ toJSON: { virtuals: true }, toObject: { virtuals: true } }
);
if you use numOfDays in virtual middleware to calculate the weeks like this:
MonthSchema.virtual('NumOfWeeks').get(function() {
return this.numOfDays / 7
})
this you must be sure that numOfDays are selected
const user = await User.find().select('name'); // this.numOfDays will be undifined
const user = await User.find().select('-numOfDays'); // this.numOfDays will be undifined
const user = await User.find().select('name numOfDays'); // this.numOfDays will be not undifined
I spent whole day to figure this, may be this will help someone

Mongoose - multiple database connections

I want to understand how to switch between databases within mongoose global promise connection.
My current connection is established this way app.ts
import * as mongoose from 'mongoose';
...
try {
await mongoose.createConnection(`mongodb://localhost:27017/db1`, {
useNewUrlParser: true,
})
console.log("Connected")
} catch (error) {
console.log(error)
}
And then I am accessing it in different files some.model.ts
import { Schema, Document, model } from 'mongoose';
const SomeSchema: Schema = new Schema({
name: { type: String, required: true },
owner: { type: string, required: true }
});
export default model('Some', SomeSchema);
According to documentation.
So far we've seen how to connect to MongoDB using Mongoose's default connection. At times we may need multiple connections open to Mongo, each with different read/write settings, or maybe just to different databases for example. In these cases we can utilize mongoose.createConnection() which accepts all the arguments already discussed and returns a fresh connection for you.
const conn = mongoose.createConnection('mongodb://[username:password#]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]', options);
I can create multiple database connections like this
try {
const db1 = await mongoose.createConnection(`mongodb://localhost:27017/db1`, {
useNewUrlParser: true,
})
const db2 = await mongoose.createConnection(`mongodb://localhost:27017/db2`, {
useNewUrlParser: true,
})
console.log("Connected")
} catch (error) {
console.log(error)
}
I can see both connection in console.log(mongoose.connections)
But how can I specify what database should be used for the Model in some.model.ts?
import { Schema, Document, model } from 'mongoose';
const SomeSchema: Schema = new Schema({
name: { type: String, required: true },
owner: { type: string, required: true }
});
export default SPECIFY_DATABASE.model('Some', SomeSchema);
I have found other questions like this but there are connections created "localy", I need to use mongoose connection across many different files.
Thank you for answers, if you need more explanation please let me now.
You need to actually return the connection, and then register a given model to each of the connections. To clarify, you need:
something to create a (named, specific) connection
schemas
you create models by registering schemas to the given connections,
you also need something to orchestrate it.
Example, lets have a "db.js" file (I call mine "repo.js" usually) with a single export, a function that returns the initialized database Promise.
You'd use it by importing the function and awaiting for the db.
I have a bit of a longer example, so error handling etc is ommited for brevity.
import { createConnections } from './create-connections';
import { UsersSchema } from './users-schema';
import { PostsSchema } from './posts-schema';
let db: any;
export function getDatabase(): Promise<any> {
if (this.db) return Promise.resolve(db);
return createDatabases();
}
async function createDatabases() {
const { db1, db2 } = await createConnections('mongodb://localhost/db1', 'mongodb://localhost/db2');
const UserModel = db1.model('users', UserSchema);
const PostModel = db2.model('posts', PostSchema);
db = {
UserModel,
PostModel,
// also if you need this
connections: {
db1,
db2,
}
}
return db;
}
Now, I've used './create-connections' here, which is almost what you have:
// create-connection.js
const { createConnection } = require('mongoose');
// You create connections by calling this function and giving it the URL to the server
export function createConnections(url1, url2) {
const db1 = await createConnection(url1);
const db2 = await createConnection(url2);
return {
db1,
db2
}
}
Now, let's say you have two models: users and posts, let's have their schemas.
// users schema
import { Schema, Document } from 'mongoose';
export const UserSchema: Schema = new Schema({
name: { type: String, required: true },
});
// posts schema
import { Schema, Document } from 'mongoose';
export const PostSchema: Schema = new Schema({
text: { type: String, required: true },
owner: { type: SchemaID, required: true }
});
So now you need to bind it all in that fdirst file.
But how to use it? As I've said, since it's async, you always import it and use it as a simple async getDB:
// some controller, route handler, service etc.
import { getDatabase } from './get-database';
router.get('/users', async (req, res) => {
const User = await getDatabase().UserModel;
const users = await User.find();
return res.json(users);
});
router.post('/posts', async (req, res) {
const { text } = req.body;
const owner = req.user.id;
const Post = await getDatabase().PostModel;
const post = await Post.create({ text, owner });
return res.json(post);
});

Mongoose.js: isModified flag for attribute with default value

I have a model with a default generated value that doesn't change throughout the document lifetime except in one special case.
A document may get marked as deleted using doc.update({_id: doc._id, deleted_at: new Date()}, {overwrite: true})
In a very special case the document may be "revived" - looked up by it's id and being worked with again afterwards.
In a pre-save hook I need to perform some action (for example store a document in another collection) whenever the document is created or revived.
Consider following simplified code:
'use strict';
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');
var someSchema = mongoose.Schema({
immutable: {
type: String,
default: function () {
return 'SomeVeryRandomValue';
}
}
});
someSchema.pre('save', function (next) {
if (this.isNew || this.isModified('immutable')) {
console.log('Processing pre-save hook!');
}
next();
});
var SomeModel = mongoose.model('SomeModel', someSchema, 'test');
mongoose.connection.once('open', function (err) {
var testDoc = new SomeModel({});
console.log('New: %j', testDoc.toObject());
testDoc.save(function(err) {
console.log('Initial saved: %j', testDoc.toObject());
testDoc.update({_id: testDoc._id}, {overwrite: true}, function (err) {
// at this point using mongo console:
// > db.test.findOne()
// { "_id" : ObjectId("5617b028bf84f0a93687cf67") }
SomeModel.findById(testDoc.id, function(err, reloadedDoc) {
console.log('Reloaded: %j', reloadedDoc.toObject());
console.log('reloaded isModified(\'immutable\'): %j', reloadedDoc.isModified('immutable'));
reloadedDoc.save(function(err) {
console.log('Re-saved: %j', reloadedDoc);
mongoose.connection.close();
});
});
});
});
});
And the script runtime output:
$ node mongoose-modified-test.js
New: {"_id":"5617b64c5376737b46f6bb98","immutable":"SomeVeryRandomValue"}
Processing pre-save hook!
Initial saved: {"__v":0,"_id":"5617b64c5376737b46f6bb98","immutable":"SomeVeryRandomValue"}
Reloaded: {"_id":"5617b64c5376737b46f6bb98","immutable":"SomeVeryRandomValue"}
reloaded isModified('immutable'): false
Re-saved: {"_id":"5617b64c5376737b46f6bb98","immutable":"SomeVeryRandomValue"}
The immutable is not marked as modified and IMHO it should - original document had no value for that attribute.
A work-around solution is to remove the default value for immutable attribute and define pre-validate hook like this one:
someSchema.pre('validate', function (next) {
if (this.isNew || !this.immutable) {
this.immutable = 'SomeVeryRandomValue';
}
next();
});
This is not exactly what I need because the value won't be generated until I try to validate/save the document. The pre/post-init hooks are not executed on new SomeModel({}) so I can't use those.
Should I open an issue for mongoose.js?
this.$isDefault('immutable') can be used instead.
someSchema.pre('save', function (next) {
if (this.isNew || this.$isDefault('immutable')) {
console.log('Processing pre-save hook!');
}
next();
});
Output of the script with updated pre-save hook:
$ node --harmony mongoose-modified-test.js
New: {"_id":"56276f0c1a2f17ae7e0a03f7","immutable":"SomeVeryRandomValue"}
Processing pre-save hook!
Initial saved: {"__v":0,"_id":"56276f0c1a2f17ae7e0a03f7","immutable":"SomeVeryRandomValue"}
Reloaded: {"_id":"56276f0c1a2f17ae7e0a03f7","immutable":"SomeVeryRandomValue"}
Processing pre-save hook!
Re-saved: {"_id":"56276f0c1a2f17ae7e0a03f7","immutable":"SomeVeryRandomValue"}
Thanks to #vkarpov15 for clarification.

Getting list of all databases with Mongoose

There are some similar questions but all of them involves using the MongoDB NodeJS driver instead of Mongoose ODM.
I read the docs but couldn't find such functionality.
You can't directly get the list from the connection provided by mongoose, but it's easy to do with the mongo Admin object as it contains a function called listDatabases:
var mongoose = require('mongoose')
, Admin = mongoose.mongo.Admin;
/// create a connection to the DB
var connection = mongoose.createConnection(
'mongodb://user:pass#localhost:port/database');
connection.on('open', function() {
// connection established
new Admin(connection.db).listDatabases(function(err, result) {
console.log('listDatabases succeeded');
// database list stored in result.databases
var allDatabases = result.databases;
});
});
A very modern approach to get list of all mongo databases using mongoose (version 6.10.*) is to Create a mongoose connection to connect to Mongo's admin database and make sure you have an admin user.
Mongoose object is a very complex object. To list the db's :
const connection = `mongodb://${encodeURIComponent(username)}:${encodeURIComponent(password)}#${hostname}:${port}/admin`
mongoose is a very complex object with promises for executing several functions. to list the db's :
mongoose.connect(connection, { useNewUrlParser: true , useUnifiedTopology: true }).then( (MongooseNode) => {
/* I use the default nativeConnection object since my connection object uses a single hostname and port. Iterate here if you work with multiple hostnames in the connection object */
const nativeConnetion = MongooseNode.connections[0]
//now call the list databases function
new Admin(nativeConnetion.db).listDatabases(function(err, results){
console.log(results) //store results and use
});
})
Result:
{ databases:
[ { name: 'admin', sizeOnDisk: 184320, empty: false },
{ name: 'config', sizeOnDisk: 73728, empty: false },
{ name: 'local', sizeOnDisk: 73728, empty: false },
{ name: 'test', sizeOnDisk: 405504, empty: false } ],
totalSize: 737280,
ok: 1 }
If someone is looking for answers from the latest version of Mongoose and Mongodb, the below code can be used.
import mongoose from 'mongoose';
mongoose.set('strictQuery', true);
mongoose.connect('mongodb://localhost:27017/mydb', {
useNewUrlParser: true,
useUnifiedTopology: true,
});
const db = mongoose.connection;
// Check DB Connection
db.once('open', () => {
(async () => {
const data = await mongoose.connection.db.admin().command({
listDatabases: 1,
});
console.log(data);
})();
console.log('Connected to MongoDB');
});
// Check for DB errors
db.on('error', (err) => {
console.log('DB Connection errors', err);
});
export default mongoose;
If you want to get the database list on your other functions, make sure the connection is established first and also make sure the user has admin access and then just do the below query. This is a sample from my API router.
// Get all databases
router.get('/database/get', async (req, res) => {
try {
const data = await mongoose.connection.db.admin().command({
listDatabases: 1,
});
if (data && data !== null) {
res.status(200).send({ data: data });
return;
}
res.status(200).send({ data: null, message: 'Data not found' });
} catch (e) {
// eslint-disable-next-line no-console
console.log(e);
res.status(500).send(e.message);
}
});
Try running this code. Original take from Gist.

Resources