Validate unique text using Express Validator & Mongoose - mongodb

I have a mongodb collections named "articles"
I have configured below rules for validating "title" field of article while Updating the record.
validator.body('title').custom( (value, {req}) => {
console.log(value, req.params.id)
return Article.find({ title:value, _id:{ $ne: req.params.id } })
.then( article => {
if (article) {
return Promise.reject('Title already in use');
}
})
})
So basically it should check if "title" should not exists in the collection and it should not be the same ID as the one I am updating.
The line console.log(value, req.params.id) is printing proper Title and ID but the validation is always saying "Title already in use". Even though I use entirely different title that is not used at all.
Any idea what's wrong?

You should use findOne query for better performance and check data is null like as bellow.
validator.body('title').custom( (value, {req}) => {
console.log(value, req.params.id)
return Article.findOne({ title:value, _id:{ $ne: req.params.id } })
.then( article => {
if (article !== null) {
return Promise.reject('Title already in use');
}
})
})

Stupid mistake,
the
if (article) {
is supposed to be
if (article.length) {
And it worked fine.

Related

Why mongo updates first element in db despite the condition

tell me pls why this code updates firs element in db despite the condition. And i couldn't see any logs. Im using NestJs+Typeorm+Mongo
await this.workOrderModel
.updateOne({ createRequestId: 'someMockedVaue' }, { $set: { createFlowTrackingId: 'otherValue' } })
.then((updatedWorkOrder) => {
this.logger.verbose('Updated WorkOrder: %o', updatedWorkOrder);
})
.catch((err) => {
this.logger.error('Error on update WorkOrder: %o', err);
});
I understood what the problem is, if you specify the wrong field in the filter, it takes the first user in the db and changes the data

How to use azure cognitive search ? Can we use for global filteron table with pagination and sorting?

I am trying to return all the records that match my searchtext.So far I have only seen examples where we need to specify field name but I want return records if any of the field contains the searchtext, without specifying any field name. And I got to see $text , but unfortunatly it's not supported in cosmosdb API mongodb.
Can someone please help me to resolve this issue ?
Here is what I tried but failed
let querySpec = {
entity: "project",
$text: { $search: "\"test\"" } ,
$or: [{
accessType: "Private",
userName: userName
}, {
accessType: "Public"
}]
}
dbHandler.findandOrder(querySpec, sortfilter, "project").then(function (response) {
res.status(200).json({
status: 'success',
data: utils.unescapeList(response),
totalRecords:response.length
});
exports.findandOrder = function (filterObject, sortfilter, collectionname) {
return new Promise((resolve, reject) => {
return getConnection().then((db) => {
if (db == null) {
console.log("db in findandOrder() is undefined");
} else {
db.db(config.mongodb.dbname).collection(collectionname).find(filterObject).sort(sortfilter).toArray((err, res) => {
if (db) {
//db.close();
}
if (err) {
reject(err);
} else {
resolve(res);
}
});
}
});
});
};
Error:
{"message":{"ok":0,"code":115,"codeName":"CommandNotSupported","name":"MongoError"}}
I am using $regex as temperory solution as $text is not supported.
Please suggest ...
From the MongoDB manual, Create a Wildcard Index on All Fields:
db.collection.createIndex( { "$**" : 1 } )
but this is far from an ideal way to index your data. On the same page is this admonition:
Wildcard indexes are not designed to replace workload-based index planning.
In other words, know your data/schema and tailor indices accordingly. There are many caveats to wildcard indices so read the manual page linked above.
Alternately you can concatenate all the text you want to search into a single field (while also maintaining the individual fields) and create a text index on that field.

How to guarantee unique primary key with one update query

In my Movie schema, I have a field "release_date" who can contain nested subdocuments.
These subdocuments contains three fields :
country_code
date
details
I need to guarantee the first two fields are unique (primary key).
I first tried to set a unique index. But I finally realized that MongoDB does not support unique indexes on subdocuments.
Index is created, but validation does not trigger, and I can still add duplicates.
Then, I tried to modify my update function to prevent duplicates, as explained in this article (see Workarounds) : http://joegornick.com/2012/10/25/mongodb-unique-indexes-on-single-embedded-documents/
$ne works well but in my case, I have a combination of two fields, and it's a way more complicated...
$addToSet is nice, but not exactly what I am searching for, because "details" field can be not unique.
I also tried plugin like mongoose-unique-validator, but it does not work with subdocuments ...
I finally ended up with two queries. One for searching existing subdocument, another to add a subdocument if the previous query returns no document.
insertReleaseDate: async(root, args) => {
const { movieId, fields } = args
// Searching for an existing primary key
const document = await Movie.find(
{
_id: movieId,
release_date: {
$elemMatch: {
country_code: fields.country_code,
date: fields.date
}
}
}
)
if (document.length > 0) {
throw new Error('Duplicate error')
}
// Updating the document
const response = await Movie.updateOne(
{ _id: movieId },
{ $push: { release_date: fields } }
)
return response
}
This code works fine, but I would have preferred to use only one query.
Any idea ? I don't understand why it's so complicated as it should be a common usage.
Thanks RichieK for your answer ! It's working great.
Just take care to put the field name before "$not" like this :
insertReleaseDate: async(root, args) => {
const { movieId, fields } = args
const response = await Movie.updateOne(
{
_id: movieId,
release_date: {
$not: {
$elemMatch: {
country_code: fields.country_code,
date: fields.date
}
}
}
},
{ $push: { release_date: fields } }
)
return formatResponse(response, movieId)
}
Thanks a lot !

Mongoose remove one object from array of array

I have Mongoose schema like this:
{
......
project: [
{
Name: String,
Criteria:[
{
criteriaName:String,
}
]
}
]
......
}
And I want to remove one of the objects of criteria array which is in the array of project based on the object id
I tried the code following
criteria.findOneAndUpdate({
"_id": uid,
},{ $pull: { "project.Criteria": { _id: cid } } }, (err) => {
......
}
However this cannot work, it said "Cannot use the part (Criteria) of (project.Criteria) to traverse the element"
Do you need to do it in one query to the database? If not, the following solution may work for you:
criteria.findOne({ _id: uid })
.then((obj) => {
// Filter out the criteria you wanted to remove
obj.project.Criteria = obj.project.Criteria.filter(c => c._id !== cid);
// Save the updated object to the database
return obj.save();
})
.then((updatedObj) => {
// This is the updated object
})
.catch((err) => {
// Handle error
});
Sorry if the .then/.catch is confusing. I can rewrite with callbacks if necessary, but I think this looks a lot cleaner. Hope this helps!

Does the shape of json object have to match when you make a mongoose query with findOne?

I'm trying to findOne document that matches but the user returns as null when I remove the field imageUrl. Which in turn creates duplicate copies of my user when they log in. What am I misunderstanding here?
User.findOne({
google: {
id: profile.id,
imageUrl: profile.photos[0].value, // When I comment this out, existingUser is null.
}
})
.then((existingUser) => {
console.log('discovered', existingUser);
if (existingUser) {
console.log('FOUND ONE!');
return done(null, existingUser);
} else {
console.log('DID NOT FIND!');
new User({
google: {
id: profile.id,
imageUrl: profile.photos[0].value
}
})
.save()
.then(user => done(null, user));
}
});
What I am trying to do is findOne document based on the google id only.
Yes, the element (field) must complete match the value (including the sequence of the elements (fields)).
Your query must look like this (it means that google element must have the subelement id equal to profile.id):
{
"google.id": profile.id
}
Your query means that google element must be equal to {id: profile.id, imageUrl: profile.photos[0].value}.
When you removing the imageUrl element, your query then is:
{
google: { id: profile.id }
}
It means that google element must be equal to { id: profile.id }.