Skip to content

Conversation

addaleax
Copy link
Collaborator

Diff probably best viewed with whitespace changes hidden.


Follow-up to c865b66 (#3239), which introduced this bug:
Handle updates to arrays in the case in which the updated array
(or another updated path) contains dots or starts with a dollar sign,
using alternatives to $getField and $setField which can work on
arrays ($arrayElemAt for the former, $concatArrays + $slice for
the latter).

Description

Checklist

  • New tests and/or benchmarks are included
  • Documentation is changed or added

Motivation and Context

  • Bugfix
  • New feature
  • Dependency update
  • Misc

Open Questions

Dependents

Types of changes

  • Backport Needed
  • Patch (non-breaking change which fixes an issue)
  • Minor (non-breaking change which adds functionality)
  • Major (fix or feature that would cause existing functionality to change)

@github-actions github-actions bot added the fix label Aug 25, 2022
…6011

Follow-up to c865b66 (#3239), which introduced this bug:
Handle updates to arrays in the case in which the updated array
(or another updated path) contains dots or starts with a dollar sign,
using alternatives to `$getField` and `$setField` which can work on
arrays (`$arrayElemAt` for the former, `$concatArrays` + `$slice` for
the latter).

/** Used to represent missing values, i.e. non-existent fields. */
const DoesNotExist = Symbol('DidNotExist');
const DoesNotExist = Symbol('DoesNotExist');
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Purely cosmestic, I think I just forgot to change the name while working on #3239.

path: [{ key: String(element.key), isArrayIndex }],
value: DoesNotExist,
});
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(This just mirrors the change above.)

path: [{ key: String(element.currentKey), isArrayIndex }],
value: DoesNotExist,
});
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is kind of an independent issue, but didn’t appear in tests so far because when building the query in the non-dots-and-dollars mode, duplicate entries in originalFields would not cause trouble. For example, if property a with value 42 was removed and then later another element renamed to a, originalFields would contain two entries for a, one claiming that it does not exist, one claiming that it had the value 42; the query would still (accidentally correctly) end up being { a: 42 }, since the property in the query doc was simply overwritten.

In dots-and-dollars mode, however, the different values in originalFields are turned into multiple $eq checks which are then $and-ed together, so obviously there would be a conflict between an $eq asserting that the field did not exist and an $eq asserting that the field had its previous value.

field: { $literal: key },
input,
},
},
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In retrospect, it feels a bit dumb to have assumed (or probably just not thought about) that $getField and $setField don’t work on arrays the way they do on objects.

if (equalityMatches.length === 1) {
query.$expr = equalityMatches[0];
} else if (equalityMatches.length > 1) {
query.$expr = { $and: equalityMatches };
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This part of the change is also a little independent, and only makes it so that $and: [singleEntry] is reduced to singleEntry, with the main purpose being making the test files a little more readable

expect(doc.generateUpdateUnlessChangedInBackgroundQuery()).to.deep.equal({
query: { _id: null, 'a.3': { $exists: false } },
updateDoc: { $set: { 'a.3': new Int32(4) } },
context('with a plain array name', function () {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests remain unchanged, only split into two context()s, one for plain names and one for dots-and-dollar names

},
},
],
});
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don’t necessarily mind having these fairly verbose/repetitive test object assertions, but if we feel that this is too noisy, I’m happy to take suggestions on how to improve that. I guess ideally some kind of snapshot testing where it’s easier to do bulk updates to expected results would be a good pick, but I don’t want to introduce entirely new paradigms here.

Copy link
Contributor

@lerouxb lerouxb left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good! And nice new tests.

@addaleax addaleax merged commit b605d7c into main Aug 25, 2022
@addaleax addaleax deleted the 6011-dev branch August 25, 2022 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants