Validate array of strings in mongoose - mongodb

I wanted to validate each value from the request which is array of strings. Something like
emails: [ 'johndoe#gmail.com', 'jandoe#gmail.com' ]
Here is my schema
const UserSchema = mongoose.Schema({
name: {
type: String,
index: true,
required: true,
},
emails: [String],
});
In my validation I wanted to make sure that each email is not already exists in the database. I've tried the following
body("emails").custom((value, { req }) => {
return User.findOne({
emails: { $all: value },
_id: { $ne: req.params.id },
}).then((exists) => {
if (exists) {
return Promise.reject("Email already exists!");
}
});
});
But the problem is if I tried to post multiple emails in array the validation fails and the data will be inserted to db. How can I check if one of the emails already exists and reject the request?

In the docs of $in, it mentioned that:
If the field holds an array, then the $in operator selects the documents whose field holds an array that contains at least one element that matches a value in the specified array...
So you can solve it by:
User.findOne({
emails: { $in: value },
_id: { $ne: req.params.id },
})...

Related

Is something missing in these positional arguments for nested array updates?

I have a document that has 3 nested arrays. I'm updating the second nested array with this function:
// Append $set key
for(var key in u)
updates["scopes.$[].sections.$." + key] = u[key];
Proposal.findOneAndUpdate({
"scopes.sections._id": req.body.id // sectionId
}, {
$set: updates
}, { new: true })
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
I use a similar function to update the first nested array- scopes. That is working properly and updates the scope that matches. But for the second nested array only the first element of the array is being updated. I logged the id and the correct param is being passed in the req.body.
Is there something I'm missing in the update key- scopes.$[].sections.$.key ?
Edit with sample document and logs-
_id: 6079c199c5464b6296b113f6
name: ""
status: "outstanding"
hasAutomaticThreshold:false
isDiscount:true
discount: 0
discountPercentage: 0
taxRate: 9
companyId: 606f5e179cc0382ad6aacd84
clientId: 6070fa06dd505146ccfac9ec
projectId: 60736ed48fb2c869e0c9b33d
author: 606f5e259cc0382ad6aacd86
scopes: Array
0:Object
title: ""
isExpanded: true
_id: 6079c199c5464b6296b113f7
sections:Array
0:Object
title:"Section One"
description:""
isExpanded:false
_id: 6079c199c5464b6296b113f8
items: Array
1:Object
title:""
description:""
isExpanded:false
_id: 6079c1f8d3176462c0840388
items: Array
And this is what the logged req.body.id and updates object looks like:
6079c1f8d3176462c0840388 // ID
{ 'scopes.$[].sections.$.title': 'Section One' }
The positional $ operator will update single position, you need to use arrayFilters $[<identifier>],
// Append $set key
for(var key in u)
updates["scopes.$[].sections.$[s]." + key] = u[key];
Proposal.findOneAndUpdate(
{ "scopes.sections._id": req.body.id },
{ $set: updates },
{
arrayFilters: [{ "s._id": req.body.id }],
new: true
}
)
.then(response => {
console.log(response);
})
.catch(error => {
console.log(error);
});
Playground

Comparing JSON array with Mongo array document

Currently I have a mongoose model 'Event' that stores a list of UUIDs as participants that reference another model 'User'
.
participants: [{
_id: false,
id: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
tickets: {
type: Number,
min: 0,
},
}],
winners: [{
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
}],
.
.
Now I receive a request with the following JSON data to update my winners
{
"winners": [
"5f61132da98bac2a98487d79",
"5f611378a98bac2a98487d7a",
"5f611378a98bac2a98487d77"
]}
Is there a way to compare this array with participant field of that model and only allow entry of user ids that are participants? For example
const event = await Event.findOne({_id: _id}, 'participants.id -_id');
console.log(event.participants);
Output:
[
{ id: 5f61132da98bac2a98487d79 },
{ id: 5f611378a98bac2a98487d7a },
{ id: 5f6113b1a98bac2a98487d7b }
]
console.log(req.body.rewards);
[
'5f61132da98bac2a98487d79',
'5f611378a98bac2a98487d7a',
'5f611378a98bac2a98487d77'
]
Clearly the last UUID is not matching (is not a participant) so should be discarded and other two remaining should be updated in winners field. JSON object can be made flexible if needed.
Using aggregation framework does not really help me on this problem, and it might be very costly to project specific elements for a single document only. A work around solution would be using arrays.
const event = await Event.findOne({
_id: _eventId,
rewards: { $elemMatch: { _id: _rewardId }}
}, 'participants.id -_id');
const allParticipants = []
(event.participants).forEach((item) => {
allParticipants.push(item.id.toString());
});
const validIds = (req.body.winners).filter(item => allParticipants.includes(item));
This will check for arrays using includes and filter and return matching array item from the request and participants in the event.

Is an ObjectId automatically generated for a nested object?

My schema is as follows:
const MessageType = {
// ...
oAuth: { provider: String, id: String },
attachments: [ {name: String, contentType: String} ],
// ...
}
MessageSchema = new mongoose.Schema(MessageType, { timestamps: true});
Messages = mongoose.model("Message", MessageSchema);
When I insert a new Message document using Messages.create, an ObjectId (_id) is also generated for attachments, in addition to my name and contentType fields, ie:
[ { name: "xxx", contentType: "yyy", _id: zzzzzz }]
Why is this happening, for attachments but not oAuth?
For avoiding that the _id was generated you must set the option _id: false, Also if you don't want to save the empty attachments object, you need to set default: undefined.
const MessageTypeSchema = new mongoose.Schema({
oAuth: {
type: String
},
attachments: {
type: [
{
type: String
}
],
_id: false,
default: undefined
}
});
Here the code that I used to test:
console.log('-------- Document with attachments --------');
new MessageTypeModel({
oAuth:'xxxxxxxxxxxxx',
attachments: ['teste.png','teste2.jpg']
}).save().then(result => {
console.log(result);
});
console.log('-------- Document without attachments --------');
new MessageTypeModel({
oAuth:'xxxxxxxxxxxxx'
}).save().then(result => {
console.log(result);
});
And here the result of execution:
Mongoose creates _id for single nested subdocuments or arrays, and your object field oAuth is not one of this cases:
Subdocuments are documents embedded in other documents. In Mongoose,
this means you can nest schemas in other schemas. Mongoose has two
distinct notions of subdocuments: arrays of subdocuments and single
nested subdocuments.
Each subdocument has an _id by default. Mongoose
document arrays have a special id method for searching a document
array to find a document with a given _id.
var childSchema = new Schema({ name: 'string' });
var parentSchema = new Schema({
// Array of subdocuments
children: [childSchema],
// Single nested subdocuments. Caveat: single nested subdocs only work
// in mongoose >= 4.2.0
child: childSchema
});
Link of Mongoose documentation: Mongoose SubDocs
You can define _id : false in attachments array.
const MessageType = {
// ...
attachments: [ {name: String, contentType: String, _id: false} ],
// ...
}

MongoDB Document Validation in Meteor?

How would one approach doing this (https://docs.mongodb.com/v3.2/core/document-validation/):
db.createCollection( "contacts",
{ validator: { $or:
[
{ phone: { $type: "string" } },
{ email: { $regex: /#mongodb\.com$/ } },
{ status: { $in: [ "Unknown", "Incomplete" ] } }
]
}
} )
In this:
// database.js
import { Mongo } from 'meteor/mongo';
export const Test = new Mongo.Collection('Test');
Thanks
you first need to define your schema in meteor.
Lists.schema = new SimpleSchema({
name: {type: String},
incompleteCount: {type: Number, defaultValue: 0},
userId: {type: String, regEx: SimpleSchema.RegEx.Id, optional: true}
});
This example defines a schema with a few simple rules:
We specify that the name field of a list is required and must be a
string.
We specify the incompleteCount is a number, which on insertion is
set to 0 if not otherwise specified.
We specify that the userId, which is optional, must be a string that
looks like the ID of a user document.
It’s pretty straightforward to validate a document with a schema. We can write:
const list = {
name: 'My list',
incompleteCount: 3
};
Lists.schema.validate(list);
In this case, as the list is valid according to the schema, the validate() line will run without problems. If however, we wrote:
const list = {
name: 'My list',
incompleteCount: 3,
madeUpField: 'this should not be here'
};
Lists.schema.validate(list);
Then the validate() call will throw a ValidationError which contains details about what is wrong with the list document.

Find a Document where value matched in either field using Mongoose Middleware

I have a list of account connections between source and target accounts so my schema looks like
var ConnectionRequestSchema = new Schema({
sourceAccountId: {
type: Schema.ObjectId,
ref: 'Account'
},
targetAccountId: {
type: Schema.ObjectId,
ref: 'Account'
},
status: {
type: String,
enum: ['pending', 'accept', 'decline'],
trim: true
}
});
I want to query all documents where the sourceAccountId or the targetAccountId are equal to the queried accountId.
I saw this link how-to-find-a-document-where-either-one-or-another-field-matches-a-value which is relevant for find a docouments using the stand find method in Mongo.
User.findOne({
$or: [
{first_name: name},
{last_name: name},
],
}, function(err, user) {
})
But I would like to do this using Mongoose Middleware and I'm not sure how I would construct this condition.
already you figured out the solution, but you have to make some changes in query
ConnectionRequest.find({
$or: [
{sourceAccountId: "5736eac90a39c2547cb9d911"},
{targetAccountId: "5736eac90a39c2547cb9d911"},
],
}, function(err, connection) {
console.log(connection)
})
then finally you will get the result is array of documents