Home > Net >  mongodb pull is not removing all items
mongodb pull is not removing all items

Time:01-13

I'm working on a project in nodejs using mongodb as my database. I'm trying to get rid of elements within my array that have dates before today. The problem that I'm having is that at most 5 elements are being deleted. I want all elements that meet this criteria to be deleted. Also, when I don't have user.possible.pull(items._id) const result = await user.save() all elements that meet this criteria are shown in my deletePossible array. However, when I do have user.possible.pull(items._id) const result = await user.save() at most 5 are being shown as well.

In my database, my User document looks like:

_id: '',
name: '',
possible: Array
0 Object
      date: "Tues Jan 10 2023",
      _id: "63c0b169b6fa12ac49874a13"
1 Object  
      date: "Wed Jan 11 2023",
      _id: "63c0b172b6fa12ac49874a32"
...

My code:

     const user = await User.findById(args.userId)
     const deletePossible = [];

     for (var items of user.possible) {
      if ( new Date(items.date) <  new Date().setHours) {
        deletePossible.push(items._id)
        user.possible.pull(items._id)
        const result =  await user.save()
      }
    }
`
    console.log(deletePossible)   

I've tried a number of things such as:

for (var item of deletePossible) {
        user.possible.pull(item)
        const result =  await user.save()
        }

following deletePossible.push(items._id), and

const userInfo = await User.updateOne( { _id: args.userId}, {possible:{$pull:[...deletePossible] }} )

which removes all of the arrays from possible regardless of if it's contained within deletePossible and then adds a random _id. Nothing I have tried seems to work. Does anyone have any idea why this is happening and how to get this to work properly? I would really appreciate any help or advice. Thank you!

CodePudding user response:

You can simply filter user.possible and save the updated User:

const user = await User.findById(args.userId);
if (!user) return;
// Change the condition based on your needs
user.possible = user.possible.filter(p => new Date(p.date) >= new Date());
await user.save();

CodePudding user response:

The core of the issue appears to not be related to Mongo or Mongoose really, but is rather just a standard algorithmic logic problem.

Consider the following code, which iterates over an array, logs each element, and removes the third element when it arrives at it:

const array = [0, 1, 2, 3, 4];
for (const element of array) {
  console.log(element);
  if (element === 2) {
    array.splice(2, 1); // remove the element at index 2 from the array
  }
}

This code outputs:

0
1
2
4

Notice anything interesting? 3 has been skipped.

This happens because deleting an element from an array causes everything in front of it to move up a position. So if you're looking at 2 and you delete it, then 3 moves into 2's place, and 4 moves into 3's place. So then if you look at the next position, you're now looking at 4, not 3. The code never sees the 3.

This is why you should never change an array while iterating over it. A lot of languages won't even allow you to (if you're using iterators), they'll throw some sort of "underlying collection was modified during iteration" error. You can make it work if you know what you're doing (often just by iterating over the array backwards), but there are usually better solutions anyway, like using Array.prototype.filter().

One easy solution is to iterate over a copy of the array, so that when you do the delete, the array you're iterating over (the copy) isn't changed. That would look like this:

for (const item of [...user.possible]) {
  if (/* some condition */) {
    user.possible.pull(item._id);
  }
}

Another problem with your code: new Date().setHours will always evaluate to NaN since setHours is a function and converting a function to a number always results in NaN. I suspect this is just a typo you introduced while struggling with the original issue.

The suggestion to use filter() is even better.

  • Related