I have a schema that looks like this
const CustomersSchema = new mongoose.Schema({
merchant:{
type: mongoose.Schema.Types.ObjectId,
required:true,
ref: 'Merchent'
},
idCustomer:{
type: String,
required: true,
trim:true,
//unique: true
},
customerData:{
type:Object,
required:true
}
});
and customerData looks like
{
"name": "Other",
"birthday": null,
"age": null,
"createdAt": "2019-04-01T20:01:04.000Z",
"email": null,
"visits": 0,
"payments": "0.00",
"lastVisit": "2019-12-16T12:58:09.000Z",
}
Is there a way I can query by customerData.name and if it doesn't exist it won't cause errors
Right now I do
let query = {
merchant: merchant._id,
'customerData.name': req.query.name
}
Customer.find(query)
.then(...)
but this way if there is no query.name no documents return
Edit: also regex won't do because I will need to search by numbers(ex: customerData.visits) in the future
It seems like you need to return the document whether or not you have query.name and if there is query.name the query will consider that and if there is no query.name it will not take that into account. So, you can setup a small logic for that. Something like this:
let query = {
merchant: merchant._id
};
// we only set the query for the customerData.name if query.name exist
if(req.query.name) {
query['customerData.name'] = req.query.name;
}
Customer.find(query)
.then(...)
Related
I have some code in javascript with moongooes that I used in mongo DB to store a data
Sometimes I need to delete all the objects in array
and get a clean array
this is my schema
const orderSchema =new Schema({
date: {
type: Date,
},
OrderNumber: {
type: String,
required: true
},
City: {
type: String,
required: true
},
Address: {
type: String,
required: true
},
Phone: {
type: String
},
Country: {
type: String
},
Name: {
type: String,
required: true
},
Trackingnumber: {
type: String
},
ZipCode: {
type: Number
},
Province: {
type: String,
},
fulfillmentOrders:{
type: String,
},
Quantity: {
},
});
Holde:[
orderSchema
],
module.exports = mongoose.model('User', userSchema);
and my data on mongo looks like this
"Holde": [
{
"OrderNumber": "gid://shopify/Order/4958122475753",
"City": "xxxx",
"Address": "xxxx",
"Phone": "",
"Country": "xxx",
"Name": "xxx",
"Trackingnumber": "0",
"ZipCode": xxxx,
"fulfillmentOrders": "gid://shopify/FulfillmentOrder/6034089509097",
"Quantity": [
{
"quantity": 1,
"product": {
"id": "gid://shopify/Product/7909915590889"
},
"variant": {
"sku": "11111"
}
}
],
"_id": {
"$oid": "6389b12faaade0788141bf4f"
}
I try to delete all the objects in my array
whit this code
const User = require('../model/User');
const foundUse= await User.findOne({ "user":req.body.user}).exec();
await foundUse.updateOne({
Holde :{
$pull: {'_id':6389b12faaade0788141bf4f},
}
},
)
and expect to get "hold":[]
but actually
I get
"Holde": [
{
"_id": {
"$oid": "6389d882afbc458cc1c1af23"
}
}
],
It's pretty normal because you are updating your User with theses data.
With mongoose, the way to propperly delete item is .deleteMany(), but in your case it will only delete the User (and you seems to want to delete only the order).
You can then filter user's orders and re-assign it without the found order, using FindOneAndUpdate, like:
const User = require('../model/User');
const foundUser = await User.findOne({ "user": req.body.user });
const result = await User.updateOne({
Holde: foundUser?.Holde.filter(holde => holde._id !== HOLD_ID_TO_DELETE)
});
Where HOLD_ID_TO_DELETE is the id to delete (you seems to pass the whole user object with all orders at the moment)
But not it would be prettier and more maintenable to create an Order collection, linked to your User one using ref.
I have a collection called Orders that contains this schema:
const mongoose = require('mongoose');
const orderSchema = new mongoose.Schema({
user: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: true
},
restaurant: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Restaurant',
required: true
},
dishes: [
{
dish: {
type: mongoose.Schema.Types.ObjectId,
ref: 'Dish'
},
amount: Number
}
],
price: {
type: Number,
required: true
},
comment: {
type: String,
required: false
},
status: {
type: String,
enum: ['PROCESSING', 'CANCELLED', 'COMPLETED', 'ERROR'],
default: 'PROCESSING'
},
timestamp: {
type: Date,
default: Date.now
}
})
module.exports = mongoose.model('Order', orderSchema);
Inside my router, I have this code:
let orders = await Order.find({restaurant: restaurantID, status:'PROCESSING'}).populate('dishes._id').exec()
Order.find does not throw an exception, but it isnt working either.
I want the res.body to look like this:
{
"_id": "objectID",
"user": "objectID",
"restaurant": "objectID",
"dishes": [
{
"amount": number,
"dish": {
//dish object
}
},
...
],
//other order properties
},
...
]
But for some reason the dishes array looks like this:
"dishes": [
{
"amount": 1,
"_id": "6184e848e6d1974a0569783d"
}
],
What am I doing wrong?
I know that if populate() worked the res.body dishes array would not have a property called 'dish' and instead have a property called _id that would contain the dish object, but this shouldnt be hard to change once populate() works.
EDIT:
I realised that my createOrder route could be part of the problem since it ignores my schema and uses an id property for the objectID instead of dish. The array I save to the DB contains a property called id for the id instead of dish, but shouldnt my schema throw an exception when i try to save something like this to my database?
At first glance, I think the problem might be that you have a syntax problem.
Try
.populate('dishes').exec()
instead of
.populate('dishes._id').exec()
I am using Mongoose with Javascript (NodeJS) to read/write to MongoDB. I have a Document (Parent) that has a bunch of Subdocuments (Children) in it. Both my Document and Subdocuments have validation (required: true and a function that validates that the user puts text in the field) defined in their Model.
When attempting to push a new Subdocument into the database, Mongoose rejects my push because validation fails on the Document. This has perplexed me as I am not trying to create a new Document with Subdocument, I am simply trying to push a new Subdocument into an existing Document.
Here is my (example) Mongoose Model:
const mongoose = require('mongoose');
const requiredStringValidator = [
(val) => {
const testVal = val.trim();
return testVal.length > 0;
},
// Custom error text
'Please supply a value for {PATH}',
];
const childrenSchema = new mongoose.Schema({
childId: {
type: mongoose.Schema.Types.ObjectId,
},
firstName: {
type: String,
required: true,
validate: requiredStringValidator,
},
lastName: {
type: String,
required: true,
validate: requiredStringValidator,
},
birthday: {
type: Date,
required: true,
},
});
const parentSchema = new mongoose.Schema(
{
parentId: {
type: mongoose.Schema.Types.ObjectId,
},
firstName: {
type: String,
required: true,
validate: requiredStringValidator,
},
lastName: {
type: String,
required: true,
validate: requiredStringValidator,
},
children: [childrenSchema],
},
{ collection: 'parentsjustdontunderstand' },
);
const mongooseModels = {
Parent: mongoose.model('Parent', parentSchema),
Children: mongoose.model('Children', childrenSchema),
};
module.exports = mongooseModels;
I can successfully push a new Child Subdocument into the Parent Document via the following MongoDB command:
db.parentsjustdontunderstand.update({
firstName: 'Willard'
}, {
$push: {
children: {
"firstName": "Will",
"lastName": "Smith",
"birthday": "9/25/1968" }
}
});
However, when I follow the Mongoose documentation Adding Subdocs to Arrays and try to add it via Mongoose, it fails.
For testing purposes, I am using Postman and performing a PUT request against an endpoint.
The following is req.body:
{
"firstName": "Will",
"lastName": "Smith",
"birthday": "9/25/1968"
}
My code is:
const { Parent } = require('parentsModel');
const parent = new Parent();
parent.children.push(req.body);
parent.save();
What I get back is:
ValidationError: Parent validation failed: firstName: Path `firstName` is required...`
and it lists all of the Parent Document's validation requirements.
I could use some help on what I am doing wrong. For the record, I have looked at this answer on Stackoverflow: Push items into mongo array via mongoose but most examples I see do not show or discuss validation in their Mongoose Models.
EDIT 1
Based on feedback from #j-f, I modified my code to below (moving the body out of req.body and just creating it in code for testing purposes. When I attempt to push the update the way recommended, the record gets inserted, however, I still get a validation error thrown to console:
const parent = await Parent.findOne({firstName: 'Willard'});
const child = {
children: {
"firstName": "Will",
"lastName": "Smith",
"birthday": "9/25/1968"
}
}
parent.children.push(child);
parent.save();
ValidationError: Parent validation failed: children.12.firstName: Path `firstName` is required., children.12.lastName: Path `lastName` is required., children.12.birthday: Path `birthday` is required.
ANSWER
#J.F is correct and I am wrong.
This is incorrect:
const child = {
children: {
"firstName": "Will",
"lastName": "Smith",
"birthday": "9/25/1968"
}
}
This is correct:
const child = {
"firstName": "Will",
"lastName": "Smith",
"birthday": "9/25/1968"
}
The record gets inserted into the database and saved but since I was initiating this as a PUT request, I was not properly responding after a successful save with an HTTP 200 OK. Correct code below for the entire solution, however, keep in mind the res.status code is only necessary in this scenario because I was imitating the code via a PUT request.
Mongoose Model:
const mongoose = require('mongoose');
const requiredStringValidator = [
(val) => {
const testVal = val.trim();
return testVal.length > 0;
},
// Custom error text
'Please supply a value for {PATH}',
];
const childrenSchema = new mongoose.Schema({
childId: {
type: mongoose.Schema.Types.ObjectId,
},
firstName: {
type: String,
required: true,
validate: requiredStringValidator,
},
lastName: {
type: String,
required: true,
validate: requiredStringValidator,
},
birthday: {
type: Date,
required: true,
},
});
const parentSchema = new mongoose.Schema(
{
parentId: {
type: mongoose.Schema.Types.ObjectId,
},
firstName: {
type: String,
required: true,
validate: requiredStringValidator,
},
lastName: {
type: String,
required: true,
validate: requiredStringValidator,
},
children: [childrenSchema],
},
{ collection: 'parentsjustdontunderstand' },
);
const mongooseModels = {
Parent: mongoose.model('Parent', parentSchema),
Children: mongoose.model('Children', childrenSchema),
};
module.exports = mongooseModels;
The following is req.body:
{
"firstName": "Will",
"lastName": "Smith",
"birthday": "9/25/1968"
}
Code is:
const { Parent } = require('parentsModel');
const parent = await Parent.findOne({firstName: 'Willard'});
parent.children.push(req.body);
parent.save((err, doc) => {
if (err) {
res.status(500).json({
message: 'Error finding active projects',
error: err,
});
} else {
res.status(200).json(doc);
}
});
You can push the children into the parent using a mongo query because into the update, the first object is to find the document where do the push.
The syntaxis is like: update({query},{update},{options}). So you are looking for a document with firstName: 'Willard' and adding the children into it.
Here everything is ok, all fields exists, the parent exists into collection so there is no problem.
But using
const parent = new Parent();
parent.children.push(req.body);
parent.save();
Your parent object is empty (unless the constructor fill all fields, but this is not a good idea I think).
If you try this:
var parent = await model.findOne({firstName: 'Willard'})
parent.children.push(req.body);
parent.save();
Then should works.
In this case, the object parent is retrieved from collection, so it contains all necessary fields.
I'm going to edit to explain better why is not thethe two queries are not the same.
Basically the child object you are trying to save has not the same structure as db.collection.update one. Note that the object child you have create to insert into collection has only one property called children. It doesn't have the necessary properties like firstName...
I'm going to use pure JS to can see what is the console.log() output and to see the differences.
Your mongo query push an object like this (translated to js language):
var array = []
array.push(
children = {
"firstName": "Will",
"lastName": "Smith",
"birthday": "9/25/1968"
}
)
console.log(array)
But you are creating the object in this way:
const child = {
children: {
"firstName": "Will",
"lastName": "Smith",
"birthday": "9/25/1968"
}
}
console.log(child)
Do you see now the difference? One object is the children itself the other object has the property children with the necessary fields.
So let's combinate the two pieces of code:
const child = {
children: {
"firstName": "Will",
"lastName": "Smith",
"birthday": "9/25/1968"
}
}
const children = {
"firstName": "Will",
"lastName": "Smith",
"birthday": "9/25/1968"
}
var array = [child,children]
console.log(array)
So, for your code, if you use:
parent.children.push(child.children);
parent.save();
should works. BUT, the best ways is not created the object children inside const child
Try using:
const child = {
"firstName": "Will",
"lastName": "Smith",
"birthday": "9/25/1968"
}
parent.children.push(child);
parent.save();
I have a problem understanding why $push creates only one element in nested array and shows only the id of that element
const mongoose = require('mongoose');
let historySchema = new mongoose.Schema({
time: {
type:String
}
})
//users schema
let userSchema = new mongoose.Schema({
name:{
type:String,
},
dob:{
type:String,
},
email:{
type:String,
},
noOfpeopleClimbing:{
type: Number,
default:0
},
details:{
type:String,
},
status:{
type: Boolean,
default: false
},
timeIn:{
type: Number,
default: 0
},
timeOut:{
type: Number,
default: 0
},
timeFinal:{
type: Number,
default: 0
},
timeHistory:[{historySchema}]
})
let User = module.exports = mongoose.model("User", userSchema);
I am talking about timeHistory:[{historySchema}] the rest you can ignore.
I am running the next test:
app.post('/users/reset_timeIn/:id', function(req,res){
Users.findOneAndUpdate({_id:req.params.id},{
timeIn:0,
timeOut:0,
timeFinal:0,
timeHistory:{
$push: {time:"some text" }
},
status:false,
noOfpeopleClimbing:0
},function(err){
if(err){
console.log(err);
return
}else{
res.redirect(req.get('referer'));
}
})
});
time:"some text"is just for testing. The output of this test, no matter how many times I have pushed an elment is like this:
{
"noOfpeopleClimbing": 0,
"status": false,
"timeIn": 0,
"timeOut": 0,
"timeFinal": 0,
"_id": "5fa3faa68b302530fcb6bba5",
"name": "Name",
"dob": "1666-02-02",
"email": "name#name.com",
"__v": 0,
"details": "name",
"timeHistory": [
{
"_id": "5fa44343aece7d37dc532fd9"
}
]
},
The "timeHistory" should containt the id and the field time:"some text", right? And if i push another record to have that one as in the second object of that array? right? Please help
Looks like you're not using $push correctly. Try passing the correct path to the operator, which in your case is:
Users.findByIdAndUpdate(req.params.id, {
timeHistory: {
$push: {
time: "some text"
}
}
})
Here's an example on mongoplayground.
I am developing a server using Expressjs, Mongodb and Mongoose. I need to push an element (a string) into the "tweets" array which is inside an object (a friend) which is in turn inside the "friends" array which is inside a "user" Object which document in the "users" collection. Here is an example of how my documents in the Mongodb collection looks like:
{
"loggedIn": true,
"_id": "5f91ef0ce75d3b1d40539da0",
"username": "username",
"email": "a#h.com",
"password": "$2a$10$9krWS9Kq5024lRTexqaweePrn8aughepqTkaj3oA48x0fJ2ajd79u",
"dateOfBirth": "2002-12-07",
"gender": "male",
"friends": [
{
"tweets": [],
"_id": "5f91effae75d3b1d40539da7",
"username": "Jonas"
},
],
"__v": 0
}
I need to pick the specified username from the "Users" arrary first and then access "friends" array within this user and then pick the right friend object and finally push the tweet on $position: 0 in this array. I I tried to achieve that as shown in this code and I could access the friend object with the given friendUsername
await Users.updateOne(
{ username: req.params.username },
{
$push: {
friends: {
$elemMatch: {
username: req.params.friendUsername,
},
},
},
}
);
And now the question is how to access the "tweets" array inside $elemMatch and push the req.body.tweet at $position: 0 into it?
Here is how I would solve your issue, I first would re-define the way I am defining schemas.
My User schema would look something like the following
User.js
const mongoose = require('mongoose')
const UserSchema = mongoose.Schema({
...
friends: {
type: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
required: true,
default: []
},
tweets: {
type: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'Tweet'
}],
required: true,
default: []
},
...
}, {timestamps: true})
module.exports = mongoose.model('User', UserSchema)
User.js
const mongoose = require('mongoose')
const TweetSchema = mongoose.Schema({
...
text: {
type: String,
required: true
},
tweeter: {
type: mongoose.Schema.Types.ObjectId,
required: true,
ref: 'User'
},
likes: {
type: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
required: true,
default: []
},
...
}, {timestamps: true})
module.exports = mongoose.model('Tweet', TweetSchema)
This assumes that every user can have tweets and that a User can be friend with another User
And now if someone tweets something you can do something like
const Tweet = require('./Tweet.js')
const User = require('./User.js')
let tweet = new Tweet({
text: "My first tweet!",
tweeter: "ID Of user who is posting the tweet"
})
tweet.save()
// Now update the user who tweeted
User.findOneAndUpdate()
User.updateOne({ _id: "ID Of user who is posting the tweet" }, { $push: { tweets: tweet._id } })
and now whenever you request a user all of his friends will be referenced and all of their tweets will also be referenced! if you want to see the actual tweets then use something like .populate() here are the docs for .populate() https://mongoosejs.com/docs/populate.html
Keep in mind is really a good practice to only return the actual ids and your frontend takes care of requesting the appropriate objects from their perspective endpoints. And if you wish to reduce network calls then the frontend would cache the data.
If the above doesn't help and you still would like to achieve your goal with your schemas then something like this should work (assuming your schema is called User)
let tweetObj = {}
User.updateOne({_id: 'your userid'}, {$push: {"friends.$.tweets": tweetObj}})
NOTE: I have omitted callbacks as they are irrelevant to the question