0

I have the following issue. I have some comments that are soft-deletable. So they have a flag is_deleted and it is set to true when a record is deleted.

My comments aren't an independent model, but they are a nested array in another model (simplified model):

let CommentSchema = new Schema({
    text: {
        type: String,
        required: true
    },
    modified_at: {
        type: Date,
        default: null
    },
    created_at: {
        type: Date,
        default: Date.now
    },
    is_deleted: {
        type: Boolean,
        default: false
    },
});

let BookSchema = new Schema({
    ...
    comments: [CommentSchema],
    ...
});

Now when I get all my Books with Books.find({}, (err, books) => {}) I wanted to filter out the deleted comments in:

BookSchema.pre('find', function() {
    this.aggregate(
    {},
    { $unwind: '$comments'},
    { $match: {'comments.is_deleted': false}})
}); 

But it does not work. Any idea how to write the query, so that it only return the non-deleted nested comments without creating an independent Comment collection?

EDIT: I didn't mention it's an endpoint where I access only one book object. The resource url is like this: /books/{bookId}/comments. But also nice to have if it would work when getting all book objects.

1 Answer 1

1

You can use the positional $ operator and filter directly from find. As greatly explaned by @Blakes Seven Here

BookSchema.find({
 'comments.is_deleted': false,
}, {
 'comments.$': 1,
});

EDIT:

As you said the $ operator only return one element, here is what the document says :

The positional $ operator limits the contents of an from the query results to contain only the first element matching the query document

There is two solution to your problem:

  1. Make an aggregate, which is usually slow to get executed by database
  2. Get the books and filter the comments using loops

BookSchema.find({
   'comments.is_deleted': false,
}).map(x => Object.assign(x, {
   comments: x.comments.filter(y => !y.is_deleted),
}));

The find get all books that have a non-deleted comment.

The map loop on each book.

We then remove the comments marked as deleted

Sign up to request clarification or add additional context in comments.

3 Comments

It works (get's one comment) but I need to get all Book comments. Is there any way ?
I didn't mention one important thing. This is an endpoint where I have only one book object. The resource is accesed via: /books/{bookId}/comments. Sorry, I will update my post above.
Ok, I went with the map/filter solution. Works ok, but if somebody has a more generic solution I would be happy to see it. Thanks @Grégory NEUT :)!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.