-
Notifications
You must be signed in to change notification settings - Fork 134
Folders Implementation
Since I wrote the tree views in v1.2.4, the most common feature request has been to allow users to create folders and subfolders to help them organize their recipes and ingredients. This had been my intent since I created the trees. This feature should finally see the light of day in v2.1.
This document is intended to provide an overview to the design choices made and some of the implementation details. I've tried to comment the code very well. If you want a low-level implementation description, please read the code.
The biggest decision to make was how to store the folder information. As a rule, brewtarget has tried very hard to implement the BeerXML specification, but that specification provides no guidence on this topic.
One possible choice prior to v2.0 was to store each recipe in its own file, and use the file system to "store" the folders. As complex as this sounds, it would have actually been an easy and natural approach.
With v2.0, we moved to using an RDBMS to store the data. Overall, this move was a good one and one I pushed. Load times dropped from 10 seconds to 2. Unfortunately, it made implementing folders much harder.
In essence, the difficulty comes from the simple idea that a directory structure is hierarchical -- the relationship between items is expressed by where they are in the hierarchy. The "R" in "RDBMS" stands for "Relational". The relationship between items is expressed using keys and queries to link related items. Yes, that's a gross simplification and if you want it better, go read wikipedia on the topic.
The important point is that the two don't mix very well. Hierarchies suck at expressing relational data and relational databases suck at expressing hierarchies. Admittedly, the second one is easier, but it still sucks.
I could have built a few new tables, defined some complex queries for finding children and then the elements (e.g., recipes or hops) at the leaf nodes. This approach would have been very .. hard, actually. I am aware of at least one if not more patents covering exactly how to do this. I know the guy who held those patents. I'm not that smart.
Instead of having a tree that knows what data it stores, I turned it around and had the data know which folder it was in. Since I only need the folders and hierarchy for displaying information, this actually presented a workable idea.
The elements in brewtarget are those items descended from the BeerXMLElement class. The comprise: recipes, brewnotes, equipment, fermentables, hops, miscellaneous, water (not quite working yet) and yeast. In other words, they are exactly the things that I wanted to display in folders.
They also all have their own tables. A simple extension to the schema definition for each table and suddenly each element knows which folder it is in.
Every time you simplify a model, there are consequences. In this case, I think they are fairly minor.
The biggest consequence is that a folder doesn't exist unless an element is in it. If you remove all of the elements from a folder and restart brewtarget, the empty folder will not be there. And not just the leaf folder, but every folder up the hierarchy until something other than a folder is in there.
Accepting this one side effect really simplified the implementation. Everything simply became manipulating the model, which is much easier to do than manipulating the data.
Oh. And my apologies to Dali.