Ecco Model
This has been swirling around in my head since I started using Ecco. I've been trying to figure out an easy way of keeping ecco data in chandler. The "hard part" with Ecco (or other Agenda-like models) is that all items have meta attributes associated with them, and that metadata changes day-to-day depending how the user is setting up his/her data. For instance, if I have a "daily journal" view, I can arbitrarily assign a date to the "ToDo" attribute of the item, and that automatically adds it to the list of To Do items. All the data in Ecco is stored in a kind of folder/soup model where each item has a "home folder" of sorts, but really just exists in the soup of items. I believe Ecco even allows for items that get orphaned from any kind of home folder.
There are basically two approaches I can think of to model this dynamic data:
- Dynamically create an "Ecco Item" Kind that grows and changes dynamically to accomodate the various columns/folders that the user defines.
- Advantages: probably very little overhead in the database, since it will be keeping track of metaattributes on the items. Queries/folders would probably be fairly easy.
- Disadvantages: all items would "get" an attribute that got assigned to any one item - hopefully the database would accomodate this with minimal overhead. When attributes are removed, this might invalidate queries, or cause other mismatches within the UI, etc. Other things like Sharing might also get messy, because my idea of an Ecco item may be very different than yours, not to mention the sharing cloud would have to dynamically change to accomodate the changing Kinds. We might be pushing the repository to its limit to be constantly adjusting Kinds.
- Ecco items are very simple objects, and attributes are also objects.. you could store the attribute/value pairs as items that hang off of the objects - its fortunate that the repository handles lists fairly well.
- Advantages: Kinds would be very consistent. You'd probably be able to share easily with a simple cloud.
- Disadvantages: A single object (item + attributes) might take up a lot of space, since attributes would also have to be items. There would be a lot more code (python) overhead to manage the relationships between items and attributes. It might require more work to keep similar objects in sync with each other, as far as data typing goes. (but maybe that isn't an issue? the data is supposed to be pretty darn dynamic) Queries might be hard, or we might at least have to push queries very far to be able to look up objects with attributes set to specific values.
New model
I just had an epiphany about how to implement this.
First, each Ecco item has an attribute "columnvalues" of
list cardinality.. its a bidirectional reference to Items of type
ColumnValue?
A
ColumnValue? is an Item with a single bidi reference back to the "source" Item, a data-specific value, and a bidi pointer to a Column Item.
Column has a number of sub-types such as
DateColumn?,
BooleanColumn?, what have you... the
ColumnValue? subtype has to match the Column subtype
For instance
EccoItem?
- Body: single of type blob
- Parent: single of type EccoItem?
- Children: list of type EccoItem?
- columnValue: bidi List of type ColumnValue?
ColumnValue?
- parentItem: bidi single of type EccoItem?
Column
- nothing in the base type?
Subtype example:
ColumnDateValue?:
ColumnValue?
- column: bidi single of type ColumnDate?
- value: single of type Date
ColumnDate?
- values: bidi list of type ColumnDateValue?
So the mapping is something like
EccoItem? ->
ColumnValue? <-> Column
->
ColumnValue? <-> Column
I need a better diagram, but that should be the gist of it. The Bidi references should allow rotation on all the different columns.
Here's my Kind implementation:
<Kind itsName="CheekoItem">
<superKinds itemref="content:Note"/>
<!-- <classes key="python">samples.skeleton.Kind1</classes> -->
<displayName>CheekoItem</displayName>
<Attribute itsName="parent">
<displayName>Parent CheekoItem</displayName>
<cardinality>single</cardinality>
<type itemref="doc:CheekoItem"/>
<inverseAttribute itemref="doc:CheekoItem/children"/>
</Attribute>
<Attribute itsName="children">
<displayName>Child CheekoItems</displayName>
<cardinality>list</cardinality>
<type itemref="doc:CheekoItem"/>
<inverseAttribute itemref="doc:CheekoItem/parent"/>
</Attribute>
<Attribute itsName="columnValues">
<displayName>Columns</displayName>
<cardinality>list</cardinality>
<type itemref="doc:CheekoColumnValue"/>
</Attribute>
<!-- Typical clouds include a "default" cloud, and a "sharing" cloud -->
<Cloud itsName="SharingCloud">
<Endpoint itsName="parent">
<attribute value="parent"/>
</Endpoint>
<endpoints itemref="doc:CheekoItem/SharingCloud/parent"/>
<Endpoint itsName="children">
<attribute value="children"/>
</Endpoint>
<endpoints itemref="doc:CheekoItem/SharingCloud/children"/>
</Cloud>
<clouds alias="sharing" itemref="doc:CheekoItem/SharingCloud"/>
</Kind>
<Kind itsName="CheekoColumnValue">
<Attribute itsName="column">
<displayName>CheekoColumn</displayName>
<cardinality>single</cardinality>
<type itemref="doc:CheekoColumn"/>
<inverseAttribute itemref="doc:CheekoColumn/values"/>
</Attribute>
<Attribute itsName="item">
<displayName>CheekoColumnValue</displayName>
<cardinality>single</cardinality>
<type itemref="doc:CheekoItem"/>
<inverseAttribute itemref="doc:CheekoItem/columnValues"/>
</Attribute>
</Kind>
<Kind itsName="CheekoColumn">
<Attribute itsName="name">
<displayName>CheekoColumn</displayName>
<cardinality>single</cardinality>
<type itemref="doc:CheekoItem"/>
</Attribute>
<Attribute itsName="values">
<displayName>All existing Column Values for this column</displayName>
<cardinality>list</cardinality>
<type itemref="doc:CheekoColumnValue"/>
<inverseAttribute itemref="doc:CheekoColumnValue/column"/>
</Attribute>
</Kind>