CONTEXT
I have a collection ("venues"). I'm trying to allow users to add new objects ("rooms" - defined as subschema) to an instance of the venue object. Each room should have an unique id (using autovalue in collection2 schema).
PROBLEM / WHAT I'VE TRIED
I tried two methods:
Venues.insert - attempts to create a new entire Venue instance instead of adding a new row to the roomPricing sub-schema.
Venues.update - using '$set:params" throws an Exception while invoking method 'addRoom' MongoError: Cannot update 'roomPricing' and 'roomPricing.roomId' at the same time. If I remove the Id from the collection, the app updates the 'existing' room values instead of creating a 'new' instance of room.
In summary, I need a method that updates the 'parent' object (the venue) while creating a new 'child' object in the 'room' subschema.
COLLECTIONS
Schema.RoomPricing = new SimpleSchema({
roomId: {
type: String,
autoValue: function(){
return Random.id();
},
optional: true
},
roomName: {
type: String,
max:50,
optional: true
}
}
// MAIN SCHEMA for the Venues colleciton.
Schema.Venues = new SimpleSchema({
venueName: {
type: String,
label: "Venue Name",
max: 200,
optional: false
},
roomPricing: {
type: Schema.RoomPricing,
optional: true
}
}
CONTROLLER & METHOD
var currentVenueId = this.params._id
var params = {
roomPricing: {
roomName: roomName,
sitCapacity: sitCapacity,
}
}
Meteor.call('addRoom', currentVenueId, params);
//Method
Meteor.methods({
'addRoom': function (id, params) {
Venues.insert({
_id: id
},{
$set:params});
}
});
In the end this is what I used - needed $push instead of $set.
'addRoom': function (id, params) {
Venues.update({
_id: id
},{
$push:params});
}
Related
I have 2 models: Commitments and Users. The Users model has a property named commitments whose value is an array of ObjectIds and reference is the Commitments collection:
const modelName = 'users';
const schema = new mongoose.Schema<UserProperties, UsersModel, UserVirtuals>({
(...)
commitments: {
type: [
{
type: mongoose.SchemaTypes.ObjectId,
ref: 'commitments',
},
],
required: true,
default: [],
},
}, {
collection: modelName,
}).loadClass(User);
The Commitments model has a property named owner whose value is an ObjectId that references a document from the Users model's collection:
const modelName = 'commitments';
const schema = new mongoose.Schema<CommitmentProperties, CommitmentsModel, CommitmentVirtuals>({
(...)
owner: {
type: mongoose.SchemaTypes.ObjectId,
required: true,
},
}, {
collection: modelName,
}).loadClass(Commitment);
I wanted to make it so whenever I deleted a document from the Commitments model's collection, it would also remove the ObjectId of that document from the Users model document of ObjectId owner's commitments property. So, in my Commitments model's schema:
schema.pre('deleteOne', async function() {
(<UsersModel>storage.models.get('users')).findOneAndUpdate({
_id: this.owner,
}, {
$pull: {
commitments: this._id,
},
}).exec().catch(reason => {
logger.log({
category: LoggingCategories.MIDDLEWARE,
type: LoggingType.ERROR,
message: reason,
extraInfo: 'COULD NOT REMOVE COMMITMENT FROM ARRAY OF USER COMMITMENTS',
});
});
});
However this is not working. I don't get any errors and I'm sure that the middleware function is getting executed because I tried logging something within it to test and it did log.
I am storing chat app messages in MongoDB. After X time i would like them to delete themselves.
Where in the code do i add the line from the Docs
{expireAfterSeconds: x }
My code for creating the item is
try {
MessageModel.create({
username: user.username,
text: msg,
time: moment().format('h:mm a'),
room: user.room
})
} catch (error) {
// do stuff
}
and my model is set out as below
const MessageSchema = new mongoose.Schema(
{
userName: String,
text: String,
time: String,
room: String
},
{ collection: 'messages' }
)
const messageModel = mongoose.model('MessageSchema', MessageSchema)
Do I add the code to the model? or as a second argument to the create method?
Thanks in advance
The MongoDB TTL collection feature is set by using an index.
First, modify your time-field to store a timestamp as a valid date type. You can use moment().toISOString()
const MessageSchema = new mongoose.Schema(
{
userName: String,
text: String,
time: String,
room: String,
},
{ collection: 'messages' }
)
Set the TTL index like so
db.messages.createIndex( { "time": 1 }, { expireAfterSeconds: 3600 } )
For more information look at the docs
I am currently having a problem where I am trying to update an of a nested array in a Mongoose record.My schema is as follows:
const customerSchema = new mongoose.Schema({
kimCustomerId: {
type: Number,
required: true
},
addresses: [
{
created: Date,
updated: Date,
addressInfo: {
type: { type: String },
typeOfAddress: String,
careOf: String,
address: String,
addressRow1: String,
addressRow2: String,
zipcode: String,
city: String,
countryCode: String,
physicalAddressType: String,
validFrom: Date,
validTo: Date
}
}
],
.....
As you can see, the adrress array for each record holds many addresses. I want to be able to pass through an update object and update the properties inside the addressInfo nested object inside a particular array object. Here is my query as it stands:
const updated = await db.models.customers.findOneAndUpdate(
{
_id: customer._id,
'addresses.addressId': addressData.addressId
},
{ $set: { 'addresses.$': addressData } },
{ new: true }
);
and an example of an object I pass through to update a record:
{
addressId: officialAddressExists.addressId,
addressInfo: {
validTo: new Date().toISOString()
}
}
What I want to happen is, when I pass this object to the schema method, I want to select the correct address by the values 'kimCustomerId' and 'addressId' (which I have working fine) then only update the values of the 'addressInfo' nested object that I have passed and keep the ones not passed as they are, in this case the 'validTo' field but it can be any number of them updated. It is overwriting the whole 'addressInfo' nestedObject at the moment so I presume I have to do some kind of set operation on that nested object as well but I am unsure how.
Is anyone able to point me in the right direction here?
Thanks!
There is no straight way to do this in query, you can do it in your client side, something like,
// Sample body request
let addressData = {
addressId: 1,
addressInfo: {
validTo: new Date().toISOString(),
typeOfAddress: "Home",
address: "ABC"
}
};
let set = {};
for (let key in addressData.addressInfo) {
set["addresses.$.addressInfo." + key] = addressData.addressInfo[key];
}
console.log(set);
Pass set variable in to your query,
const updated = await db.models.customers.findOneAndUpdate(
{
_id: customer._id,
'addresses.addressId': addressData.addressId
},
{ $set: set },
{ new: true }
);
I have a region collection:
var RegionSchema = new Mongoose.Schema({
name: {type: String},
registrations: [{type: Mongoose.Schema.Types.ObjectId, ref: 'registrations'}],
...
});
A registration collection:
var RegistrationSchema = Mongoose.Schema({
firstName: {type: String},
...
});
In my controller I instantiate a registration then save it to my region with the upsert option set to true:
var registration = new Registration(req.body.registration);
...
Region
.update(
{ _id: user.region},
{ $push: {registrations: registration},
{ upsert: true }
)
.exec();
What I find is that an ObjectId("...") does, in fact, get pushed onto the registrations property of the region, e.g.:
{
name: "Northwest",
registrations: [ObjectId("57d038a1466345d52920b194")]
}
but there is no matching document with that _id in the registrations collection. So my question: what am I not understanding about the nature of the upsert flag; does it not suggest that calling save on the registration is unnecessary?
The upsert flag only applies to the collection you're operating on; in this case, the Region collection. So, when calling Region.update, it will create an object with _id of user.region if an object with that ID doesn't exist.
Mongo doesn't enforce strict ID references, so it will let you push an ID onto registrations so long as the ID is valid.
You'll need to save the Registration object first:
var registration = new Registration(req.body.registration);
registration.save(function() {
Region.update(
{ _id: user.region },
{ $push: { registrations: registration } },
{ upsert: true }
).exec();
});
I'm new to Mongoose and I'm having difficulty getting my head around accessing properties deeper in the model and updating properties on the following model structures.
Game Schema
var gameSchema = new Schema({
opponents: [{
type: Schema.Types.ObjectId,
ref: 'teams'
}],
startTime: { type: Date, default: Date.now },
endTime: Date,
pauses: [{
start: Date,
end: Date
}],
winner: {
type: Schema.Types.ObjectId,
ref: 'teams'
},
status: {type: String, default: "created"},
score: [{
"opponent1": {type: Number, default: 0},
"opponent2": {type: Number, default: 0}
}],
}, { versionKey: false });
Team Schema
var teamSchema = new Schema({
name:String,
badge:String,
goals:[{type: Date, default: Date.now}],
totalWins:Number
}, { versionKey: false });
My problem is I'm trying to add a goal to a team from a specific game.
So my end point:
POST: /api/game/GAME_ID/goal
DATA: {_id: TEAMID}
I thought the following would work:
Games.findById(GAME_ID)
.populate('opponents')
.find({'opponent._id': TEAM_ID})
.exec(function(err, team) {
// Team from game with matching ID returned
// Now push goal time into array
team.goal.push(Date.now());
});
The above does not appear to return a team. if I remove the second find the game is returned and then I have to do something horrible like this:
Games.findById(GAME_ID)
.populate('opponents')
.exec(function(err, game) {
if(game.opponents[0]._id.toString() === req.body._id) {
game.opponents[0].goals.push(Date.now());
} else if (game.opponents[1]._id.toString() === req.body._id) {
game.opponents[1].goals.push(Date.now());
} else {
// Throw error no matching team with id
}
});
game.save(function(err, game) {
//Game saved
});
this last example appears to work but when I try to add further goals pushing into the goals array it overwrites the old goal time.
So to recap.
How do I query the Games model to retrieve a child by id which has
yet to be populated?
How do I set push the goal time stamp into the goals array without
overwriting the previous one?
Is it possible to do these a bit more gracefully than the current example given above.
Games.findById(GAME_ID)
.populate(
path: 'opponents',
match: { _id: TEAM_ID}
)
.exec(function(err, team) {
// Team from game with matching ID returned
});