-
Notifications
You must be signed in to change notification settings - Fork 9
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
Merge predefined* and user* tables #239
Comments
@weberax was talking about a database refactoring. So maybe this is something we can discuss here? |
I like the idea! The original idea of having two different tables, was
But since we decided to have the ids in the google form fix, 1. should not be a problem anymore. So to summarize what needs to be done (analogous with the combos):
This might also fix #240 |
Co-Authored by @JulianDietzel The Document defines changes to the Database Structure that we deem useful. Outline
The following text will talk about Tricks, but note that all mentioned paradigms apply to Combos, too. The user will no longer be able to modify predefined Tricks. They can however use predefined Tricks as a template. The Primary Keys get turned into composite keys. Below you can see the new proposed Database Structure. Notes
Assumptions / RequirementsIn order to create a purposeful architecture we made some assumptions on what the application should / wants to be. These assumptions are at the core of how the app should function and how it should be designed. We made the assumption that the primary tasks of this app are to
If you feel like our assumptions are wrong or incomplete, please correct them. It is critical that we get them right. Updading Predefined DataCurrently, a version number is used for updating predefined data. If the version number provided by a JS Object is bigger than the version in the Table, an update takes place. We propose to store the Hash in a Dexie Table as a simple string. On startup, the predefined data gets run through the hash function. In case there is a difference between stored and computed hash all predefined rows are deleted from the Database and updated. The following figure shows the steps taken during startup to check if the predefined tricks need to be updated, as well as the general procedure taken if an update is needed. sequenceDiagram
participant A as Start
participant B as Version Table (Dexie)
participant C as Trick Table (Dexie)
A->>A: Load JS Object with <br>predefined tricks
A->>A: Hash the object <br>(computed hash)
A->>+B: Check if a hash is <br>already stored (stored hash)
B-->>-A:
alt stored hash == computed hash
note over A,C: Stop. Already synced
else else
A->>+C: Update predefined Trick Entries in DB<br>(see other Diagram for further info.)
C-->>-A:
A->>B: Write Hash
end
Handling Deleted Predefined DataAs mentioned above, a new Type of Combo and Trick, the "Orphan" gets added. Here are the strategies we identified for dealing with deleted predefined tricks: 1. Orphan Tricks as 3rd Trick typePredefined tricks that get deleted are turned into orphans. They then function the same way as user tricks, but are marked as Orphans in the UI. 2. Orphans as intermediary (in the limbo :D)Predefined tricks that get deleted are turned into orphans. While in that state, they are excluded from any lists and searches. The user is prompted to either convert the tricks into User Tricks or to outright delete them. 3. Simply Delete Trick like it never existed (without Orphan Type)This is probably undesirable because users could lose their progress or be confused as to why some tricks suddenly disappeared. 4. Autoconvert to User Trick (without Orphan Type)This would skip the orphan stage and simply convert any to-be-deleted predefined trick into a user trick. Waldemar prefers Option 2. Julian prefers Option 1 and 4. All of them would work. We would love your input on what you deem best. Here you can see an overview of the deletion process for all 4 options. How we came to this designThere were some other approaches considered, with the most promising being the use of JS-Objects that contain the predefined tricks. RationaleThis design allows us to keep one "source of truth" — that's the JS Objects defining predefined tricks created based on the Google Sheets tables. By having everything in the Dexie DB we can make use of Dexie's querying functionality without needing to join data from different sources (e.g. separate userTrick and predefinedTrick tables). In order to satisfy the requirement of serving as an "official" database of tricks, removing the functionality for users to edit predefined tricks allows for a consistent global state of predefined tricks. This way all users will always have the same "official" trick information which makes it easier to use for competitions. In order to still allows users to track their progression and make custom notes on official and user-defined tricks we opted to create a new table, metadata. This table is decoupled from the main trick table to allow for easier updates. It also makes it more clear what is "predefined" and what is "custom" data for predefined tricks from a developer's perspective. Related issues and open questionsIf there are any plans to add any new features which would affect the database structure, now would be a good time address them. Here are some open issues that come to mind:
Another question in case you like the proposed concept of user notes: |
Assumptions / RequirementsYou pretty much nailed it! Updating Predefined DataThe process you described for updating tricks sounds good to me. Handling Deleted Predefined DataI sat down with @Felix-Hmpl and discussed about this. What do you think about the approach? Some RemarksA minor comment to the DB design: The optional keyword for technicalName and alias is the other way round (every trick has a technical name and some tricks have aliases). For now it seems fine to me that the metadata is the same for tricks and combos. If this changes it still shouldn't pose a problem as we could just decide to not show certain fields in the UI for combos or tricks. Since we are saying that users can't modify predefined tricks (only by using them as a template for a user trick) I don't see the necessity of having a composite key for the tricks and combos. The predefined trick ids start with 10000 and I don't see any user creating 10000 tricks for now (not even Ian :D ). So we could just keep it as it is for now without problems, I guess. I have created some additional columns in the trick list. One will be used for #197, 'dateAdded'. Two other columns are one way to deal with #171. We have too many tricks now to show all of them on the main screen, especially tricks that pretty much noone cares about. So I added a bool column 'isImportant' and a column 'isVariationOf'. If a trick is not important at least one trick id has to be filled in the corresponding 'isVariationOf' field, this could be used to create little drop downs on tricks that have unimportant variations and show them if the user clicks on the drop down button. I have not thought too much about #170 yet but one possibility would be (kind of related to #171) to allow an array of start and end positions to be used, where the first entry in the array is used as the main combination of positions and the other entries show other possible variations. But I think this can be handled in a later release, for now we have more important things to work on. Let me know what you think and feel free to correct me with things that I got wrong. |
Haven't talked to Julian about this yet, so here are just my 2ct: Updating Predefined DataI see some mentions of inheritance. I don't see how that is relevant. Any logic should be pretty much Trick/Combo-Type agnostic. No inheritance takes place. DRY is not violated. Your mentioned approach works for me. I would still argue that the orphan Type is helpful here. How would you track of a Trick has been "orphaned"? Some Remarks
The Issue here is abandoning a clean data structure. A good database should not rely on "magic numbers" or have some hidden meaning behind values. But maybe I am overdoing the entire engineering part :D I would like you to take a look at this from the point of view of a new Dev. I would argue it is easier to infer if a DB entry is a User-Entry of a Predefined Entry with the composite key, instead of having a magic number (in this case, 10_000).
I don't like this because of the same issues mentioned above (e.g. first entry is implied to be primary) I guess it boils down to: Does the start Position need to be queried? interface Table {
id: number // PK, autoincrement
trickId: number // Part of Composite FK;
trickType: TrickType // // Part of Composite FK;
startPosition: Position
endPosition: Position
difficultyOverride?: Difficulty // Just a thought :P
} so something like
would define 4 alternative Start / Stop Positions Sorry if the thing is a bit incoherent. :P |
Handling Deleted TricksI like your proposal of showing the tricks like they are user tricks in the trick list but then prompting the user about how to handle them when the open it. On the question of whether the Orphan-Type is needed: Firstly I think there is a small bit of confusion around what the Orphan-Type actually is: This was the best way to store the information that a trick is in this kind of "limbo", that we could come up with. Repeating Waldemars question:
Some Remarks
|
Handling Deleted TricksI like the idea of combining option 1 and 2 but would insist on the extra orphan type and would suggest to rename it to something like "archived Trick". Composite Key / Compound IndexNice, I like it. The Doubles, Variations, Different starting/ending PossitionChanging the Endpossiton of a trick makes it to a variation of that trick in my eyes, same as doubles... To handle that nicely in the google sheet, I would suggest to:
This is just out of the top of my head, feel free to think trough all that again ;) Dexie?Now when we do this whole refactoring we could also consider changing to different library then Dexie... Or should we also consider a different storage option? |
Ok, thanks for the clarification :)
I think this is basically answered in your first very detailed post in the image describing the process of deleting tricks for the different options. First we store the ids of predefined tricks in the current version, then we check the ids of the updated version and whichever tricks are missing in the updated version will be turned into 'Orphan Tricks'. Whenever the user first visits this trick a pop-up will show up asking the user whether to delete or keep the trick. It will then either be deleted or turned into a user trick. You also convinced me that it is not the best idea to go with the magic number 10.000 :D So let's use the composite key variant.
This seems like a good option to me. I also like @weberax's approach for that. However, I am still not sure how to handle variations (showing them on the main trick list or not; creating separate tricks for them or just list them in the original trick if they are not important; which ones should exist in the app anyway?). Do you think this is a decision we can delay a bit or should we come up with a way to do it now?
While it's true for now that I only put entries in rows where
That seems like an easy fix for the problem. Maybe I wouldn't say that a combo is made out of tuples but rather that we can use 'normal' tricks and something like temporary/quick/fill trick (don't know how to best name it). Not really different from a note but kind of handled like a trick (just without the link to a trick page and so on).
After a quick search it looks to me like indexedDB is the best option for offline storage in PWAs and Dexie is the most well-maintained indexedDB wrapper as far as I know. So maybe we should just stay with Dexie. But if you find any better alternatives we can also change this. |
@WaldemarLehner and I looked at the recent suggestions and updated our old graphics accordingly: Changes
Open Questions
Others
This is what we have for now. Let us know what you think or if we missed anything (for which there is a decent chance). |
Nice, it looks really good!
Open Questions:
|
The database of the rewrite will be designed in accordance to this discussion. There will therefore be no change to the database of the current version of the app. It will be rewritten entirely as part of the complete rewrite of the app. |
The user tricks and the predefined tricks, just as the user combos and the predefined combos are stored in separate tables in the database. Yet, it is made sure in almost every function that adds to those tables, that there are no duplicate ids between the tables. Also, when tricks or combos are fetched from the database, the predefined and the user tables are almost always if not always merged. This makes the code harder to read, makes functions longer (as always both tables need to be queried and the results need to be merged) and creates the need for functions like the mergeLists one.
Couldn't the tables just be merged into one, with an additional column
isPredefined
, which just stores a boolean value and specifies whether the trick is a predefined one or not. I'd estimate that this would shorten the db.js file by about 20% and increase readability a lot. Also it would prevent the tables from getting out of sync, if someone were to accidentally only change / update one of the tables but not the other.I'd be interested if there is perhaps a good reason for the current architecture that I am missing. If not, I'd suggest merging both the
predefinedTricks
anduserTricks
tables and thepredefinedCombos
anduserCombos
tables.The text was updated successfully, but these errors were encountered: