I am new to mongodb and am trying to make this type of entry:
{
"resources": [
{
"amount": 1,
"resource": {
"_id": "61be82b9549b4ede0c8df07e"
}
}
]
}
Here is my schema code:
const schema = new Schema({
resources: [
{
amount: {
type: Number,
required: true,
},
resource: {
_id: {
type: Schema.Types.ObjectId,
ref: "Resource"
}
}
}
]
});
here is the json code i send
{
"resources": [
{
"amount": 1,
"resource": {
"_id": "61be82b9549b4ede0c8df07e"
}
}
]
}
After processing the request, the following entry is created
{
"resources": [
{
"amount": 1,
"resource": {
"_id": "61be82b9549b4ede0c8df07e"
},
"_id": "61ebf5d2e47442bd566fe157"
}
],
"_id": "61ebf5d2e47442bd566fe156",
"__v": 0
}
Id for the resource was created correctly, but I can't figure out where the resources._id key came from? Where did I make a mistake?
You have to off _id from schema declaration in the array, after updating schema try inserting new document,
const schema = new Schema({
resources: [
{
_id: false,
amount: {
type: Number,
required: true,
},
resource: {
_id: {
type: Schema.Types.ObjectId,
ref: "Resource"
}
}
}
]
});
Related
Problem:
I need to only update one document in a nested array of subdocuments. My previous query was updating all matching documents which is no good Example Below. So I decided to use aggregation so that I could add a limit stage so that I could only update one item, but I cannot get the update to happen through node and I am not even getting errors.
Old Query/Update:
This query updates all documents that match the shape of userId: "empty" I need it to only update the first match
const query = await db.collection('events').updateOne({
_id: new ObjectId(eventId),
createdBy: new ObjectId(createdBy),
"weights.weight": weight
},
{
$set: {
"weights.$.spotsAvailable.$[el2]": {
"name": applicantName,
"userId": new ObjectId(applicantId)
}
}
},
{
arrayFilters: [
{
"el2.userId": "empty"
}
]
})
I have tested the aggregation in the MongoDB compass aggregation builder and it works fine.
But in the actual node code no luck
I have tried:
Pluging in variables without the new ObjectId syntax
Plugin in variables with the new ObjectId syntax
Using the exact same hardcoded values that I got from copying the aggregation code from compass for the node driver
Example document
{
"_id": {
"$oid": "6398c34ca67dbe3286452f23"
},
"name": "test",
"createdBy": {
"$oid": "636c1778f1d09191074f9690"
},
"description": "testing",
"date": {
"$date": {
"$numberLong": "1645488000000"
}
},
"location": {
"type": "Point",
"coordinates": [
0,
0
]
},
"weights": [
{
"spotsAvailable": [
{
"name": "empty",
"userId": "empty"
},
{
"name": "empty",
"userId": "empty"
},
{
"name": "empty",
"userId": "empty"
}
],
"weight": 12
},
{
"spotsAvailable": [
{
"name": "empty",
"userId": "empty"
},
{
"name": "empty",
"userId": "empty"
}
],
"weight": 15
}
],
"eventApplicants": [
{
"userId": {
"$oid": "636c1778f1d09191074f9690"
},
"name": "Wayne Wrestler",
"weight": 15
}
]
}
Code:
const acceptOrRemoveApplicant = async (eventId: ObjectId, createdBy: ObjectId, applicantId: ObjectId, applicantName: string, boolean: boolean, weight: number): Promise<boolean | undefined> => {
console.log({ eventId, createdBy, applicantId, applicantName, boolean, weight })
if (boolean == true) {
try {
/*
* Requires the MongoDB Node.js Driver
* https://mongodb.github.io/node-mongodb-native
*/
const agg = [
{
'$match': {
'_id': new ObjectId('6398c34ca67dbe3286452f23'),
'createdBy': new ObjectId('636c1778f1d09191074f9690')
}
}, {
'$unwind': {
'path': '$weights'
}
}, {
'$unwind': {
'path': '$weights.spotsAvailable'
}
}, {
'$match': {
'weights.spotsAvailable.name': 'empty',
'weights.weight': 15
}
}, {
'$limit': 1
}, {
'$set': {
'weights.spotsAvailable.name': 'Wayen',
'weights.spotsAvailable.userId': '123456'
}
}
]
const client = await clientPromise;
const db = client.db();
const query = db.collection('events').aggregate(agg);
// const query = await db.collection('events').updateOne({
// _id: new ObjectId(eventId),
// createdBy: new ObjectId(createdBy),
// "weights.weight": weight
// },
// {
// $set: {
// "weights.$.spotsAvailable.$[el2]": {
// "name": applicantName,
// "userId": new ObjectId(applicantId)
// }
// }
// },
// {
// arrayFilters: [
// {
// "el2.userId": "empty"
// }
// ]
// })
if (query) {
console.log("we queried")
console.log({ query })
return true
} else {
throw new Error("User not added to event")
}
} catch (e) {
console.error(e);
}
How can I grab the _id of sellerId inside the products? So my backend crash because I change the schema and won't compute the total sum, so right now, how can I grab it.
so this is my data.
{
"_id": "636656163b470e00d0c44872",
"userId": {
"_id": "6366559f3b470e00d0c447e9",
"studentId": "1234567890"
},
"products": [
{
"productId": {
"_id": "636655f03b470e00d0c447fb",
"title": "Shoe Cabinet",
"img": "https://firebasestorage.googleapis.com/v0/b/tua-ecom.appspot.com/o/1667651055293%5Bobject%20File%5D?alt=media&token=608c62f4-915c-47ce-a817-b025bebff06f"
},
"quantity": 1,
"sellerId": {
"_id": "636655c53b470e00d0c447eb",
"studentId": "1234567891"
},
"_id": "636656163b470e00d0c44873"
}
],
"amount": 100,
"location": "gym",
"time": "8 am",
"status": "pending",
"tax": 1,
"createdAt": "2022-11-05T12:24:54.934Z",
"updatedAt": "2022-11-05T12:24:54.934Z",
"__v": 0
},
This is request.
router.get("/total/:id", async (req, res) => {
const { id } = req.params;
try {
const income = await Order.aggregate([
{$match:
{
"products.sellerId" : {"$elemMatch": {"_id": id}}
}
},
{$project: {amount: 1}},
])
console.log("Hello")
res.status(200).json(income);
} catch (err) {
res.status(500).json({err: err.message});
}
});
note, the id inside the sellerId, is the params I'm using in the router, what I'm trying to do is grab the orders of specific users and compute it.
EDIT: ADDED SCHEMA
OrderSchema.js
const OrderSchema = new mongoose.Schema({
userId: {type: mongoose.Schema.Types.ObjectId, ref: 'User'},
products: [
{
productId:{
type: mongoose.Schema.Types.ObjectId, ref: 'Product'
},
quantity: {
type: Number,
default: 1,
},
sellerId: {
type: mongoose.Schema.Types.ObjectId, ref: 'User'
}
}
],
amount: {type: Number,required: true},
location:{type: Object, required:true},
time: {type: String, required: true},
status: {type:String, default: "pending"},
tax: {type: Number,}
}, {timestamps: true}
)
export default mongoose.model('Order', OrderSchema)
I have a ride Collection with trips as a field, trips is a map where the keys are different years. I want to query the collection but exclude the passengers field in each trip
const ride = new Schema(
{
boat_operator: {
type: Schema.Types.ObjectId,
required: true,
ref: 'User'
},
trips: {
type: Map,
of: {
passengers: [{ type: Schema.Types.ObjectId, ref: 'User' }],
available_seats: { type: Number, required: true }
},
default: new Map()
}
}
)
I tried this
const rides = await Ride.find({ status: 'waiting' }).select("-trips.*.passengers")
I tried to select all the items in value then remove the corresponding passengers field in each
It had no effect
this is what the response looks like
[
{
"_id": "632a1669279c86f4ab3a4bf5",
"boat_operator": "6328c434a98212a7f57c4edc",
"trips": {
"2019": {
"passengers": [],
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bfe"
},
"2020": {
"passengers": [],
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bfc"
},
"2021": {
"passengers": [],
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bfa"
},
"2022": {
"passengers": [],
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bf8"
}
}
}
]
I want to exclude the passengers field in the returned document
This would solve it
const rides = await Ride.aggregate([
{ "$match": { "status": "waiting" } },
{ $project: { "trips": { $objectToArray: '$trips' } } },
{ $project: { 'trips.v.passengers': 0 } },
{ $project: { "trips": { $arrayToObject: '$trips' } } }
]);
nodejs javascript mongodb mongoose
Here's the returned document
{
"_id": "632a1669279c86f4ab3a4bf5",
"trips": {
"2019": {
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bfe"
},
"2020": {
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bfc"
},
"2021": {
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bfa"
},
"2022": {
"available_seats": 5,
"_id": "632a1669279c86f4ab3a4bf8"
}
}
}
This question already has answers here:
Mongodb $push in nested array
(4 answers)
Closed 1 year ago.
I want to add a new data object inside of a nested document in MongoDB. this is what my schema look like :
const transactionSchema = new Schema({
transactionType: {type: String, require: true, trim: true},
senderWallet: {type: String, require: true, trim: true},
recipientWallet: {type: String, require: true, trim: true},
amount: {type: Number, require: true, trim: true},
createdAt: {type: Date, require: true},
});
const walletHoldShema = new Schema({
balance: {type: Number},
transaction:[transactionSchema]
});
const walletSchema = new Schema({
createdAt: {type: Date, require: true},
userId: {type: String, require: true, trim: true},
walletAdrress: {type: String},
usdWallet: [walletHoldShema],
})
const Wallets = mongoose.model('wallet', walletSchema);
module.exports = Wallets
This is what my data look like right now
wallet: {
"_id": "60ca5e8e2c57463733b65f89",
"usdWallet": [
{
"_id": "60ca5ede2c57463733b65f8a",
"balance": 0,
"transaction": []
}
],
"createdAt": "2021-06-16T20:28:14.589Z",
"userId": "fb0df5c9a9eb",
"walletAdrress": "fb0df5c9a9eb",
"__v": 0
}
I have tried different solution like this one which I followed via stack :
const _id = req.params._id;
const transactionInput = {
transactionType: 'input',
senderWallet: req.body.sender,
recipientWallet: req.body.recipient,
amount: req.body.amount,
createdAt: Date.now(),
};
walletSchema.findOneAndUpdate({_id: _id}, {$push: {transaction: transactionInput}}, { new: true }, function(err, data){
if (err) {
res.send(err);
} else {
res.send(transactionInput);
}
});
// calling router
router.patch('/v1/sendUSD/', wallet.sendDollar
But I am getting this in my DB after my router is called :
"wallet": [
{
"_id": "60ca5e8e2c57463733b65f89",
"usdWallet": [
{
"_id": "60ca5ede2c57463733b65f8a",
"balance": 0,
"transaction": []
}
],
"createdAt": "2021-06-16T20:28:14.589Z",
"userId": "fb0df5c9a9eb",
"walletAdrress": "fb0df5c9a9eb",
"__v": 0
},
{
"_id": "60ca5e8e2c5746373",
"usdWallet": [
{
"_id": "60ca5ede2c57463733b65f8a",
"transaction":
[
{
"transactionType": "input",
"senderWallet": "fcdhjgknsdfihber",
"recipientWallet": "mbindu",
"amount": "50.0",
"createdAt": 1623881009306
}
]
}
],
},
]
But what I want is this :
wallet: [
{
"_id": "60ca5e8e2c57463733b65f89",
"usdWallet": [
{
"_id": "60ca5ede2c57463733b65f8a",
"balance": 0,
"transaction":
[
{
"transactionType": "in",
"senderWallet": "fcdhjgknsdfihber",
"recipientWallet": "mbindu",
"amount": "50.0",
"createdAt": 1623881009306
},
{
"transactionType": "out",
"senderWallet": "fcdhjgknsdfihber",
"recipientWallet": "mbindu",
"amount": "50.0",
"createdAt": 1623881009306
},
]
}
],
"createdAt": "2021-06-16T20:28:14.589Z",
"userId": "fb0df5c9a9eb",
"walletAdrress": "fb0df5c9a9eb",
"__v": 0
},
]
having an array inside an array is always a bad idea. that said, you can push transaction into usdWallet through:
walletSchema.findOneAndUpdate({ _id: _id }, { $push: { "usdWallet.$.transaction": transactionInput }}, { new: true }, function(err, data) {
if (err) {
res.send(err);
} else {
res.send(transactionInput);
}
});
Good Morning,
So I seem to have an issue with populating my fields with Node.js and Mongoose. It is just turning up a blank array:
Result: (JSON)
[
{
"courseassignments": [],
"_id": "5db56ceb4cc2c92824ae2651",
"name": "Garibaldi",
"website": "www.garibaldi.org.uk",
"__v": 0
},
{
"courseassignments": [],
"_id": "5db56d074cc2c92824ae2652",
"name": "Kirk Hallam",
"website": "www.kirkhallamacademy.co.uk",
"__v": 0
}
]
Below is the code I am using the call the populate function, plus the models that I am using for each of the data. It is very weird.
*school.js (Model) *
// Require modules within our file:
const mongoose = require('mongoose')
const schoolSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true,
trim: true
},
website: {
type: String,
required: true,
trim: true
},
logo: {
type: Buffer
},
courseassignments: [{
type: mongoose.Schema.Types.ObjectID,
ref: 'CourseAssignment'
}]
})
// Export the user to use within other files:
const school = mongoose.model('School', schoolSchema)
module.exports = school
courseassignment.js (Model)
// Require modules within our file:
const mongoose = require('mongoose')
const courseAssignmentSchema = new mongoose.Schema({
school: {
type: mongoose.Schema.Types.ObjectID,
required: true,
ref: 'School'
},
course: {
type: mongoose.Schema.Types.ObjectID,
required: true,
ref: 'Course'
}
})
// Export the user to use within other files:
const courseAssignment = mongoose.model('CourseAssignment', courseAssignmentSchema)
module.exports = courseAssignment
* Code to fetch data: (within app.js)*
router.get('/school', async (req, res) => {
const schools = await School.find({}).populate({ path: 'courseassignments' })
res.send(schools)
})
I would remove the courseassigment ref from the School model, and would take advantage of virtual populate.
So here are the steps:
school.js (school model - as you see I removed courseassignments ref, and added options for virtual features)
const mongoose = require('mongoose')
const schoolSchema = new mongoose.Schema({
name: {
type: String,
required: true,
unique: true,
trim: true
},
website: {
type: String,
required: true,
trim: true
},
logo: {
type: Buffer
}
}, {
toJSON: { virtuals: true },
toObject: { virtuals: true }
})
schoolSchema.virtual("courseassignments", {
ref: "CourseAssignment",
foreignField: "school",
localField: "_id"
})
const school = mongoose.model('School', schoolSchema)
module.exports = school
At this point when you hit the schools endpoint, your response will be like this.
( I only show one item to be short.)
[
{
"_id": "5db5a809cfc9951770d5078a",
"name": "school 1",
"website": "school 1 website",
"__v": 0,
"courseassignments": [
{
"_id": "5db5a892cfc9951770d50790",
"school": "5db5a809cfc9951770d5078a",
"course": "5db5a847cfc9951770d5078d",
"__v": 0
},
{
"_id": "5db5a89ccfc9951770d50791",
"school": "5db5a809cfc9951770d5078a",
"course": "5db5a851cfc9951770d5078e",
"__v": 0
},
{
"_id": "5db5a8a1cfc9951770d50792",
"school": "5db5a809cfc9951770d5078a",
"course": "5db5a858cfc9951770d5078f",
"__v": 0
}
],
"id": "5db5a809cfc9951770d5078a"
},
...
...
]
And if you also want to access to the Course name (which I think would be good),
courseassigment.js
const mongoose = require('mongoose')
const courseAssignmentSchema = new mongoose.Schema({
school: {
type: mongoose.Schema.Types.ObjectID,
required: true,
ref: 'School'
},
course: {
type: mongoose.Schema.Types.ObjectID,
required: true,
ref: 'Course'
}
}, {
toJSON: { virtuals: true },
toObject: { virtuals: true }
})
courseAssignmentSchema.pre(/^find/, function (next) {
this.populate({
path: 'course'
});
next();
});
// an index may be required like this
//courseAssignmentSchema.index({ school: 1, course: 1 }, { unique: true });
const courseAssignment = mongoose.model('CourseAssignment', courseAssignmentSchema)
module.exports = courseAssignment
And with this the result will contain the course related fields like course name.
[[
{
"_id": "5db5a809cfc9951770d5078a",
"name": "school 1",
"website": "school 1 website",
"__v": 0,
"courseassignments": [
{
"_id": "5db5a892cfc9951770d50790",
"school": "5db5a809cfc9951770d5078a",
"course": {
"_id": "5db5a847cfc9951770d5078d",
"name": "course 1",
"__v": 0
},
"__v": 0,
"id": "5db5a892cfc9951770d50790"
},
{
"_id": "5db5a89ccfc9951770d50791",
"school": "5db5a809cfc9951770d5078a",
"course": {
"_id": "5db5a851cfc9951770d5078e",
"name": "course 2",
"__v": 0
},
"__v": 0,
"id": "5db5a89ccfc9951770d50791"
},
{
"_id": "5db5a8a1cfc9951770d50792",
"school": "5db5a809cfc9951770d5078a",
"course": {
"_id": "5db5a858cfc9951770d5078f",
"name": "course 3",
"__v": 0
},
"__v": 0,
"id": "5db5a8a1cfc9951770d50792"
}
],
"id": "5db5a809cfc9951770d5078a"
},
...
...
]
Docs:
https://mongoosejs.com/docs/tutorials/virtuals.html
https://mongoosejs.com/docs/populate.html#populate-virtuals