Mongoose ODM use - node.js

I read an article in this link http://theholmesoffice.com/mongoose-and-node-js-tutorial/
here there is a code:
exports.teamlist = function(gname,callback){
db.once('open', function(){
var teamSchema = new mongoose.Schema({
country: String,
GroupName: String
});
var Team = db.model('Team', teamSchema);
Team.find({'GroupName':gname}, function (err, teams) {
if(err){
onErr(err,callback);
}else{
mongoose.connection.close();
console.log(teams);
callback("",teams);
}
})// end Team.find
});// end db.once open
};
Here it calls db.once method whereas in other places its used like this
var mongoose = require('mongoose')
,Schema = mongoose.Schema
,ObjectId = Schema.ObjectId;
var postSchema = new Schema({
thread: ObjectId,
date: {type: Date, default: Date.now},
author: {type: String, default: 'Anon'},
post: String
});
module.exports = mongoose.model('Post', postSchema);
In the router part its used like this
exports.show = (function(req, res) {
Thread.findOne({title: req.params.title}, function(error, thread) {
var posts = Post.find({thread: thread._id}, function(error, posts) {
res.send([{thread: thread, posts: posts}]);
});
})
});
And in the app.js there is
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/norum');
I dont understand why these two have different approach and which one is the better one and why? Can Anyone please help me. One thing that I have observed is that the second approach is the one most used. SO please help me as to which one is a better approach.
I know this is mainly concerned regarding creation of the schemes in Mongodb, and so the once method looks little better. But still I am not at all sure. Please help.

Basically, in the first approach - it mearly shows you what you can do with the mongoose. It presumes that somewhere you were opening a connection to the database (presumably a few string of code back). Then it listens to the event and does all the job later. It's only there to show you what you can do.
For intance, this creates a mongoose model (it doesnt rely on the database connection being open) - hence it could be included from a separate file with ease and generally be written as a module as it is in the second approach
var teamSchema = new mongoose.Schema({
country: String,
GroupName: String
});
var Team = db.model('Team', teamSchema);
Later on in the first approach you close the connection to the database, but thats not the thing you would want to do in the real application. You have a connection pool, and use it to frequently query the database, closing the connection is only needed when you plan to stop the application from running.
Team.find({'GroupName':gname}, function (err, teams) {
if(err){
onErr(err,callback);
}else{
mongoose.connection.close();
console.log(teams);
callback("",teams);
}
First line - you perform find on the database, if no err was returned - you close the connection, log the results, etc..
Anyway, to sum up - first approach is generally a show-off, second - is what its like in a real world - where you separate logic, make models reusable and includable, where you have router logic

Related

How do I properly save a Mongoose document?

thanks in advance for your attention on this pretty basic question! I'm currently working on a FreeCodeCamp project involving Node, MongoDB and Mongoose (here's my Glitch project), and I'm very confused as to what is going on when I call .save() on a Mongoose model. You can see the full context on Glitch, but here is the specific part that's not behaving as I'd expect it to:
const { User } = require("./models/user");
...
app.post("/api/exercise/new-user", (req, res) => {
var newUser = new User({ name: req.body.username });
newUser.save((error) => console.error(error));
res.json({username: newUser.name, _id: newUser._id})
});
Here's models/user.js:
const mongoose = require("mongoose");
exports.User = mongoose.model(
"User",
new mongoose.Schema({
name: { type: String, required: true }
})
);
I'm seeing the JSON response in my browser with the information I'd expect, but I'm not seeing a new document in my database on MongoDB. What am I missing here? As far as I know when I call .save() on newUser the document should be showing up in MongoDB. In previous projects I did just this and it was working, and I can't figure out what's different in this situation.
And a broader question that I have that I didn't feel like was explained in the FCC curriculum is: at what point is a collection created in MongoDB by Mongoose? Is it when I create a new model? Like at this point in my code:
const mongoose = require("mongoose");
exports.User = mongoose.model(
"User",
new mongoose.Schema({
name: { type: String, required: true }
})
);
Or does it happen when I go to save an instance of the model? When is MongoDB told to create the collection? Thanks very much in advance!
Well, I've done it again, the answer was very simple. I saw that I was getting an error from the mongoose.connect() call, because it was having trouble with the URI starting with mongodb+srv:// instead of just mongodb://. I then saw that because I had forked the start project from FCC, it had some dated versions of Mongoose and MongoDB, just updating those two to the most recent versions (3.5.7 of MongoDB and 5.9.12 of Mongoose) solved the problem, and I'm now able to save the documents correctly!

MongoDB expire event on node js

I want to catch mongodb collection expire event on node.js using mongoose model.
For example if I have this collection
var mySchema = mongoose.Schema({
name: String,
time: {
type: Date,
default: Date.now,
expires: 60*60,
}
})
When document created it will be deleted after 1 hour. I want to call node js function when this document is deleted. For example I have this function
function log(name) {
console.log('document with name ' + name + ' was successfully removed');
}
And I want to call this function for each document when they are being removed.
Thank you.
As I mentioned in a comment, MongoDB doesn't have any notify features built-in. You could implement the same method Meteor uses by tailing MongoDBs oplog, but that's going to take a lot of work.
The only fairly simple and reasonable solution is to do this on the application-level and not database-level by using something like node-schedule (npm install node-schedule --save).
Here's an example:
var schedule = require('node-schedule');
var Model = mongoose.model('Model', mySchema);
Model.create({ name: 'Michael' }, function(err, doc) {
if (err) return console.error(err); // Handle the error
var now = new Date();
var when = new Date(now).setHours(now.getHours() + 1);
schedule.scheduleJob(when, function() {
// This callback will fire in one hour
});
});
The issue with this is that the callback will fire even if MongoDB has crashed or similar and couldn't remove the document. Another issue is that if your app crashes, your schedule callback is lost.

Mongoose referencing models in other models

I am working on an express/node app, using mongodb and mongoose to model users, players, teams, etc. Between these models I am using references with ObjectIDs, so between players and teams there is a connector model RosterSpot which holds a team_id and player_id, both with the Schema type of ObjectID.
Anyways, I am running into problems trying to reference RosterSpots inside of the Team and Player models. I would like to have a method for Players that grabs all their teams, and vice versa for Teams. First, given this code in roster_spot.js
// Synchronously load model dependecies, so foreign model calls can be made
var fs = require('fs');
var models_path = __dirname;
fs.readdirSync(models_path).forEach(function (file) {
if (~file.indexOf('.js')) require(models_path + '/' + file);
})
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var ObjectId = Schema.ObjectId;
var Team = mongoose.model('Team');
var Player = mongoose.model('Player');
var RosterSpotSchema = new Schema({
team_id: {type: ObjectId, required: true},
player_id: {type: ObjectId, required: true}
});
....
RosterSpotSchema.statics.getTeamsForPlayer = function(player_id, callback) {
this.getTeamIdsForPlayer(player_id, function(ids){
Team.find({ _id: { $in: ids } }, function(err, teams){
callback(teams);
});
});
};
...
this works perfectly fine, as this RosterSpot model calls the Team.find() and Player.find() methods without a problem.
The problem comes when I try to make that call in the Player or Team model in the following method in player.js:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var RosterSpot = mongoose.model('RosterSpot');
var PlayerSchema = new Schema({...});
...
PlayerSchema.methods.getTeams = function (callback) {
RosterSpot.getTeamsForPlayer(this._id, function(teams){
callback(teams);
})
};
When I have the var RosterSpot = mongoose.model('RosterSpot'); in there, it throws a "MissingSchemaError: Schema has not been registered for 'RosterSpot'."
I've tried loading in the models via require and async stuff but nothing is working.
The crazy thing is, though, that I've been able to call these sorts of methods for users and players, using the connector model Family and that all works fine. I don't even have to initialize a variable for Family in users.js, which is weird but it works...
I have thought that maybe it is my testing environment that might mess with loading files, but when I start the server it also throws that error. Would be helpful to know if this is even possible, and any insight to this problem would be much appreciated.

MongoDB find() returns nothing

I am trying to query my database from the mongodb shell to retrieve all the entries. Still, my find() method returns nothing.
This is the mongoose model that I am using:
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
var ArticleSchema = new Schema({
title: String,
content: String,
author: { type: String, default: 'Vlad'},
postDate: { type: Date, default: Date.now }
});
ArticleSchema.methods.formatTitle = function() {
var link = this.title.replace(/\s/g, '-');
return '/article/' + link;
};
ArticleSchema.methods.snapshot = function() {
var snapshot = this.content.substring(0, 500);
return snapshot;
};
module.exports = mongoose.model('Article', ArticleSchema);
When loading my Express.js application in the browser, I have no issues displaying all the articles that I have added using my API. Still, I just want to go inside the mongo shell and query them. I have used the following query:
// blog is the database name (mongodb://localhost:27017/blog)
db.blog.find()
I get an empty string in return, basically a new line in the console.
Your answer is right there, in the question:
// blog is the database name (mongodb://localhost:27017/blog)
db.blog.find()
So db already refers to correct database. Instead of blog, put collection name, not db name.
db.articles.find()
Mongo shell will connect when started to the test database, you will need to change the database and then execute the find on your collection :
use blog
db.articles.find()
First you choose the db, then you find on the collection.
use blog
db.articles.find()

Dynamically create collection with Mongoose

I want to give users the ability to create collections in my Node app. I have really only seen example of hard coding in collections with mongoose. Anyone know if its possible to create collections dynamically with mongoose? If so an example would be very helpful.
Basically I want to be able to store data for different 'events' in different collections.
I.E.
Events:
event1,
event2,
...
eventN
Users can create there own custom event and store data in that collection. In the end each event might have hundreds/thousands of rows. I would like to give users the ability to perform CRUD operations on their events. Rather than store in one big collection I would like to store each events data in a different collection.
I don't really have an example of what I have tried as I have only created 'hard coded' collections with mongoose. I am not even sure I can create a new collection in mongoose that is dynamic based on a user request.
var mongoose = require('mongoose');
mongoose.connect('localhost', 'events');
var schema = mongoose.Schema({ name: 'string' });
var Event1 = mongoose.model('Event1', schema);
var event1= new Event1({ name: 'something' });
event1.save(function (err) {
if (err) // ...
console.log('meow');
});
Above works great if I hard code 'Event1' as a collection. Not sure I create a dynamic collection.
var mongoose = require('mongoose');
mongoose.connect('localhost', 'events');
...
var userDefinedEvent = //get this from a client side request
...
var schema = mongoose.Schema({ name: 'string' });
var userDefinedEvent = mongoose.model(userDefinedEvent, schema);
Can you do that?
I believe that this is a terrible idea to implement, but a question deserves an answer. You need to define a schema with a dynamic name that allows information of 'Any' type in it. A function to do this may be a little similar to this function:
var establishedModels = {};
function createModelForName(name) {
if (!(name in establishedModels)) {
var Any = new Schema({ any: Schema.Types.Mixed });
establishedModels[name] = mongoose.model(name, Any);
}
return establishedModels[name];
}
Now you can create models that allow information without any kind of restriction, including the name. I'm going to assume an object defined like this, {name: 'hello', content: {x: 1}}, which is provided by the 'user'. To save this, I can run the following code:
var stuff = {name: 'hello', content: {x: 1}}; // Define info.
var Model = createModelForName(name); // Create the model.
var model = Model(stuff.content); // Create a model instance.
model.save(function (err) { // Save
if (err) {
console.log(err);
}
});
Queries are very similar, fetch the model and then do a query:
var stuff = {name: 'hello', query: {x: {'$gt': 0}}}; // Define info.
var Model = createModelForName(name); // Create the model.
model.find(stuff.query, function (err, entries) {
// Do something with the matched entries.
});
You will have to implement code to protect your queries. You don't want the user to blow up your db.
From mongo docs here: data modeling
In certain situations, you might choose to store information in
several collections rather than in a single collection.
Consider a sample collection logs that stores log documents for
various environment and applications. The logs collection contains
documents of the following form:
{ log: "dev", ts: ..., info: ... } { log: "debug", ts: ..., info: ...}
If the total number of documents is low you may group documents into
collection by type. For logs, consider maintaining distinct log
collections, such as logs.dev and logs.debug. The logs.dev collection
would contain only the documents related to the dev environment.
Generally, having large number of collections has no significant
performance penalty and results in very good performance. Distinct
collections are very important for high-throughput batch processing.
Say I have 20 different events. Each event has 1 million entries... As such if this is all in one collection I will have to filter the collection by event for every CRUD op.
I would suggest you keep all events in the same collection, especially if event names depend on client code and are thus subject to change. Instead, index the name and user reference.
mongoose.Schema({
name: { type: String, index: true },
user: { type: mongoose.Schema.Types.ObjectId, ref: 'User', index: true }
});
Furthermore I think you came at the problem a bit backwards (but I might be mistaken). Are you finding events within the context of a user, or finding users within the context of an event name? I have a feeling it's the former, and you should be partitioning on user reference, not the event name in the first place.
If you do not need to find all events for a user and just need to deal with user and event name together you could go with a compound index:
schema.index({ user: 1, name: 1 });
If you are dealing with millions of documents, make sure to turn off auto index:
schema.set('autoIndex', false);
This post has interesting stuff about naming collections and using a specific schema as well:
How to access a preexisting collection with Mongoose?
You could try the following:
var createDB = function(name) {
var connection = mongoose.createConnection(
'mongodb://localhost:27017/' + name);
connection.on('open', function() {
connection.db.collectionNames(function(error) {
if (error) {
return console.log("error", error)
}
});
});
connection.on('error', function(error) {
return console.log("error", error)
});
}
It is important that you get the collections names with connection.db.collectionNames, otherwise the Database won't be created.
This method works best for me , This example creates dynamic collection for each users , each collection will hold only corresponding users information (login details), first declare the function dynamicModel in separate file : example model.js
/* model.js */
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema;
function dynamicModel(suffix) {
var addressSchema = new Schema(
{
"name" : {type: String, default: '',trim: true},
  "login_time" : {type: Date},
"location" : {type: String, default: '',trim: true},
}
);
return mongoose.model('user_' + suffix, addressSchema);
}
module.exports = dynamicModel;
In controller File example user.js,first function to create dynamic collection and second function to save data to a particular collection
/* user.js */
var mongoose = require('mongoose'),
function CreateModel(user_name){//function to create collection , user_name argument contains collection name
var Model = require(path.resolve('./model.js'))(user_name);
}
function save_user_info(user_name,data){//function to save user info , data argument contains user info
var UserModel = mongoose.model(user_name) ;
var usermodel = UserModel(data);
usermodel.save(function (err) {
if (err) {
console.log(err);
} else {
console.log("\nSaved");
}
});
}
yes we can do that .I have tried it and its working.
REFERENCE CODE:
app.post("/",function(req,res){
var Cat=req.body.catg;
const link= req.body.link;
const rating=req.body.rating;
Cat=mongoose.model(Cat,schema);
const item=new Cat({
name:link,
age:rating
});
item.save();
res.render("\index");
});
I tried Magesh varan Reference Code ,
and this code works for me
router.post("/auto-create-collection", (req, res) => {
var reqData = req.body; // {"username":"123","password":"321","collectionName":"user_data"}
let userName = reqData.username;
let passWord = reqData.password;
let collectionName = reqData.collectionName;
// create schema
var mySchema = new mongoose.Schema({
userName: String,
passWord: String,
});
// create model
var myModel = mongoose.model(collectionName, mySchema);
const storeData = new myModel({
userName: userName,
passWord: passWord,
});
storeData.save();
res.json(storeData);
});
Create a dynamic.model.ts access from some where to achieve this feature.
import mongoose, { Schema } from "mongoose";
export default function dynamicModelName(collectionName: any) {
var dynamicSchema = new Schema({ any: Schema.Types.Mixed }, { strict: false });
return mongoose.model(collectionName, dynamicSchema);
}
Create dynamic model
import dynamicModelName from "../models/dynamic.model"
var stuff = { name: 'hello', content: { x: 1 } };
var Model = await dynamicModelName('test2')
let response = await new Model(stuff).save();
return res.send(response);
Get the value from the dynamic model
var Model = dynamicModelName('test2');
let response = await Model.find();
return res.send(response);

Resources