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.

losik123
  • 580
  • 5
  • 20

1 Answers1

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

Orelsanpls
  • 22,456
  • 6
  • 42
  • 69
  • 1
    It works (get's one comment) but I need to get all Book comments. Is there any way ? – losik123 Aug 24 '17 at 15:50
  • 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. – losik123 Aug 24 '17 at 16:03
  • 1
    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 :)! – losik123 Aug 24 '17 at 16:34