-
-
Notifications
You must be signed in to change notification settings - Fork 6.2k
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
Fix bug with new nested where filters #8005
Conversation
Query builder would generate an incorrect query If you mixed primary and non-primary keys
@imnotjames how do I check if an entity is a transformed class vs a plain filter object? struggling with the differences between: const foundChild = await manager.findOne(Child, { parent: loadedParent });
// Expected: where parent.id = ? and .where({
post: {
category: {
id: In([1, 2, 3]),
name: In(["Foo", "Bar", "Baz"]),
}
}
})
// Expected: where category.id in (?, ?, ?) and name in (?, ?, ?)
// Use case: permissions filter on ids, but want to search on name Edit: const isEntityTarget = relation.inverseEntityMetadata.target === entity[key].constructor; Running tests again now. |
@imnotjames The node 14 test failed due to a circleci issue - could you please re-run? |
Doesn't the test pass if you write the code as
|
I tried that after I added the isEntityTarget check, but a couple tests failed. Realizing now that those tests might be invalid now and it now generates the correct sql. Let me know if I'm wrong: 6de9327#diff-fc53d8864169b2e2d0b175dc446c164524f59407c457d6b81a0fa1c004d8ca5cR206 Updated with the simple solution and changed those tests. |
Nvm the simple solution doesn't play nice with updates: 1) embedded > embedded-many-to-one-case1
owner side
should insert, load, update and remove entities with embeddeds when embedded entity having ManyToOne relation2:
EntityColumnNotFound: No entity column "counters.likedUser.id" was found. |
Fix bug with yaml mkdir test
@imnotjames Ready for another look. Would love your feedback on a better check for hasOnlyPrimaryKeys. Also please note that a couple tests that used to fail now pass - I'm not 100% if they are correct. |
@@ -11,7 +11,7 @@ async function createDotenvFiles() { | |||
} | |||
|
|||
async function createYamlFiles() { | |||
await fs.mkdir(path.join(__dirname, "configs/yaml")); | |||
await fs.mkdir(path.join(__dirname, "configs/yaml"), { recursive: true }); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this change needed?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wasn't able to run test-fast multiple times in a row. It would complain that the directory already existed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a bigger problem that our tests have than just this. Can you omit this change for now just to simplify this PR & what it touches? If we have to revert or find the cause of something it makes it a whole lot easier if it's one PR for one change.
if (relation.relationType === "one-to-many" || relation.relationType === "many-to-many") { | ||
throw new Error(`Cannot query across ${relation.relationType} for property ${path}`); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Pretty sure this needs to remain outside the if (isEntityTarget || hasOnlyPrimaryKeys) { ... }
block as it's unrelated to the shortcuts taken. We just do not currently support this kind of query at all.
// In the case of a *-to-one, there's only ever one possible entity on the other side | ||
// so if the join columns are all defined we can return just the relation itself | ||
// because it will fetch only the join columns and do the lookup. | ||
if (relation.relationType === "one-to-one" || relation.relationType === "many-to-one") { | ||
const joinColumns = relation.joinColumns | ||
.map(j => j.referencedColumn) | ||
.filter((j): j is ColumnMetadata => !!j); | ||
|
||
const hasAllJoinColumns = joinColumns.length > 0 && joinColumns.every( | ||
column => column.getEntityValue(entity[key], false) | ||
); | ||
|
||
if (hasAllJoinColumns) { | ||
paths.push(path); | ||
continue; | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Does the hasOnlyPrimaryKeys
actually apply here? Isn't it just in the case of an isEntityTarget
?
Can this ever apply with hasOnlyPrimaryKeys
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe you're right and I can have this block only apply to isEntityTarget.
if (value) { | ||
seenPrimaryColumns.add(columnName); | ||
} else { | ||
break; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we know this inner one is invalid, we know they all are. Do we want to break
further up?
const seenPrimaryColumns = new Set<string>(); | ||
for (const primaryColumn of primaryColumns) { | ||
const entityValueMap = primaryColumn.getEntityValueMap(entity[key]); | ||
for (const [columnName, value] of Object.entries(entityValueMap || {})) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What about embedded entity primary keys? Is that a concern here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm not sure - happy to hand off this pr to you if its easier.
Otherwise please point me to docs and I can try to add a test case.
for (const primaryColumn of primaryColumns) { | ||
const entityValueMap = primaryColumn.getEntityValueMap(entity[key]); | ||
for (const [columnName, value] of Object.entries(entityValueMap || {})) { | ||
if (value) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is null
a valid PK value? I know 0
is. So is an empty string. Is that something we should be handling here? Only checking for undefined
instead?
} | ||
} | ||
} | ||
const hasOnlyPrimaryKeys = primaryColumns.length > 0 && seenPrimaryColumns.size === Object.keys(entity[key]).length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If it's only primary columns do we actually care if it hits the optimizations?
In theory, it would be the same either way, wouldn't it?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The update flow uses this method and fails if you pass contact.id
vs contactId
.
ex. #8005 (comment)
That's the reason I added the hasOnlyPrimaryKeys check back.
Closing in favour of #8082 |
Description of change
Found a bug in the most recent nested where filter feature:
#7805
When combining nested primary keys and non-primary keys the query builder would build an incorrect query.
Included a test case that illustrates the issue.
fixes #8082
Pull-Request Checklist
master
branchnpm run lint
passes with this changenpm run test
passes with this change