Skip to content
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

Add index to local entities #6261

Merged
merged 3 commits into from
Jul 19, 2024
Merged

Conversation

seadowg
Copy link
Member

@seadowg seadowg commented Jul 10, 2024

Work towards #5623

This adds a new index property for local entities that can be used as the multiplicity (XPath "index") for each TreeElement when we generate a secondary instance. This isn't strictly required right now, but it will be needed later when we want to query EntitiesRepository in a FilterStrategy to allow for optimized entity filters (ultimately allowing larger entity lists to be used).

Why is this the best possible solution? Were any other approaches considered?

The biggest change here was actually switching to a New/Saved sealed class/ADT pattern for Entity to allow for properties that are only available after saving (with EntitiesRepository). This allows us to keep the logic of generating index entirely within the repository implementations. Any other quirks will be pointed out inline.

How does this change affect users? Describe intentional changes to behavior and behavior that could have accidentally been affected by code changes. In other words, what are the regression risks?

The biggest risk here is that the changes here could mess with forms that use entity lists in selects or calculations. It would be good to play around with those along with deleting (on the server) and adding entities (both on different devices/server and offline).

Before submitting this PR, please make sure you have:

  • added or modified tests for any new or changed behavior
  • run ./gradlew connectedAndroidTest (or ./gradlew testLab) and confirmed all checks still pass
  • added a comment above any new strings describing it for translators
  • added any new strings with date formatting to DateFormatsTest
  • verified that any code or assets from external sources are properly credited in comments and/or in the about file.
  • verified that any new UI elements use theme colors. UI Components Style guidelines

repository.save(wine, whisky)

assertThat(repository.getLists(), containsInAnyOrder("wines", "whiskys"))
}

@Test
fun `#save assigns an index to each entity in insert order when saving multiple entities`() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They don't strictly need to be in insert order most likely, but this behaviour is much easier to test, and will mean the resulting multiplicity within form entry will match the order in the CSVs.

}

@Test
fun `#delete does not change index values for remaining entities`() {
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This actually might not be what we want, but this drives out actually persisting index (as we don't have a query method to test yet). It also felt important to make the interaction between index values and delete explicit.

Would be good to get a second opinion here @lognaturel.

Copy link
Member Author

@seadowg seadowg Jul 10, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh actually this totally won't work: multiplicity must start at 0 and not contain any gaps (with the current version of JavaRosa). I'll mark this as a draft again and add that constraint.

@seadowg seadowg marked this pull request as ready for review July 10, 2024 14:58
@seadowg seadowg requested review from grzesiek2010 and lognaturel and removed request for lognaturel and grzesiek2010 July 10, 2024 14:58
@seadowg seadowg marked this pull request as draft July 10, 2024 16:36
override fun getEntities(list: String): List<Entity> {
return readEntities().filter { it.list == list }
override fun getEntities(list: String): List<Entity.Saved> {
return readEntities().filter { it.list == list }.mapIndexed { index, entity ->
Copy link
Member Author

@seadowg seadowg Jul 11, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our future "query" APIs would just filter the result of this.

This sort of thing is obviously going to be much more awkward to implement in SQL, but I think we have some options. Worst case, we'll need to do more work in form download (or in some new background job kicked off after form download) to give ourselves an "index".

@seadowg seadowg marked this pull request as ready for review July 11, 2024 07:52
@seadowg seadowg force-pushed the entity-multiplicity branch 3 times, most recently from d3ebf19 to fcd2db9 Compare July 15, 2024 19:08
Copy link
Member

@lognaturel lognaturel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks reasonable to me! Just one question/comment.

enum class State {
OFFLINE,
ONLINE
}

fun sameAs(entity: Entity): Boolean {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is currently just for testing it looks like. It feels like two identical items at different positions in document order should not be considered the same but maybe it doesn't matter for the intended usage.

Is == in Kotlin only going to compare the list references for properties? Maybe that makes my point above moot.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is currently just for testing it looks like. It feels like two identical items at different positions in document order should not be considered the same but maybe it doesn't matter for the intended usage.

The idea here was to be able to compare entities without the data that gets added by our repo (currently just index, but might also be things like a db ID down the line.). We might have actually end up using this in real code I could imagine.

Is == in Kotlin only going to compare the list references for properties?

Because Entity is a data class it'll have an intelligent equals implementation (and Kotlin calls equals rather than doing reference equality with ==).

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In the case of Entities, we're (essentially) guaranteed a unique ID so this is fine. In the case of arbitrary secondary instances, I think the index may be important to consider because duplicates are more likely. We can likely come back to it then.

Copy link
Member

@lognaturel lognaturel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've given enough of a look at this to feel comfortable proceeding. I know @grzesiek2010 is tied up with point release things and getting this in will unblock downstream PRs.

@grzesiek2010
Copy link
Member

I already had my first pass so if you would like to wait today with merging then I can finish.

@grzesiek2010 grzesiek2010 merged commit a9c57b9 into getodk:master Jul 19, 2024
6 checks passed
@seadowg seadowg deleted the entity-multiplicity branch July 19, 2024 11:37
@WKobus
Copy link

WKobus commented Jul 22, 2024

Tested with Success!

Verified on devices with Android 14, 10

Verified Cases:

  • Adding, updating and deleting entities on server
  • Adding and updating entities on different devices in Collect
  • Forms with entity list in select
  • Offline entities

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants