I wanted to create a database for MTG combos, and doing something very simple like this, using Mongoose and MLab: We have two schemas, Combo and Card, and two instances of a Card model associated with an instance of a Combo model. When I try to populate the combos of the cards with the name of such combo, it doesn't work and it still shows the ID instead of the name.
var mongoose = require('mongoose') ;
mongoose.connect('mongodb://wernerbusch:xxxx!h#ds115340.mlab.com:15340/wernercito');
var Schema = mongoose.Schema;
var comboSchema = new Schema({
name : String,
cards : [{type: Schema.Types.ObjectId, ref:'Card'}],
type : ["infinite mana", "infinite damage", "draw library", "lethal damage", "storm", "graveyard"]
});
var cardSchema = new Schema({
name: String,
colour:["U","R","B","W","G","C"],
combos: [{type:Schema.Types.ObjectId, ref: 'Combo'}]
})
var Combo = mongoose.model("Combo",comboSchema);
var Card = mongoose.model("Card",cardSchema);
var PanderBurst = new Combo({
name:"PanderBurst",
type:'lethal damage'
})
PanderBurst.save(function(err){
if (err) console.log( err)
var SaprolingBurst = new Card({
name: "Saproling Burst",
colour:["G", "R"],
combos : PanderBurst._id
})
var Pandemonium = new Card({
name: "Pandemonium",
colour:["R"],
combos: PanderBurst._id
})
SaprolingBurst.save(function(err){
if (err) console.log( err)
});
Pandemonium.save(function(err){
if (err) console.log( err)
})
});
Card.find().populate('combos', 'name').exec(function(err,cards){
if (err) console.log (err);
})
After this code is executed, nothing changes in the cards documents.
Have you tried:
Card.find().populate({path: "comboes", select: "name"})
.exec((err,cards)=>{
if (err) console.log (err);
console.log(cards);
});
Using an object has always worked for me.
Update:
you also have:
comboes: {type:Schema.Types.ObjectId, ref: 'Combo'}
in your schema
and combos in your populate.
Final Update:
Populate is used when you make a query. Populating a schema during create isn't good practice because it causes duplication, so call populate with each query.
Related
I'm building a CMS where I have to do a lot of POST and GET requests to my MongoDB. The problem I am having now is that, the more I work on my CMS, the more POST and GET requests I have to do and eventually a lot of double code I'm having in my application. My question is, can I somehow re-use the POST and GET methods? I am using Express framework, MongoDB and Angular on the Front-End.
Here is an example how my application looks like:
Express:
router.post('/news_blocks', function(req, res, next){
var randomNumber = Math.floor(1000 + Math.random() * 9000);
var news_image = req.files.myImage;
news_image.mv('/home//projects/website/public/uploads/' + 'image_' + randomNumber + '.jpg' , function(err) {
if(err){
console.log(err);
}else{
var data = new news_blocks(postOptions(req, randomNumber));
saveToDB(data,res);
}
});
});
router.post('/research', function(req, res, next){
var randomNumber = Math.floor(1000 + Math.random() * 9000);
var research_image = req.files.myImage;
research_image.mv('/home/projects/website/public/uploads/' + 'image_' + randomNumber + '.jpg' , function(err) {
if(err){
console.log(err);
}else{
var data = new research_blocks(postOptions(req, randomNumber));
saveToDB(data,res);
}
});
});
postOptions = function(req, randomNumber){
var options = {
title: req.body.title,
date: new Date,
message: req.body.message,
image: 'image_' + randomNumber
};
return options;
};
MongoDB model:
file1:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var research_block = new mongoose.Schema({
title: String,
date: String,
message: String,
image: String
}, {collection: 'research'});
module.exports = mongoose.model("research", research_block);
File 2:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var news_block = new mongoose.Schema({
title: String,
date: String,
message: String,
image: String
}, {collection: 'news'});
module.exports = mongoose.model("news", news_block);
As you can see, there is a lot of the same code in the POST methods. But I am not sure how I make this more DRY
Suggestion 1:
Make a controller file for every table. And, write functions in it to do all kind of operations in it. And, while calling a service send type of operation along with it. On basis of type you can call any function you want.
Suggestion 2:
Try using GraphQL
I just started learning MongoDB and mongoose. Currently I have the following structure:
database -> skeletonDatabase
collection -> adminLogin
When I run db.adminLogin.find() from the command line I get:
{ "_id" : ObjectId("52lhafkjasfadsfea"), "username" : "xxxx", "password" : "xxxx" }
My connection (this works, just adding it FYI)
module.exports = function(mongoose)
{
mongoose.connect('mongodb://localhost/skeletonDatabase');
var db = mongoose.connection;
db.on('error', console.error.bind(console, 'connection error:'));
db.once('open', function callback () {
console.log('Conntected To Mongo Database');
});
}
My -js-
module.exports = function(mongoose)
{
var Schema = mongoose.Schema;
// login schema
var adminLogin = new Schema({
username: String,
password: String
});
var adminLoginModel = mongoose.model('adminLogin', adminLogin);
var adminLogin = mongoose.model("adminLogin");
adminLogin.find({}, function(err, data){
console.log(">>>> " + data );
});
}
My console.log() returns as >>>>
So what am I doing wrong here? Why do I not get any data in my console log? Thanks in advance for any help.
mongoose by default takes singular model names and pairs them with a collection named with the plural of that, so mongoose is looking in the db for a collection called "adminLogins" which doesn't exist. You can specify your collection name as the 2nd argument when defining your schema:
var adminLogin = new Schema({
username: String,
password: String
}, {collection: 'adminLogin'});
Had a problem with injecting it within an express route for my api so I changed it thanks to #elkhrz by first defining the schema and then compiling that one model I want to then pull like so:
app.get('/lists/stored-api', (req, res) => {
Apis.find(function(err, apis) {
if (err) return console.error(err);
res.send(apis);
});
});
I wouldn't send it to the body, I would actually do something else with it especially if you plan on making your API a production based application.
Run through this problem and read up on possible proper ways of rendering your data:
How to Pass Data Between Routes in Express
Always a good idea to practice safe procedures when handling data.
first compile just one model with the schema as an argument
var adminLogin = mongoose.model('adminLogin', adminLogin);
in your code adminLogin does not exist, adminLoginModel does;
after that ,instead to
adminLogin.find({}, function(err, data){
console.log(">>>> " + data );
});
try this
adminLogin.find(function (err, adminLogins) {
if (err) return console.error(err);
console.log(adminLogins);
is important the "s" because mongo use the plural of the model to name the collection, sorry for my english...
Problem is that then I run this code, after I check the db records with Robomongo on windows, I see only one collection created with name 'maximas' with two records,
if I remove Model2 from code, after creation result will be the same, but must be collection 'maxima'.
Is there bug in code that i don't see, or this word is reserved, any ideas?
The code,
var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/mymodels', (e)=>{
if(e) throw e;
});
var schema = new mongoose.Schema({
text: {type: String}
});
var Model1 = mongoose.model('maxima', schema);
var Model2 = mongoose.model('maximas', schema);
var newData1 = new Model1({
text: 'test'
});
var newData2 = new Model2({
text: 'test'
});
newData1.save((e)=>{
if(e) throw e;
console.log('Saved');
});
newData2.save((e)=>{
if(e) throw e;
console.log('Saved');
});
Mongoose will automatically pluralise your model name as the collection name. This is why they are both the same, as the second collection name is already in plural form.
You can pass the desired collection name as the third model argument:
var Model1 = mongoose.model('maxima', schema, 'maxima');
var Model2 = mongoose.model('maximas', schema, 'maximas');
NOTE: You can also specify your collection name as an option in your schema:
var schema = new mongoose.Schema({
text: { type: String }
}, {
collection: 'maxima'
});
...but in this case you would require a second schema so the first approach above is more suitable here.
var mongo = require('mongoose');
var connection = mongo.createConnection('mongodb://127.0.0.1/test');
connection.on("error", function(errorObject){
console.log(errorObject);
console.log('ONERROR');
});
var Schema = mongo.Schema;
var BookSchema = new Schema({ title : {type : String, index : {unique : true}}});
var BookModel = mongo.model('abook', BookSchema);
var b = new BookModel({title : 'aaaaaa'});
b.save( function(e){
if(e){
console.log('error')
}else{
console.log('no error')
}});
Neither the 'error', or 'no error' are printed to the terminal. What's more the connection.on 'error' doesn't seem to fire either. I have confirmed that MongoDb is running.
this is a case where you are adding the model to the global mongoose object but opening a separate connection mongo.createConnection() that the models are not part of. Since the model has no connection it cannot save to the db.
this is solved either by connecting to mongo on the global mongoose connection:
var connection = mongo.createConnection('mongodb://127.0.0.1/test');
// becomes
var connection = mongo.connect('mongodb://127.0.0.1/test');
or by adding your models to your separate connection:
var BookModel = mongo.model('abook', BookSchema);
// becomes
var BookModel = connection.model('abook', BookSchema);
I really like Aaron's answer, and thanks to him I am now on my way to fixing the issue... although I'm not there yet! Here is my particular issue:
I want to have my schema and models defined in separate files, so I can reuse them from project to project. So as an example I have a file named W8DBItem.js as follows:
var mongoose = require('mongoose');
var itemSchema = new mongoose.Schema({ name: {type: String, required: true}});
module.exports = mongoose.model('W8DBItem', itemSchema);
In my program file I do this this:
var mongoose = require('mongoose');
var W8DBItem = require('../w8/W8DBItem.js');
var dbURL ='mongodb://localhost:27017/default';
var mongoOptions = { useNewUrlParser: true, bufferCommands: false }
mongoose.connect(dbURL, mongoOptions);
var db = mongoose.connection;
// DEAL WITH CONNECTION ERROR
db.on('error', console.error.bind(console, 'connection error:'));
// PREP DATA
var aWeight = { name: "My Test Name" };
var newWeightItem = W8DBItem(aWeight);
// CONNECTION ESTABLISHED
db.once('open', function() {
console.log("Here 1")
// TRY TO SAVE
newWeightItem.save(function (err, newWeightItem) {
if (err) {
console.log("Here 2");
console.log(err);
}
else {
console.log("Here 3");
console.log(newWeightItem);
}
});
});
When I run this program I get "Here 1" but never "Here 2" or "Here 3" in the console.
From Aaron's post I get that the W8DBItem object has no associated (and open) connections, but I am not sure how to go about fixing things. I could connect to the DB in the W8DBItem.js file, but I really don't like hard-coding the server info with the objects - I want these objects to be used in different files, and perhaps with different servers.
Ideas and suggestions are much appreciated!
[EDIT: SOLUTION FOUND!!!]
Instead of exporting my mongoose.model from my object file, I am only exporting the schema:
var mongoose = require('mongoose');
var itemSchema = new mongoose.Schema({name: {type: String, required: true}});
module.exports = itemSchema;
In my program files I then do this:
var itemSchema = require('../w8/W8DBItemSchema.js');
...
var W8DBItem = db.model('W8DBItem', itemSchema);
var newWeightItem = W8DBItem(aWeight);
...
Works like a charm. I hope this helps someone!
The posted answer does not solve the problem. Unfortunately, I cannot just upgrade my database, so that is not a solution either for me. But here I found a solution to this problem: https://github.com/Automattic/mongoose/issues/4064
Just use .$__save instead of .save as shown:
var b = new BookModel({title : 'aaaaaa'});
b.$__save({}, function(e){
if(e){
console.log('error')
// callback will show if e exists
}else{
console.log('no error')
// callback will show 'no error'
}});
I have a large collection of 300 question objects in a database test. I can interact with this collection easily through MongoDB's interactive shell; however, when I try to get the collection through Mongoose in an express.js application I get an empty array.
My question is, how can I access this already existing dataset instead of recreating it in express? Here's some code:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
mongoose.model('question', new Schema({ url: String, text: String, id: Number }));
var questions = mongoose.model('question');
questions.find({}, function(err, data) { console.log(err, data, data.length); });
This outputs:
null [] 0
Mongoose added the ability to specify the collection name under the schema, or as the third argument when declaring the model. Otherwise it will use the pluralized version given by the name you map to the model.
Try something like the following, either schema-mapped:
new Schema({ url: String, text: String, id: Number},
{ collection : 'question' }); // collection name
or model mapped:
mongoose.model('Question',
new Schema({ url: String, text: String, id: Number}),
'question'); // collection name
Here's an abstraction of Will Nathan's answer if anyone just wants an easy copy-paste add-in function:
function find (name, query, cb) {
mongoose.connection.db.collection(name, function (err, collection) {
collection.find(query).toArray(cb);
});
}
simply do find(collection_name, query, callback); to be given the result.
for example, if I have a document { a : 1 } in a collection 'foo' and I want to list its properties, I do this:
find('foo', {a : 1}, function (err, docs) {
console.dir(docs);
});
//output: [ { _id: 4e22118fb83406f66a159da5, a: 1 } ]
You can do something like this, than you you'll access the native mongodb functions inside mongoose:
var mongoose = require("mongoose");
mongoose.connect('mongodb://localhost/local');
var connection = mongoose.connection;
connection.on('error', console.error.bind(console, 'connection error:'));
connection.once('open', function () {
connection.db.collection("YourCollectionName", function(err, collection){
collection.find({}).toArray(function(err, data){
console.log(data); // it will print your collection data
})
});
});
Update 2022
If you get an MongoInvalidArgumentError: The callback form of this helper has been removed. error message, here's the new syntax using async/await:
const mongoose = require("mongoose");
mongoose.connect('mongodb://localhost/productsDB');
const connection = mongoose.connection;
connection.on('error', console.error.bind(console, 'connection error:'));
connection.once('open', async function () {
const collection = connection.db.collection("Products");
collection.find({}).toArray(function(err, data){
console.log(data); // it will print your collection data
});
});
I had the same problem and was able to run a schema-less query using an existing Mongoose connection with the code below. I've added a simple constraint 'a=b' to show where you would add such a constraint:
var action = function (err, collection) {
// Locate all the entries using find
collection.find({'a':'b'}).toArray(function(err, results) {
/* whatever you want to do with the results in node such as the following
res.render('home', {
'title': 'MyTitle',
'data': results
});
*/
});
};
mongoose.connection.db.collection('question', action);
Are you sure you've connected to the db? (I ask because I don't see a port specified)
try:
mongoose.connection.on("open", function(){
console.log("mongodb is connected!!");
});
Also, you can do a "show collections" in mongo shell to see the collections within your db - maybe try adding a record via mongoose and see where it ends up?
From the look of your connection string, you should see the record in the "test" db.
Hope it helps!
Something else that was not obvious, to me at least, was that the when using Mongoose's third parameter to avoid replacing the actual collection with a new one with the same name, the new Schema(...) is actually only a placeholder, and doesn't interfere with the exisitng schema so
var User = mongoose.model('User', new Schema({ url: String, text: String, id: Number}, { collection : 'users' })); // collection name;
User.find({}, function(err, data) { console.log(err, data, data.length);});
works fine and returns all fields - even if the actual (remote) Schema contains none of these fields. Mongoose will still want it as new Schema(...), and a variable almost certainly won't hack it.
Go to MongoDB website, Login > Connect > Connect Application > Copy > Paste in 'database_url' > Collections > Copy/Paste in 'collection' .
var mongoose = require("mongoose");
mongoose.connect(' database_url ');
var conn = mongoose.connection;
conn.on('error', console.error.bind(console, 'connection error:'));
conn.once('open', function () {
conn.db.collection(" collection ", function(err, collection){
collection.find({}).toArray(function(err, data){
console.log(data); // data printed in console
})
});
});
I tried all the answers but nothing worked out, finally got the answer hoe to do it.
var mongoose = require('mongoose');
mongoose.connect('mongodb://0.0.0.0:27017/local');
// let model = require('./test1');
setTimeout(async () => {
let coll = mongoose.connection.db.collection(<Your collection name in plural form>);
// let data = await coll.find({}, {limit:2}).toArray();
// let data = await coll.find({name:"Vishal"}, {limit:2}).toArray();
// let data = await coll.find({name:"Vishal"}, {projection:{player:1, _id:0}}).toArray();
let data = await coll.find({}, {limit:3, sort:{name:-1}}).toArray();
console.log(data);
}, 2000);
I have also mentioned some of the criteria to filter out. Delete and update can also be done by this.
Thanks.
Make sure you're connecting to the right database as well as the right collection within the database.
You can include the name of the database in the connection string.
notice databasename in the following connection string:
var mongoose = require('mongoose');
const connectionString = 'mongodb+srv://username:password#hosturl.net/databasename';
mongoose.connect(connectionString);