I am trying the aggregation in mongoose. When I run that aggregation, it show the error. What am I missing?
const data = await Rooms.aggregate([{ $match: { adminID: "1234" } }]);
Error is like that
MongoInvalidArgumentError: Method "collection.aggregate()" accepts at most two arguments
Edit -- code for Rooms Schema
const mongoose = require('mongoose');
const Rooms = new mongoose.Schema(
{adminID: {
type: String,
required: true,
},
roomID: {
type: String,
required: true,
},
roomName: {
type: String,
required: true,
},
users: [
{
id: {
type: String,
required: true,
unique: true,
},
},
],
},
{ timestamps: true } );
module.exports = mongoose.model("rooms", Rooms);
solution 1 : downgrade mongoose to version 5
solution 2 :
const data = await Rooms.aggregate([{ $match: { adminID: "1234" } }],"adminID roomID roomName users");
in new version second argument is selected fields in out put,
or use :
const data = await Rooms.aggregate.match({ adminID: "1234" } )
Related
I have tried so many times to add new field to the existing MongoDB document but I failed. I tried following code to do the job but nothing happened.
Here is the User model.
const UserSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
},
{ timestamps: true }
);
Here is the code to add new field to the document.
const updateDocument = async () => {
const updatedUser = await User.findOneAndUpdate(
{ _id: "63eb30f466127f7a0f7a9b32" },
{
$set: { lastName: "syed" },
}
);
console.log(updatedUser);
};
updateDocument();
NOTE 1: lastName field does not exist in the MongoDB document and in the UserSchema. I want to add that field to the MongoDB document.
NOTE 2: The same code works when I update the existing field inside the document but it does not work when adding new field.
You need to pass strict:false as an option to findOneAndUpdate.
According to the mongoose doc:
The strict option, (enabled by default), ensures that values passed to our model constructor that were not specified in our schema do not get saved to the db.
const updatedUser = await User.findOneAndUpdate(
{ _id: "63eb30f466127f7a0f7a9b32" },
{
$set: { lastName: "syed" },
},
{ strict: false }
);
An alternative way is to pass this parameter when you defined the schema:
const UserSchema = new mongoose.Schema(
{
name: {
type: String,
required: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
},
{ timestamps: true, strict: false }
);
EDIT added mongoose.model.
I'm pretty new to mongodb and mongoose. I'm not able to populate books to authors in mongoose.
Can anyone help me with that? Here is my code. I removed unnecessary fields
const authorSchema = mongoose.Schema({
_id:{
type: String,
required: true,
unique: true,
trim: true
},
name: {
type: String,
required: true,
trim: true
},
books: [{
type: String,
ref: "Book"
}]
}, {_id:false})
const Author = mongoose.model('Author', authorSchema)
module.exports = {Author}
And my books schema looks as following
const bookSchema = mongoose.Schema({
_id:{
type:String,
required:true,
unique:true,
trim: true
},
name: {
type: String,
required: true,
trim: true,
unique: true
},
description:{
type: String,
trim: true,
default: 'No description specified'
},
datePublished: {
type: Date,
trim: true,
default: '01/01/2001'
},
author:{
type: String,
ref: 'Author',
required: true
}
}, {_id:false})
const Book = mongoose.model('Book', bookSchema)
module.exports = {Book}
Here is the route to populate them together
AuthorRouter.get('/authors/:id', async(req, res)=>{
const authorID = req.params.id
try {
const author = await Author.findById(authorID)
try {
const populated = await author.populate({path: 'books.book', select: 'name description -_id'})
console.log(populated);
res.status(201).send(populated)
} catch (e) {
res.status(401).send(`${e}\n Unable to populate categories`)
}
} catch (error) {
res.status(401).send('Unable to find author')
}
})
Output is following with empty books array:
{
"_id": "123",
"name": "testuser",
"books": [],
"__v": 0
}
Populating an arrays of refs works the same way as a single ref. Just call the populate method on the query and an array of documents will be returned in place of the original _id's.
In your case that would be:
const populated = await author.populate({path: 'books', select: 'name description -_id'});
Hey i have modify your code , try to use this one
This is your author schema file
const Schema = mongoose.Schema;
const authorSchema = mongoose.Schema({
_id:{
type: String,
required: true,
unique: true,
trim: true
},
name: {
type: String,
required: true,
trim: true
},
books: [{
type: Schema.Types.ObjectId,
ref: "bookSchema"
}]
}, {_id:false})
const Author = mongoose.model('Author', authorSchema)
module.exports = {Author}
And this is your route
AuthorRouter.get('/authors/:id', async(req, res)=>{
const authorID = req.params.id
try {
const author = await Author.findById(authorID)
try {
const populated = await author.find().populate('books');
console.log(populated);
res.status(201).send(populated)
} catch (e) {
res.status(401).send(`${e}\n Unable to populate categories`)
}
} catch (error) {
res.status(401).send('Unable to find author')
}
})
I am trying to load default data into my MongoDB database from a node.js backend.
This is the data I am loading as JSON:
[
{
"datetime": "28/08/2021 16:01:00",
"sensor": {
"id": 1,
"type": "Temperature"
},
"value": 2502
},
{
"datetime": "28/08/2021 16:02:00",
"sensor": {
"id": 2,
"type": "Temperature"
},
"value": 2252
}
]
And these are the mongoose models:
const SensorType = Object.freeze({
Temperature: "Temperature"
});
const SensorSchema = new mongoose.Schema({
id: { type: Number, required: true },
type: { type: Object.values(SensorType), required: true },
});
Object.assign(SensorSchema.statics, { SensorType });
const Sensor = mongoose.model('Sensor', SensorSchema);
const DataEntrySchema = new mongoose.Schema({
datetime: { type: String, required: true },
sensor: { type: SensorSchema, required: true },
value: { type: Number, required: true }
});
const DataEntry = mongoose.model('DataEntry', DataEntrySchema);
Loading the DataEntries like this:
mongoose.connect("mongodb://127.0.0.1:27017/",{
useCreateIndex:true,
useNewUrlParser: true,
useUnifiedTopology: true}
).then(() => {
console.log('Database Successfully Connected')
if(fill_default_data) {
DataEntry.create(
JSON.parse(fs.readFileSync(path.resolve(__dirname, 'test_data.json'), 'utf8'))
);
}
}, error => {
console.log(error)
}
);
However, I am noticing that no Sensor-objects are created inside MongoDB, only DataEntries - why is that? And how can I create Sensor-objects as well?
Of course, a DataEntry object has the sensor attached but if I call Sensor.find().then( sensors => res.json(sensors) ) an empty array is returned.
You probably can't use a schema in another schema. You need to use refs instead.
So something like this sensor: { type: SensorSchema, required: true } won't work.
You should replace it with sensor: { type: number, required: true, ref: 'Sensor' },, where the ref is the name of the model you want to refer to as a string. Notice that the type is a number as you want to pass the id of the relevant SensorDocument in the DataEntryDocument.
Moreover id is a virtual, you should use _id instead when you want to spec out ids in mongoose schemes.
So your mongoose schemes should look like:
const SensorSchema = new mongoose.Schema({
_id: { type: mongoose.Schema.Types.Number, required: true },
type: { type: mongoose.Schema.Types.String, required: true },
});
const Sensor = mongoose.model('Sensor', SensorSchema);
const DataEntrySchema = new mongoose.Schema({
datetime: { type: mongoose.Schema.Types.String, required: true },
sensor: { type: mongoose.Schema.Types.Number, ref: 'Sensor', required: true },
value: { type: mongoose.Schema.Types.Number, required: true }
});
const DataEntry = mongoose.model('DataEntry', DataEntrySchema);
I still don't know why the Object.freeze and Object.assign are here.
Now if you want a DataEntry, you first need to create a Sensor.
const sensor = new Sensor({ _id: 0, type: 'Temperature' })
await sensor.save()
const dataEntry = new DataEntry({ sensor: 0, datetime: 'some timestamp as string', value: 25 })
await dataEntry.save()
I am leaving the validation-specific logic out as it is out of the scope of this query.
You can checkout docs for mongoose populate for more information.
I have this document in mongo atlas
_id: 5f8939cbedf74e363c37dd86,
firstname: "Person",
lastname: "Person lastname",
sex: "Masculino",
age: "20",
birthDay: 2020-10-07T00:00:00.000+00:00,
vaccines: Array
0:Object
dose: Array
_id: 5f8939cbedf74e363c37dd87
vaccine:5f7023ad96f7ed21e85be521
createdAt:2020-10-16T06:12:27.726+00:00
updatedAt:2020-10-16T06:12:27.726+00:00
1:Object
dose:Array
_id:5f893a9ca98e97188c93fea8
vaccine:5f70259796f7ed21e85be523
2:Object
dose:Array
_id:5f893acda98e97188c93fea9
vaccine:5f7023ad96f7ed21e85be521
This is my mongoose schema
const mySchema = new Schema({
firstname: {
type: String,
required: true,
},
lastname: {
type: String,
required: true,
},
sex: {
type: String,
required: true,
},
age: {
type: String,
required: true,
},
birthDay: {
type: Date,
required: true,
},
vaccines: [
{
type: new Schema(
{
vaccine: {
type: Schema.ObjectId,
ref: "Vaccine",
},
dose: Array,
},
{ timestamps: true }
),
},
],
});
every time I add a new person the vaccines array gets one new object with the timestamp as you can see, in my js file I use this code:
const addPerson = (person) => {
const myPerson= new Model(person);
return myPerson.save();
};
Then when I add a new vaccine for the same person this does not get the timestamp, I'm using this code for that:
const addPersonVaccine = async ({ params, body }) => {
if (!params) return Promise.reject("Invalid ID");
const vaccines = [body];
const foundPerson = await Model.updateOne(
{
_id: params,
},
{
$push: {
vaccines: vaccines,
},
}
);
return foundPerson;
};
This is what my body inside vaccines array has:
[ { vaccine: '5f72c909594ee82d107bf870', dose: 'Primera' } ]
The problem is that I have no results about the next timestamps, as you can see in my mongo atlas document:
1:Object
dose:Array
_id:5f893a9ca98e97188c93fea8
vaccine:5f70259796f7ed21e85be523
2:Object
dose:Array
_id:5f893acda98e97188c93fea9
vaccine:5f7023ad96f7ed21e85be521
Is that the best way to implement timestamps in subdocuments or sub schemas?
I will appreciate your answers, thnks 👏
You can use mongoose schema timestamps options to the inner schemas
const mongoose = require("mongoose");
const forumSchema = new mongoose.Schema(
{
title: { type: String, required: true },
biddings: [
{
type: new mongoose.Schema(
{
biddingId: String,
biddingPoints: Number
},
{ timestamps: true }
)
}
]
},
{ timestamps: true }
);
const Forum = mongoose.model("Forum", forumSchema);
module.exports = Forum;
for more Mongoose schema set timestamp on nested document
I defined two schema in mongoose: DocSchema has DocTypeSchema reference.
const DocTypeSchema = new Schema({
name: { type: String, unique: true, index: true }
});
export const DocType = mongoose.model('Doc-Type', DocTypeSchema);
const DocSchema = new Schema(
{
name: { type: String },
type: { type: Schema.Types.ObjectId, ref: 'Doc-Type' },
description: { type: String },
}
);
When I try to get the docs with type by the name I gets empty results.
How can I solve this?
docs.find({ 'type.name': 'VideoBook' }, { limit: 30 })
I don't want to get the type object inside the docs array. just to gets the docs that match to the query.
You need tu user .aggregate
Specify the collection:
const DocTypeSchema = new Schema({
name: { type: String, unique: true, index: true }
},{ collection: 'docType' });
Simple example :
const docs = await Doc.aggregate([
{
$lookup: {
from: 'docType',
localField: 'type',
foreignField: 'name',
as: 'magic'
}
},
{$unwind: '$magic'},
{
$match: {
$and: {
"magic.name": 'VideoBook'
}
}
},
{ $limit : 30 }
])