Open srimalaya opened 2 years ago
Thank you for reporting this issue, we are looking into a fix for the issue.
I have noticed this for any connection, not just hasMany.
In the above example Post.blog will return null as well, I assumed this was a limitation as the datastore sqlite tables only hold an ID.
@PS-MS I can confirm that Post.blog works on my end with the amplifyframework version I have mentioned above. I am able to get values inside a blog using "it.blog.id" or similar commands.
Only the @hasMany relationship does not work, specifically for [models].
One solution can be to increase the depth of codegen using "amplify configure codegen". However, the default depth is 2 which should be fine in this case.
Or, maybe try updating your CLI and Datastore to latest versions.
I've tried updating to the latest amplify releases and it.blog.id works because the id is stored in the Post table however it.blog.name will return null for me
Hi @shri-onecup, in the generated models, is that field post
marked as a non-null type? Referring to the docs for relational models https://docs.amplify.aws/lib/datastore/relational/q/platform/android/ there should be no expectation today of models being linked internally. If the post
field is marked as optional in Kotlin, i.e. List<Post>
, then you may need to null-check in your code before accessing. If not then we should fix the types being emitted here.
@alharris-at This is the only line that I could find related to post
in the model for blog
.
private final @ModelField(targetType="Post") @HasMany(associatedWith = "blog", type = Post.class) List<Post> post = null;
As you can see, it is being set to null by default which seems to be the issue here.
I am also attaching the entire generated models folder for your reference. model.zip
I am on the following versions of Amplify: CLI: 8.0.2 AmplifyFramework: 1.35.0 Kotlin Facade: 0.19.0
Hi @shri-onecup, taking a look at that code, I don't think the issue is necessarily that it's being set to null, since that seems correct given the statement from our docs that The @hasOne and @hasMany directives do not support referencing a model which then references the initial model via @hasOne or @hasMany if DataStore is enabled.
I believe you should be able to achieve the same goal by executing the following code.
Amplify.DataStore.query(Post::class)
.catch { Log.e("MyAmplifyApp", "Error", it) }
.collect { post ->
val comments = Amplify.DataStore
.query(Comment::class, Where.matches(Comment.POST_ID.eq(post.id)))
.toList()
Log.d("MyAmplifyApp", "Post: $post, Comments: $comments")
Log.d("MyAmplifyApp", "posts test id: ${comments[0].id}")
}
And here's the full MainActivity.kt
file I used to test this
```kotlin package com.example.bugrepro1676 import androidx.appcompat.app.AppCompatActivity import android.os.Bundle import android.util.Log import androidx.lifecycle.lifecycleScope import com.amplifyframework.AmplifyException import com.amplifyframework.core.model.query.Where import com.amplifyframework.datastore.AWSDataStorePlugin import com.amplifyframework.datastore.generated.model.Comment import com.amplifyframework.datastore.generated.model.Post import com.amplifyframework.kotlin.core.Amplify import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.InternalCoroutinesApi import kotlinx.coroutines.flow.catch import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.toList import kotlinx.coroutines.launch class MainActivity : AppCompatActivity() { @ExperimentalCoroutinesApi @InternalCoroutinesApi override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) try { Amplify.addPlugin(AWSDataStorePlugin()) Amplify.configure(applicationContext) Log.i("Amplify", "Initialized Amplify") } catch (e: AmplifyException) { Log.e("Amplify", "Could not initialize Amplify", e) } val post = Post.builder() .title("My First Post") .content("Post Content") .build() val comment1 = Comment.builder() .postId(post.id) .content("My First Comment") .build() val comment2 = Comment.builder() .postId(post.id) .content("My Second Comment") .build() lifecycleScope.launch { // Setup Test Data Amplify.DataStore.clear() Amplify.DataStore.save(post) Amplify.DataStore.save(comment1) Amplify.DataStore.save(comment2) // Verify Test Data Amplify.DataStore .query(Post::class) .catch { Log.e("MyAmplifyApp", "Query failed", it) } .collect { Log.i("MyAmplifyApp", "Post: $it") } Amplify.DataStore .query(Comment::class) .catch { Log.e("MyAmplifyApp", "Query failed", it) } .collect { Log.i("MyAmplifyApp", "Comment: $it") } // Repro NPE // Amplify.DataStore.query(Post::class, Where.id(post.id)) // .catch { Log.e("MyAmplifyApp", "Error", it) } // .collect { Log.d("MyAmplifyApp", "posts test id: ${it.comments[0].id}") } // Updated Customer Code Amplify.DataStore.query(Post::class) .catch { Log.e("MyAmplifyApp", "Error", it) } .collect { post -> val comments = Amplify.DataStore .query(Comment::class, Where.matches(Comment.POST_ID.eq(post.id))) .toList() Log.d("MyAmplifyApp", "Post: $post, Comments: $comments") Log.d("MyAmplifyApp", "posts test id: ${comments[0].id}") } } } } ```
Agreed that this behavior is inconsistent across platforms, however we consider that it is currently working as intended on Android so I have changed this from a bug to a feature request. It's possible to work around this issue by querying the child objects as Al showed above. We are keeping this on the roadmap for a future release.
Any news on this? Actually, this behaviour is not only inconsistent across platforms, it is also inconsistent with the documentation. To make it worse, it means that if I ask the parent whether it has children, I get 'null', independent from whether it actually has children or not. I.e. inconsistent data. I can only get this information by asking the children about their parents.
The posted workaround might be a reasonable solution when printing out data. In a real case scenario it adds another level of flows and observes which unnecessarily complicates the code.
I faced the same problem in Android. I had to create a copy of the generated model class. Added the comments list field to the buildsteps. Created two extension functions in kotlin, one to convert the generated Post class to my modified PostCopy class and takes a list of comments as a parameter and passes it to the comments buildstep. The other extension function simply converts the PostCopy instance back to the original Post.
With that in place. I first query the comments, then in the on success I query the posts and convert the retrieved posts using my extension function and pass the list of comments. I now can use my PostCopy class to get the expected functionality for bidirectional relationship.
Typed this from my phone, would willing write a step by step if anyone is interested in the workaround once I get to my workstation.
@osamwelian3 Have you managed to resolve this issue? We will attempt to address it as well, but it is not functioning correctly. Could you please provide a more detailed explanation, possibly with examples or screenshots, to aid our understanding?
Before opening, please confirm:
Language and Async Model
Kotlin - Coroutines
Amplify Categories
DataStore
Gradle script dependencies
Environment information
Please include any relevant guides or documentation you're referencing
https://docs.amplify.aws/lib/datastore/relational/q/platform/android/
Describe the bug
I am trying to access nested model (Blog & Post in schema) inside DataStore queries similar to how it is possible to access Nested CustomType (which works). In the code, when trying to access the (Mutable)List, I get a null pointer error and my app crashes. I have been facing this issue since amplifyframework version 1.31.3 and now I am on 1.32.x and still facing it.
Any ideas or suggestions?
This seems to be working on other platforms. On Flutter, https://github.com/aws-amplify/amplify-flutter/issues/260#issuecomment-909679333 mentions that [CustomType] is not supported but that works on my end. Only [AnotherModel] i.e. a @hasMany relationship between tables is not working.
Reproduction steps (if applicable)
Code Snippet
Log output
amplifyconfiguration.json
No response
GraphQL Schema
Additional information and screenshots
No response