-
Notifications
You must be signed in to change notification settings - Fork 196
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: CPK uni-directional has-many lazy list load #2730
fix: CPK uni-directional has-many lazy list load #2730
Conversation
assertList(comments, state: .isNotLoaded(associatedIdentifiers: [post.postId, post.title], | ||
associatedField: "postId")) | ||
associatedFields: ["postId", "postTitle"])) |
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.
Updated codegenerated schema files specify the targetNames, which are passed to the lazy list when instantiating it, thus a not loaded list will have the necessary field names of the post, that exist on the Comment, to query for the Comments by post identifiers.
} else { | ||
// If `targetNames` is > 1, then this is a uni-directional has-many, thus no reference field on the child | ||
// Construct the lazy list based on the primary key values and the corresponding target names | ||
let primaryKeyNames = schema.primaryKey.fields.map { $0.name } | ||
let primaryKeyValues = primaryKeyNames | ||
.map { getValue(from: element, by: path + [$0]) } | ||
.compactMap { $0 } | ||
.map { String(describing: $0) } | ||
return DataStoreListDecoder.lazyInit(associatedIds: primaryKeyValues, | ||
associatedWith: targetNames) |
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.
this is the functional change to pass down the targetNames
for lazy loading the uni-directional has-many use case. We have to extract out the values of the primary key values from the parent model, instead of just using the @@primaryKey
field 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.
doesn't the targetNames
only indicate the foreignKey composite key?
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.
Yes I believe so, when the targetNames is on the hasMany
modelField, they are the foreign keys fields of the parent model on the child model, which is an array due the parent model containing a composite key. For example, on the Post schema, the Post has a has-many reference to the Comments
.hasMany(post4.comments, is: .optional, ofType: Comment4.self, associatedWith: Comment4.keys.post4CommentsPostId, targetNames: ["post4CommentsPostId", "post4CommentsTitle"]),
The targetNames
represent the "associated with fields". On the Comment model,
struct Comment {
var post4CommentsPostId: String
var post4CommentsTitle: String
}
Since the comment does not have a reference object to Post, there is no "post: Post" field on Comment, and the foreign key composite keys are just strings. The proposed change is to add "targetNames" to the parent model's hasMany fields to point to "post4CommentsPostId" and "post4CommentsTitle"
// This is a bit off, the post.identifier is the CPK while the associated field is just "postId", | ||
// Loading the comments by the post identifier should be | ||
// "query all comments where the predicate is field("@@postForeignKey") == "[postId]#[title]" | ||
// List fetching is broken for this use case "uni directional has-many" | ||
assertList(comments, state: .isNotLoaded(associatedId: post.identifier, | ||
associatedField: "postId")) | ||
assertList(comments, state: .isNotLoaded(associatedIds: [post.postId, post.title], | ||
associatedFields: ["postId", "postTitle"])) |
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.
Since the Post8+schema.swift codegenerated file has been updated to include targetNames, they are used and passed as "postId" and "postTitle", which are the fields on the Comment8 to build the filter on, when lazy loading
appSyncAssociatedField: associatedField.name, | ||
appSyncAssociatedFields: modelField.associatedFieldNames, |
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.
This is the API functional change to pass the "targetNames" array or the "associatedKey", see associatedFieldNames
64f76ce
to
55b43ab
Compare
@@ -28,7 +28,7 @@ extension Post4 { | |||
model.fields( | |||
.field(post4.postId, is: .required, ofType: .string), | |||
.field(post4.title, is: .required, ofType: .string), | |||
.hasMany(post4.comments, is: .optional, ofType: Comment4.self, associatedWith: Comment4.keys.post4CommentsPostId), | |||
.hasMany(post4.comments, is: .optional, ofType: Comment4.self, associatedWith: Comment4.keys.post4CommentsPostId, targetNames: ["post4CommentsPostId", "post4CommentsTitle"]), |
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.
this is the proposed codegen changes
55b43ab
to
e58490d
Compare
AmplifyPlugins/API/Sources/AWSAPIPlugin/Core/AppSyncListProvider.swift
Outdated
Show resolved
Hide resolved
306b207
to
db534d5
Compare
7a30859
to
2e38706
Compare
2e38706
to
56fc25b
Compare
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.
LGTM!
* fix: uni-directional has-many lazy list load * remove redundant log * rename targetNames to use associatedWithFields * rename associatedWithFields to associatedFields
* fix: uni-directional has-many lazy list load * remove redundant log * rename targetNames to use associatedWithFields * rename associatedWithFields to associatedFields
* fix: uni-directional has-many lazy list load * remove redundant log * rename targetNames to use associatedWithFields * rename associatedWithFields to associatedFields
Issue #
Table of contents
data-dev-preview
fix: CPK uni-directional has-many lazy list load #2730 (You are here)v1
- fix(v1): CPK has-many has-one associatedFields #2734main
- fix: CPK has-many has-one associatedFields #2735Description
This PR fixes the use case for lazy loading a list for uni-directional has-many use cases where the parent model contains a custom primary key. A uni-directional has-many use case is the following:
The Post has a custom primary key (postId and title), and the child does not have a model reference to the post.
Querying for a post, and then traversing to the comment, and lazy loading the comment previously was incorrectly constructing the filter (Query for Comments by filter on post identifiers). It was missing the post's second identifier of the composite key.
The correct logic is to do
This is working for bi-directional since the post reference on the comment can resolve the targetNames (fields on the Comment, which reference the post). It is not working on for uni-directional because the Comment does not have a reference to the post, only the fields of the post's composite key on the comment.
This PR manually modifies the codegenerated types to include the associatedFieldNames on the comments field on the Post. When querying for a post, the associatedFieldNames will be used if available to pass which fields to construct the filter on. This is only necessary when the parent model has a composite primary key. The changes in this PR is backwards compatible with previous codegenerated hasMany relationships which do not contain associatedFieldNames, however the use case can only be solved for the customer when they upgrade to the latest codegen and regenerate the models with the associatedFieldNames.
Examples use cases
Use case: bi-directional has-many belongs-to
Comment belongs-to Post will have a reference like "post"
associatedFields
will be ["post"].associatedIdentifiers
will be ["postId123"]Use case: bi-directional has-many belongs-to with CPK on parent
associatedFields
will be ["post"].associatedIdentifiers
will be ["postId123", "title123"]Use case: uni-directional has-many
associatedFields
will be ["postId"].associatedIdentifiers
will be ["postId123"]Use case: uni-directional has-many with CPK on parent
This is the use case that has a problem being fixed by this PR (and codegen changes to add associatedFields to the hasMany modelField)
associatedFields
will be ["postId", "postTitle"].associatedIdentifiers
will be ["postId123", "title123"]General Checklist
Given When Then
inline code documentation and are named accordinglytestThing_condition_expectation()
By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.