Virtual attributes - should they invoke the getters of fields they depend on?
Describe the bug Firstly, thanks for this library. It makes working with DDB really straightforward.
The issue I'm having is that watched attribute values passed into the get method of a virtual attribute do not appear to invoke their own get methods. This can lead to errors when attempting to read existing items that may not yet have the new attribute added.
Here's an example in the playground:
ElectroDB Version 2.13.1
ElectroDB Playground Link Playground
Entity/Service Definitions Include your entity model (or a model that sufficiently recreates your issue) to help troubleshoot.
const tasks = new Entity(
{
model: {
entity: "tasks",
version: "1",
service: "taskapp"
},
attributes: {
id: {
type: "string",
},
replayedAt: {
type: 'list',
items: {
type: 'number',
required: true,
},
required: true,
default: [],
// Add a getter for accessing existing items that may not have a `replayedAt` attribute
get: (value: unknown) => {
if (Array.isArray(value)) {
return value
}
return []
},
},
isReplay: {
type: 'boolean',
required: true,
watch: ['replayedAt'],
get: (_, { replayedAt }) => {
// Note: the sibling attribute values passed into the `get` method
// do not invoke their own get methods, so we must check
// if the attribute is an array before we can check it has a length > 0.
// The below will throw an error of "Cannot read properties of undefined (reading 'length')"
return replayedAt.length > 0
},
set: () => undefined,
},
},
indexes: {
id: {
pk: {
field: "pk",
composite: ["id"]
},
},
}
},
{ table }
);
Expected behavior
In the example above, I'd expect replayedAt to be an array, even if the attribute does not exist on the Item. In the current behaviour, I need to essentially re-implement the replayedAt.get method inside isReplay.
Errors
Cannot read properties of undefined (reading 'length')" - For more detail on this error reference: https://electrodb.dev/en/reference/errors/#aws-error
at Entity4.formatResponse (/node_modules/electrodb/src/entity.js:955:20)
at Entity4.executeQuery (/node_modules/electrodb/src/entity.js:746:23)
at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
at Entity4.go (/node_modules/electrodb/src/entity.js:508:18)
Additional context I fully accept that I might be using ElectroDB incorrectly!
I just tried this locally, with the latest version (2.15.0) like this, and everything worked as expected: https://gist.github.com/tywalch/fa43890fa0c7beb84804811b7f95bf46
You have the concept of "virtual columns" correct, and your implementation looks excellent; can you try the gist above? If you are still experience the error, can you upgrade to see if it might be a bug in a previous version?
I am going to close this ticket, but feel free to reopen it if you need to pick this question back up 👍
I'm running into a similar issue, where I want to add an attribute to an existing ElectroDB entity where there are already items in DynamoDB without that new attribute. I want to query for an existing item, and since the new attribute isn't in the existing item, I want ElectroDB to call the get method of that attribute (defaulting that attribute to some value if it doesn't exist in the item)
In @JoshBarr 's case, I think he may be adding the replayedAt attribute to an existing ElectroDB entity (see "even if the attribute does not exist on the Item" in his original question), and expecting to query for existing items and have replayedAt return an empty array (and isReplay return false), since that attribute doesn't exist on the existing items
For example:
- Create items with this entity
- Add new attributes onto the entity and query for existing items (like this)