I have created the following Schema:
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var Player = require('./player');
var gameSchema = new Schema({
created_at: Date,
nrOfCards: String,
players: [{
sticks: String,
player: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Player'
}
}],
});
var Game = mongoose.model('Game', gameSchema);
The saving part works fine and a saved object may look something like this:
"_id": "57dd11aca0c36114588fd250",
"nrOfCards": "3",
"__v": 0,
"players": [
{
"_id": "57d415e527c20f3ed2416e05",
"age": "33"
},
{
"_id": "57d417df2186d53f3d49c996",
"age": "73"
},
{
"_id": "57d41d85ec315d4234010c7d",
"age": "20"
}
]
},
After having saved an object I would like to have it returned with the player-field populated. Here is my attempt:
app.post('/api/games', function(req, res) {
Game.create({
players : req.body.activePlayers,
nrOfCards: req.body.nrOfCards,
}, function(err, game) {
if (err) {
res.send(err);
} else {
Game.findOne(game)
.populate('players.player')
.exec(function (err, newgame) {
if (err) return handleError(err);
console.log(newgame);
res.json(newgame);
});
}
});
});
Thinking that the .populate('players.player') should do the trick , but I'm receiving the unpopulated field containing the _id of player only.
Tips appreciated. Thanks!
Use
player: {
type: Schema.Types.ObjectId,
ref: 'Player'
}
into your schema.
Related
I use following code to dynamically update whole object in mongoDB:
module.exports = function(req, res, next){
const Model = require('../models/' + req.body.where)
for(let i=0; i<req.body.array.length; i++){
Model.updateOne({
"_id": req.body.array[i]._id,
"user_ID": req.body.user_ID
},{
$set: req.body.array[i]
}).catch(next)
if(i+1 == req.body.array.length) res.send({})
}
}
but the code is not working, when model own array:
const mongoose = require('mongoose')
const Schema = mongoose.Schema;
const exerciseSchema = new Schema({
title: {
type: String,
required: [true, 'required!']
}
}, { _id : false })
const workout_planSchema = new Schema({
title: {
type: String,
required: [true, 'required!']
},
description: {
type: String
},
user_ID: {
type: String,
required: [true, 'required!']
},
exercises: [exerciseSchema]
})
const workout_plan = mongoose.model('workout_plan', workout_planSchema)
module.exports = workout_plan
I would like to update whole object with totally new values, staying only with the same _id.
For example , I have following value in DB:
"_id": "604a16f6cf847c1810c8fd08",
"title": "1",
"user_ID": "Test",
"exercises": [{
"title": "123"
}],
and I am sending array which looks like this:
"_id": "604a16f6cf847c1810c8fd08",
"title": "2",
"user_ID": "Test",
"exercises": [{
"title": "234"
},{
"title": "235"
}],
and the result should be same as the array I am sending. How can I change my code to reach this?
PS: Basiclly I want to make object in DB = sent object
so the answer is to change the main function like this:
module.exports = function(req, res, next){
const Model = require('../models/' + req.body.where)
for(let i=0; i<req.body.array.length; i++){
Model.replaceOne({
"_id": req.body.array[i]._id,
"user_ID": req.body.user_ID
},
req.body.array[i]
).catch(next)
if(i+1 == req.body.array.length) res.send({})
}
}
Try this one:
for( let elem of req.body.array ) {
let workout_plan = await Model.findById(elem._id);
workout_plan.title = elem.title;
workout_plan.user_ID = elem.user_ID;
workout_plan.exercises = elem.exercises;
await workout_plan.save();
}
I have the following collection attached to aldeed:simple schema
Posts = new Mongo.Collection("posts");
Posts.attachSchema(new SimpleSchema({
samplePost:{
type:String,
max:500
},
createdAt:{
type: Date,
autoValue: function(){
return new Date()
}
},
"comments.$.reply":{
type:String
},
"comments.$.commentId":{
type: String,
autoValue: function(){
var tempCommentId = new Meteor.Colletion.ObjectID();
return tempCommentId.str;
}
},
"comments.$.commentCreatedAt": {
type: Date,
optional: true,
autoValue: function(){
return new Date()
}
},
});
The actual document looks like the following:
{
"_id": "aaa",
"samplePost": "Hello world!",
"comments": [
{
"reply": "Goodbye",
"commentId": "bbb",
"createdAt": "2016-06-19T19:06:17.931Z"
},
{
"reply": "Good morning",
"commentId": "ccc"
"createdAt": "2016-06-19T19:05:17.931Z"
},
]
}
Now im trying to remove only the 2nd comment with commentId:"ccc" from the document with $pull
"click #delete-comment": function(event, template){
var tempCommentId = $(event.target).parent().find('#commentIdPass').text(); //commentId is collected from HTML view
Posts.update(
{_id: template.data._id}, //_id is collected from the url param
{$pull:{
comments: {
commentId: tempCommentId
}}
});
},
and this is not working. I have narrowed down the problem to
"comments.$.commentCreatedAt": {
type: Date,
optional: true,
autoValue: function(){
return new Date()
}
},
schema. If i remove this schema, i can delete the comment.
So, why is this causing a problem of pulling the whole comment item from the array. Any ideas? Any workarounds?
Try This Query:
db.getCollection('Mytest').update({"_id":"aaa"},{"$pull":{"comments":{"commentId":"ccc"}}});
Found the problem. As I have suspected attached schema was the culprit. I needed to set a conditional for inserting and updating a schema with autovalue method. The schema should look as following:
"comments.$.commentCreatedAt": {
type: Date,
autoValue: function () {
if (this.isInsert) {
return new Date;
}
}
},
How do I have autoincrement ids in mongoose? I want my ids to start like 1, 2, 3, 4, not the weird id numbers mongodb creates for you?
Here's my schema:
var PortfolioSchema = mongoose.Schema({
url: String,
createTime: { type: Date, default: Date.now },
updateTime: { type: Date, default: Date.now },
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
Use mongoose-auto-increment:
https://github.com/codetunnel/mongoose-auto-increment
var mongoose = require('mongoose');
var autoIncrement = require('mongoose-auto-increment');
var connection = ....;
autoIncrement.initialize(connection);
var PortfolioSchema = new mongoose.Schema({
url: String,
createTime: { type: Date, default: Date.now },
updateTime: { type: Date, default: Date.now },
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
//Auto-increment
PortfolioSchema.plugin(autoIncrement.plugin, { model: 'Portfolio' });
module.exports = mongoose.model('Portfolio', PortfolioSchema);
Or if you prefer to use an additional field instead of overriding _id, just add the field and list it in the auto-increment initialization:
var PortfolioSchema = new mongoose.Schema({
portfolioId: {type: Number, required: true},
url: String,
createTime: { type: Date, default: Date.now },
updateTime: { type: Date, default: Date.now },
user: {type: Schema.Types.ObjectId, ref: 'User'}
});
//Auto-increment
PortfolioSchema.plugin(autoIncrement.plugin, { model: 'Portfolio', field: 'portfolioId' });
If you want to have a incrementing numeric value in _id then the basic process is you are going to need something to return that value from a store somewhere. One way to do this is use MongoDB itself to store data that holds the counters for the _id values for each collection, which is described within the manual itself under Create and Auto-Incrementing Sequence Field.
Then as you create each new item, you use the implemented function to get that "counter" value, and use it as the _id in your document.
When overriding the default behavior here, mongoose requires that you both specify the _id and it's type explicitly with something like _id: Number and also that you tell it to no longer automatically try to supply an ObjectId type with { "_id": false } as an option on the schema.
Here's a working example in practice:
var async = require('async'),
mongoose = require('mongoose'),
Schema = mongoose.Schema;
mongoose.connect('mongodb://localhost/test');
var counterSchema = new Schema({
"_id": String,
"counter": { "type": Number, "default": 1 }
},{ "_id": false });
counterSchema.statics.getNewId = function(key,callback) {
return this.findByIdAndUpdate(key,
{ "$inc": { "counter": 1 } },
{ "upsert": true, "new": true },
callback
);
};
var sampleSchema = new Schema({
"_id": Number,
"name": String
},{ "_id": false });
var Counter = mongoose.model( 'Counter', counterSchema ),
ModelA = mongoose.model( 'ModelA', sampleSchema ),
ModelB = mongoose.model( 'ModelB', sampleSchema );
async.series(
[
function(callback) {
async.each([Counter,ModelA,ModelB],function(model,callback) {
model.remove({},callback);
},callback);
},
function(callback) {
async.eachSeries(
[
{ "model": "ModelA", "name": "bill" },
{ "model": "ModelB", "name": "apple" },
{ "model": "ModelA", "name": "ted" },
{ "model": "ModelB", "name": "oranage" }
],
function(item,callback) {
async.waterfall(
[
function(callback) {
Counter.getNewId(item.model,callback);
},
function(counter,callback) {
mongoose.model(item.model).findByIdAndUpdate(
counter.counter,
{ "$set": { "name": item.name } },
{ "upsert": true, "new": true },
function(err,doc) {
console.log(doc);
callback(err);
}
);
}
],
callback
);
},
callback
);
},
function(callback) {
Counter.find().exec(function(err,result) {
console.log(result);
callback(err);
});
}
],
function(err) {
if (err) throw err;
mongoose.disconnect();
}
);
For convience this implements a static method on the model as .getNewId() which just descriptively wraps the main function used in .findByIdAndUpdate(). This is a form of .findAndModify() as mentioned in the manual page section.
The purpose of this is that it is going to look up a specific "key" ( actually again the _id ) in the Counter model collection and perform an operation to both "increment" the counter value for that key and return the modified document. This is also aided with the "upsert" option, since if no document yet exists for the requested "key", then it will be created, otherwise the value will be incremented via $inc, and it always is so the default will be 1.
The example here shows that two counters are being maintained independently:
{ _id: 1, name: 'bill', __v: 0 }
{ _id: 1, name: 'apple', __v: 0 }
{ _id: 2, name: 'ted', __v: 0 }
{ _id: 2, name: 'oranage', __v: 0 }
[ { _id: 'ModelA', __v: 0, counter: 2 },
{ _id: 'ModelB', __v: 0, counter: 2 } ]
First listing out each document as it is created and then displaying the end state of the "counters" collection which holds the last used values for each key that was requested.
Also note those "weird numbers" serves a specific purpose of always being guranteed to be unique and also always increasing in order. And note that they do so without requiring another trip to the database in order to safely store and use an incremented number. So that should be well worth considering.
I try to create valid geojson FeatureCollection and some problems occurred during updating document in collection.
My Model
var mongoose = require('mongoose');
Schema = mongoose.Schema;
var DataSchema = new Schema({
type: {type:String, default:"Feature"},
properties:{
title: { type: String, required: true },
description: { type: String, required: true},
date: {type:Date, default:Date.now}},
geometry:{
type:{type:String, default:"Point"},
coordinates: {type: [Number]}}
});
var MetadataSchema = new mongoose.Schema({
type : {type: String, default: "FeatureCollection"},
features: [DataSchema]
});
var rescueModel = mongoose.model('rescueModel', MetadataSchema);
Router
router.post('/mountain_rescue', function(req, res){
db.collection('rescuemodels', function(err, collection){
collection.update({
"type": "FeatureCollection"
},
{
$push: {
"features": {
properties: {
title: req.body.title,
description: req.body.description
},
geometry: {
coordinates: req.body.coordinates.split(',')
}
}
}
});
res.redirect('/mountain_rescue');
});
});
module.exports=rescueModel;
So If everything is okay but why is it that after executing post route I get
TypeError: Cannot call method 'update' of undefined
I also checked the command in mongo shell and it works
db.rescuemodels.update(
{
"type":"FeatureCollection"
},
{
$push:{
"features": {
"properties":{"title":"WOW"}
}
}
}
)
The problem is that collection is not defined before running the update. Also, you might want to do the redirect in the callback to the update function, but that will depend on the behaviour you are looking for. Your code would then look something like this:
var collection = db.collection('rescuemodels');
collection.update({
"type": "FeatureCollection"
},
{
"$push": {
"features": {
"properties": {
"title": req.body.title,
"description": req.body.description
},
"geometry": {
"coordinates": req.body.coordinates.split(',')
}
}
}
}, function(err, result) {
if (err) throw err;
res.redirect('/mountain_rescue');
});
I have a '3-layered' relationship in MongooseJS like so, it's two one-to-many relationships between subdocuments. Like so:
var BroadcastSchema = new Schema({
...
_donationAddresses: [{
type: Schema.Types.ObjectId,
ref: 'DonationAddress'
}]
});
var DonationAddressSchema = new Schema({
...
_donations: [{
type: Schema.Types.ObjectId,
ref: 'Donation'
}]
});
var DonationSchema = new Schema({
...
amount: Number
});
I want to get the $sum total of the amount:Number on the DonationSchema
So far I've populated the Donation by using a work-around listed here (because as far as I know you can't populate a populate so far as I know)
Broadcast.find()
.exec(function(err, broadcasts) {
// this works
var iter = function(broadcast, callback) {
DonationAddress.populate(broadcast._donationAddresses, {
path: '_donations'
}, callback);
};
// tried to iterate over the donation address and
// aggregate the _donations.amount
var iter2 = function(broadcast, callback) {
DonationAddress.aggregate([{
$match: {
_id: broadcast._donationAddresses
}
}, {
$unwind: "$_donations"
}, {
$group: {
_id: "$_id",
total: {
$sum: "$_donations.amount"
}
}
}], callback);
};
async.each(broadcasts, iter, function done(err) {
async.each(broadcasts, iter2, function done(err) {
res.json(broadcasts);
});
});