-
Notifications
You must be signed in to change notification settings - Fork 64
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Query validation error - gt and lte create erroneous filter expression #228
Comments
Note - the |
Hey Ross! I was able to recreate this issue when I mapped by index fields to attributes (like in this playground here). In the instance of your error, is your model doing similar mappings -- the index name is also the attribute name? If so, could you share your model and/or one that maps attributes and keys in a similar manor?
Assuming this is because of the attribute names mirroring the index field names, I definitely need to figure out a good plan for this case. Bottom line is that Electro shouldn't make invalid parameters like this 👍 |
Hi Ty This is what the Entity looks like: export const NOTIFICATION_SCHEMA = new Entity(
{
model: {
entity: 'Notification',
version: '2.0.0',
service: 'aa',
},
attributes: {
datasetId: { type: 'string', required: true },
activityId: { type: 'string', required: true },
leadentId: { type: 'string', required: true },
apptDate: { type: CustomAttributeType<DtgIsoDate>('string'), required: true },
createTime: { type: CustomAttributeType<DtgIsoFull>('string'), required: true },
ruleName: { type: 'string', required: true },
ruleType: { type: CustomAttributeType<RuleType>('string'), required: true },
targetAddress: { type: 'string' },
targetTime: { type: CustomAttributeType<DtgIsoFull>('string'), required: true },
requestTime: { type: CustomAttributeType<DtgIsoFull>('string') },
requestId: { type: 'string' },
requestAttempts: { type: 'number', default: 0, required: true },
deliveryStatus: { type: CustomAttributeType<DeliveryStatus>('string'), required: true },
deliveryMethod: { type: CustomAttributeType<DeliveryMethod>('string'), required: true },
deliveryTime: { type: CustomAttributeType<DtgIsoFull>('string') },
failureReason: { type: 'string' },
messageText: { type: 'string', required: true },
messageParts: { type: 'number' },
siblingKey: {
type: 'map',
properties: {
ruleActivityReference: { type: 'string', required: true }, // HASH key
ruleInstance: { type: 'string', required: true }, // RANGE key
},
},
pendingActivityReference: { type: 'string' },
pendingMethod: { type: 'string' },
ttl: { type: 'number', required: true },
},
indexes: {
primary: {
pk: {
field: 'ruleActivityReference',
composite: ['datasetId', 'activityId'],
template: ACTIVITY_REFERENCE_TEMPLATE,
casing: 'none',
},
sk: {
field: 'ruleInstance',
composite: ['ruleName', 'createTime', 'deliveryMethod'],
template: '${ruleName}#${createTime}#${deliveryMethod}',
casing: 'none',
},
},
byPendingMethod: {
index: 'GSI1_pending',
pk: {
field: 'pendingMethod',
composite: ['pendingMethod'],
casing: 'none',
},
sk: {
field: 'targetTime',
composite: ['targetTime'],
casing: 'none',
},
},
byPendingActivityReference: {
index: 'GSI2',
pk: {
field: 'pendingActivityReference',
composite: ['pendingActivityReference'],
casing: 'none',
},
sk: {
field: 'ruleName',
composite: ['ruleName'],
casing: 'none',
},
},
},
},
{
table: process.env.DYNAMODB_TABLE_NOTIFICATIONS,
},
); and we're calling model.query
.byPendingMethod({
pendingMethod: method,
})
.lte({
targetTime: now,
})
.go({ limit, ignoreOwnership: true }); So, yeah, if I've understood you correctly, the index field does match the attribute name. |
Damn, this makes it a bit more difficult because the current design leans into dynamo to filter to limit post processing in app code. I'll need to look at this a bit deeper on the backside to see what options make sense 👍 |
* Fixes issue #228 * Adds new dynamic table provisioning examples * Formatting * Formatting, warnings, packages * Fixes 308
Hi Ty, is this fix released and is it in the version that ElectroDB Playground uses? I notice that the Playground link still exhibits the behaviour that is described in this issue - unless I'm missing something? |
I will check this today, it is possible there was an issue and the playground is lagging behind latest 👍 |
@PaulJNewell77 I plugged in the entity and query @rcoundon shared into the playground (no changes) and it looks correct to me: here. The fix takes into account whether or not the sortkey composite attribute is also the field name of the index. In the first playground link shared in this issue, that wasn't the case, so I would expect the parameters generated for that example to remain the same. Let me know if you see something different or there's still more that needs to change for your use case 👍 |
I reopened this issue until things are confirmed to work for you 👍 |
Hi, the fix does work for us in our use case although I there may still be a related issue. Here is a Playground link which is a tweek of the one in the original description. There are four queries in there: |
Further to this, here is a Playground link with another slight tweak, adding 'team' to the sk of the index we are querying. Now the queries do need filter expressions, but the As always .... I could be missing something. |
Yes, this is expected and deliberate 👍 These filters help trim the edges of what comes back so that it doesn't have to be done in code. I could go into more detail, but I'll put more focus on your other comment.
This won't be satisfying because candidly I'm trying to remember the exactly why, but I long while ago I removed the constraint to throw when a composite was provided out of order. The filters added to Adding a filter may make sense, since the user's intent seems clear, but it could also result in unexpected behavior. Also it does add some weird cases around collections, but we can put those aside of the moment. I will say that Using this example, a user says tasks.query
.backlog({ project })
.gt({
team: "blue",
closed: "2021-01",
})
.go(); Which of the following is their intent?
When providing this: tasks.query
.backlog({ project })
.gt({
team: "data",
})
.go(); Which of the following is their intent?
What if we actually have four teams: tasks.query
.backlog({ project })
.gt({
team: "data",
})
.go(); Which of the following is their intent?
In the example where tasks.query
.backlog({ project })
.gt({
closed: "2021-01",
})
.go(); If a filter is added in the above case, something like Let me know your thoughts on the above scenarios. How folks use the sort key operators is a bit of mystery to me, so I'd love to get your take and initial intuitions. Ideally this thread will also serve as a way others can weigh in as well. EDIT: I've edited this comment a few times now for clarity, hopefully done now. |
Apologies for the delay. My mental note to return to this finally resurfaced and I'm glad it did as there's lots of important things to unpack here. I think I'll tackle the cases in the order of easiest to hardest (from my point of view). So firstly, I think most developers think about lexicographical sorting in the same way. And so, if they queried for strings greater-than Following on from that to talk about sorting of dates. I totally feel that pure lexicographical sorting is the most expected outcome. As such, greater-than Finally, coming to compound sort keys, I must admit I've never had cause to use one. I would say that if you've designed a GSI with a sort key of the form
Something like this might be more intuitive:
I would expect this to have the intent of your number 4 option:
I hope that makes some sense and is of some use. Be interested to hear your further thoughts. |
Further to this I've been playing a bit more and have to return to my last question, but with a different example. This query:
Results in this:
So, I've asked for |
Sorry to bump @tywalch - just in case you hadn't seen this - the behaviour of this might trip some people up |
Thanks @rcoundon, I have been mulling this over but I wanted to avoid making a rash change; the removal of this behavior outright is [obviously] a breaking so it's no small change. It has been a very busy time on my end, and it has been hard to give this the time needed to think about it thoroughly. Any detailed thoughts you have on the philosophies you'd expect would be a huge help. I will try to put aside some time soon to write on this topic in this thread. |
Thanks @tywalch. Totally understand, and I didn't mean to put pressure on you. I'll have a chat with Paul tomorrow and we'll gather some thoughts to help out |
No, no! I don't feel pressured, I just mention it because I want to be transparent. I really appreciate how much you both have helped push the library to where it is today. It is so awesome you guys take the time to communicate your use cases and actually want to see things improved! |
IMHO, FilterExpressions should be handled by the user whenever possible. I also think users should use the query APIs aware of the fact that the composite SK's need to be sequentially defined and follow lexicographical sorting. I believe any abstraction that deviates from this comes with a high cost. However, it really looks like this is as breaking as it gets. So I think these are some options: a) Release a major version with these changes. |
Howdy @rcoundon and @PaulJNewell77 👋 I just published 3.0.0, with the significant breaking change being that I removed the character shifting and filtering logic by default. |
Thanks for the heads up. I'd noticed the discussion. So filtering needs to be done in userland now? |
Describe the bug
When using
gt
orlte
ElectroDB adds an erroneous filter expression which can cause the DynamoDB client to throw a validation error because filter expressions cannot contain SK attributes.ElectroDB Version
2.5.1
ElectroDB Playground Link
ElectroDB Playground
Entity/Service Definitions
All in Playground
Expected behavior
No filter expression to be added
Errors
The text was updated successfully, but these errors were encountered: