Soft delete in Sails/Waterline - node.js

Trying to delete a user model using:
//Hard Delete
User.destroy({id:userId}, function(err, res){
//Hard Delete
})
I need to do a soft delete on User model and currently setting a flag isDeleted to true on delete and updating document:
updateUser.isDeleted = true;
User.update({id:userId}, updateUser, function(err, res){
Update project
})
and while fetching documents I am doing a check If isDeleted - true or not.
Is there any In-built feature provided by Sails or Waterline which I can configure to perform a soft delete and avoid updating and then fetching based on isDeleted flag?

you can use beforeFind() life cycle function for filter of soft deleted records
model: parrot,js
module.exports = {
attributes: {
// e.g., "Polly"
name: {
type: 'string'
},
// e.g., 3.26
wingspan: {
type: 'float',
required: true
},
// e.g., "cm"
wingspanUnits: {
type: 'string',
enum: ['cm', 'in', 'm', 'mm'],
defaultsTo: 'cm'
},
// e.g., [{...}, {...}, ...]
knownDialects: {
collection: 'Dialect'
},
isDeleted:{
type:'boolean'
}
},
beforeFind: function(values, cb) {
values.isDeleted = false;
cb();
}
}
ParrotController.js
module.exports = {
// getting default parrots isDeleted = true
list: function (req, res) {
Parrot
.find()
.exec(function(err, parrots) {
if(err) return res.send({ flag:false, data:[], message:"Error." });
if(parrots && parrots.length){
return res.send({ flag:true, data:parrots, message:"Success." });
}
else{
return res.send({ flag:false, data:[], message:"Parrot list is empty." });
}
});
}
};

There is no soft-delete feature built into sails, and I doubt there will be.
Here's a challenge: why not write your own? Waterline supports class methods! Of course you would have to do it for each model or create a service... which might be even more effective.

Related

Mongodb/mongoose omit a field in response [duplicate]

I have a NodeJS application with Mongoose ODM(Mongoose 3.3.1). I want to retrieve all fields except 1 from my collection.For Example: I have a collection Product Which have 6 fields,I want to select all except a field "Image" . I used "exclude" method, but got error..
This was my code.
var Query = models.Product.find();
Query.exclude('title Image');
if (req.params.id) {
Query.where('_id', req.params.id);
}
Query.exec(function (err, product) {
if (!err) {
return res.send({ 'statusCode': 200, 'statusText': 'OK', 'data': product });
} else {
return res.send(500);
}
});
But this returns error
Express
500 TypeError: Object #<Query> has no method 'exclude'.........
Also I tried, var Query = models.Product.find().exclude('title','Image'); and var Query = models.Product.find({}).exclude('title','Image'); But getting the same error. How to exclude one/(two) particular fields from a collection in Mongoose.
Use query.select for field selection in the current (3.x) Mongoose builds.
Prefix a field name you want to exclude with a -; so in your case:
Query.select('-Image');
Quick aside: in JavaScript, variables starting with a capital letter should be reserved for constructor functions. So consider renaming Query as query in your code.
I don't know where you read about that .exclude function, because I can't find it in any documentation.
But you can exclude fields by using the second parameter of the find method.
Here is an example from the official documentation:
db.inventory.find( { type: 'food' }, { type:0 } )
This operation returns all documents where the value of the type field is food, but does not include the type field in the output.
Model.findOne({ _id: Your Id}, { password: 0, name: 0 }, function(err, user){
// put your code
});
this code worked in my project. Thanks!! have a nice day.
You could do this
const products = await Product.find().select(['-image'])
I am use this with async await
async (req, res) => {
try {
await User.findById(req.user,'name email',(err, user) => {
if(err || !user){
return res.status(404)
} else {
return res.status(200).json({
user,
});
}
});
} catch (error) {
console.log(error);
}
In the updated version of Mongoose you can use it in this way as below to get selected fields.
user.findById({_id: req.body.id}, 'username phno address').then(response => {
res.status(200).json({
result: true,
details: response
});
}).catch(err => {
res.status(500).json({ result: false });
});
I'm working on a feature. I store a userId array name "collectedUser" than who is collected the project. And I just want to return a field "isCollected" instead of "collectedUsers". So select is not what I want. But I got this solution.
This is after I get projects from database, I add "isCollected".
for (const item of projects) {
item.set("isCollected", item.collectedUsers.includes(userId), {
strict: false,
})
}
And this is in Decorator #Schema
#Schema({
timestamps: true,
toObject: {
virtuals: true,
versionKey: false,
transform: (doc, ret, options): Partial<Project> => {
return {
...ret,
projectManagers: undefined,
projectMembers: undefined,
collectedUsers: undefined
}
}
}
})
Finally in my controller
projects = projects.map(i => i.toObject())
It's a strange tricks that set undefined, but it really work.
Btw I'm using nestjs.
You can do it like this
const products = await Product.find().select({
"image": 0
});
For anyone looking for a way to always omit a field - more like a global option rather than doing so in the query e.g. a password field, using a getter that returns undefined also works
{
password: {
type: String,
required: true,
get: () => undefined,
},
}
NB: Getters must be enabled with option { toObject: { getters:true } }
you can exclude the field from the schema definition
by adding the attribute
excludedField : {
...
select: false,
...
}
whenever you want to add it to your result,
add this to your find()
find().select('+excludedFiled')

Can I populate more fields after I have already l loaded a document on mongoose?

I want to populate aditional fields after I have already loaded one document.
I am loading my cart on a ecommerce I'm building, like this on all routes:
app.use(function(req, res, next) {
Cart.findOne({session: req.cookies['express:sess']})
.populate({ path: "products.product", select: "price name photos slug" })
.exec(function(err, cart){
if(err){
return err; //TODO: PAG 500
}
if(cart){
res.locals.cart = cart;
} else {
res.locals.cart = new Cart({ session: req.cookies['express:sess']});
}
next();
});
});
But at one page, I'd like to have more the fields description and addons from product loaded.
I tried to just load the products, but then I miss the associated information of quantity that I have on the cart
var CartSchema = new Schema({
products: [{
product: { type: Schema.ObjectId, ref : 'Product' },
quantity: { type: Number, default: 1}
}],
totalItems: { type: Number, default: 0},
message: { type: String },
});
I know I could break this up in more middlewares, according to my needs on fields on different pages, or reload the cart, and I could also just go through both arrays, the products I reload and the products I loaded on the cart and do some kind of merging, but I figured that mongoose might have some way to do this.
This can actually be done:
https://mongoosejs.com/docs/api.html#document_Document-populate
So in this specific case, I'd need to add this piece of code to the function that wants cart with more fields populated, and the middleware wouldn't need any changes
ES5 with callback:
var populate = [
{ path: "products.product", select: "price name photos slug" },
{ path: "card", select: "price name photo"}
];
var cart = res.locals.cart;
cart.populate(populate, function(err, populatedCart) {
res.locals.cart = populatedCart;
next();
});
With ES6:
const populate = [
{ path: "products.product", select: "price name photos slug" },
{ path: "card", select: "price name photo"}
];
res.locals.cart = await res.locals.cart.populate(populate).execPopulate();
You cannot "re-populate" a populated field.
How about a simple if to determine which fields you want to populated. For example:
app.use(function(req, res, next) {
var productSelect;
// This is just an example, you can get the condition from params, body, header..
if (req.body.isMoreField) {
productSelect = 'add more field in here';
}
else {
productSelect = 'less field here';
}
Cart
.findOne({
// ...
})
.populate({
// ...
select: productSelect,
// ...
})
.exec()
.then(function(cart) {
// ...
})
});

How to return data without _id when i use findByIdAndUpdate in mongoose?

Here is my code:
Task.findByIdAndUpdate({_id: req.params.task_id}, updateObj, {new: true}, function (err, updatedUser) {
if (err) {
return result.serverError(req, res)
}
result.success(req, res, updatedUser);
});
It will return all fields,but i don't need '__v' and '_id',how should i do?Thanks.
I think mongoose-hidden is your want.
You can also use the method in this link: MongoDB: output 'id' instead of '_id'
if you just want dismiss __v, you can use versionKey option for mogoose.
http://mongoosejs.com/docs/guide.html#versionKey
Document versioning can also be disabled by setting the versionKey to
false. DO NOT disable versioning unless you know what you are doing.
new Schema({..}, { versionKey: false });
var Thing = mongoose.model('Thing', schema);
var thing = new Thing({ name: 'no versioning please' });
thing.save(); // { name: 'no versioning please' }

Error-handling - 404 for remoteMethods

I have been able to easily set up a basic node.js api with the help of strongloop. I have been able to add custom routes using remoteMethods. However, I am a bit confused in setting up 404 for those routes. I have one route for model category named mature that takes one argument(categorId) and fetches all games under that category that have boolean value set to true for mature. The endpoint url is: http://localhost:3000/api/Categories/1004/games/mature. If I place a non existent categorId, it breaks. What would be the best way to setup routes to handle 404 for errors? For example, display "no such category id". Github REPO
common/models/category.js
Category.mature = function(id, limit) {
var app = this.app;
var Games = app.models.Games;
Category.findById(id, {}, function(err, category){
if (err) return callback(err);
//set limit
if (limit && limit > 5){
limit = 5;
}else if(limit === undefined){
limit = 5;
}
Games.find({
"where": {
categoryId: id,
mature: true,
gameId: {gt: hashids.decode(after)}
},
"limit": limit
}, function(err, gameArray) {
if (err) return callback(err);
callback(null, gameArr);
});
)};
Category.remoteMethod(
'mature', {
accepts: [
{arg: 'id', type: 'number', required: true},
{arg: 'limit',type: 'number',required: false}
],
// mixing ':id' into the rest url allows $owner to be determined and used for access control
http: {
path: '/:id/games/mature',
verb: 'get'
},
returns: {
root: true,
type: 'object'
}
}
);
};
Set err.statusCode to 404 before you call callback(err):
if(!category) {
var err = new Error('Category ID ' + id + ' does not exist.');
err.statusCode = 404;
callback(err);
}
This will result in:

sails js save many to many is only one way

i have two models:
user.js
module.exports = {
attributes: {
...
profile: {
model: 'Profile'
},
groups: {
collection: 'group',
via: 'users',
dominate: true
},
roles: {
collection: 'role',
via: 'users',
dominate: true
}
}};
and, group.js
module.exports = {
attributes: {
...
users: {
collection: 'user',
via: 'groups'
}
}};
when i try to add users to a group (when i select a group and add users to it), it works as it is supposed to,
var defer = q.defer();
baseDbContext.single(req, 'users')
.then(function(op){
if(!op.status || !op.obj) {
defer.resolve(notFound);
return;
}
op.obj.users = [];
_.each(req.users, function(item){
op.obj.users.add(item);
});
op.obj.save(function(err, obj){
if(err) defer.reject(operationResult().throwException(err));
else defer.resolve(operationResult().succeed());
});
});
return defer.promise;
but when i try to add groups to the user (when i select the user and add groups to it) it fails silently!!!
var defer = q.defer();
baseDbContext.single(req, 'groups')
.then(function(op){
if(!op.status || !op.obj) {
defer.resolve(notFound);
return;
}
op.obj.groups = [];
_.each(req.groups, function(item){
op.obj.groups.add(item);
});
op.obj.save(function(err, obj){
if(err) defer.reject(operationResult().throwException(err));
else defer.resolve(operationResult().succeed());
});
});
return defer.promise;
when i check it in sails console it shows :
throw new Error('Unknown rule: ' + ruleName);
Error: Unknown rule: dominate
this is a simple many to many insertion why would it fail?
(a note about code, the function baseDbContext.single finds a object based on its id and the second parameter is for populate)
Seems like you have a misprint, documentation says that the rule you need is writes as "dominant: true", not "dominate: true".

Resources