A look at the database tree
Let us have a look at how the database tree is like in a case with indexed models, external models attributes and indexed models attributes.
First-level model
>>> class Saloon(sheraf.Model):
... table = "saloon"
... location = sheraf.StringAttribute()
...
>>> with sheraf.connection(commit=True):
... saloon = Saloon.create(location="heaven")
Here we have a Model
Saloon with a table named saloon, and we
create one instance of Saloon. In the database, it means that the table saloon will be
created to host all the index tables for this model. The only index here is id (that is
implicit for all the first-level models), and an index table will be created for ids.
The database tree will look like this:
![digraph cowboy {
node [
shape="box"
];
edge [
arrowhead="none"
];
"root" [label="root"];
"table_saloon" [label="OOBTree"];
"root" -> "table_saloon" [label=" saloon"];
"saloon_index_id" [label="OOBTree"];
"saloonmapping" [label="SmallDict
id: '62a29766-3ccc'
location: 'heaven'"];
"table_saloon" -> "saloon_index_id" [label="id"];
"saloon_index_id" -> "saloonmapping" [label="62a29766-3ccc"];
}](_images/graphviz-ea99f9fd147c874cbbfc82125a3d7fdc04928edf.png)
Note that for the sake of readability, the UUID
have been shortened in the graphs.
Additional indexes
Now let us add some cowboys in the saloon.
>>> class Cowboy(sheraf.Model):
... table = "cowboy"
... name = sheraf.StringAttribute().index(unique=True)
... age = sheraf.IntegerAttribute()
...
>>> with sheraf.connection(commit=True):
... peter = Cowboy.create(name="peter", age=30)
Here we define a Cowboy model with a table named cowboy. In addition to the default id index, it has an index on name. The new name index will be added in the Cowboy table near id.
![digraph cowboy {
node [
shape="box"
];
edge [
arrowhead="none"
];
"root" [label="root"];
"table_saloon" [label="OOBTree"];
"table_cowboy" [label="OOBTree"];
"root" -> "table_saloon" [label=" saloon"];
"root" -> "table_cowboy" [label=" cowboy"];
subgraph cowboy {
"cowboy_index_id" [label="OOBTree"];
"cowboy_index_name" [label="OOBTree"];
"table_cowboy" -> "cowboy_index_id" [label=" id"];
"table_cowboy" -> "cowboy_index_name" [label=" name"];
subgraph peter {
"persistent_peter" [label="SmallDict
id: 238745982085
name: 'peter'"];
"cowboy_index_id" -> "persistent_peter" [label=" 146de06d-b700"];
"cowboy_index_name" -> "persistent_peter" [label=" peter"];
}
}
subgraph saloon {
"saloon_index_id" [label="OOBTree"];
"saloonmapping" [label="SmallDict
id: '62a29766-3ccc'
location: 'heaven'"];
"table_saloon" -> "saloon_index_id" [label="id"];
"saloon_index_id" -> "saloonmapping" [label="62a29766-3ccc"];
}
}](_images/graphviz-db22870159146c1ca24a465c97f8b403cca4afa3.png)
Multiple indexes
What if we want to remember the favorite guns of the cowboys.
>>> class Cowboy(sheraf.Model):
... table = "multiple_cowboy"
... name = sheraf.StringAttribute().index(unique=True)
... age = sheraf.IntegerAttribute()
... gun = sheraf.StringAttribute().index()
...
>>> with sheraf.connection(commit=True):
... peter = Cowboy.create(name="peter", age=30)
... peter.gun = "remington"
We edited Cowboy to add a gun index. Indices are multiple by default,
so as we did not passed any argument to index()
,
the gun index is multiple.
This means that each entry in the gun index will match a list of references
to Cowboy instances (instead of a single reference if the index has been unique). By default
a LargeList
are used.
![digraph cowboy {
node [
shape="box"
];
edge [
arrowhead="none"
];
"root" [label="root"];
"table_saloon" [label="OOBTree"];
"table_cowboy" [label="OOBTree"];
"root" -> "table_saloon" [label=" saloon"];
"root" -> "table_cowboy" [label=" multiple_cowboy"];
subgraph cowboy {
"cowboy_index_id" [label="OOBTree"];
"cowboy_index_name" [label="OOBTree"];
"cowboy_index_gun" [label="OOBTree"];
"cowboy_index_gun_list" [label="LargeList"];
"table_cowboy" -> "cowboy_index_id" [label=" id"];
"table_cowboy" -> "cowboy_index_name" [label=" name"];
"table_cowboy" -> "cowboy_index_gun" [label=" gun"];
"persistent_peter" [label="SmallDict
id: 238745982085
name: 'peter'
gun: 'remington'"];
"cowboy_index_id" -> "persistent_peter" [label=" 146de06d-b700"];
"cowboy_index_name" -> "persistent_peter" [label=" peter"];
"cowboy_index_gun" -> "cowboy_index_gun_list" [label=" remington"];
"cowboy_index_gun_list" -> "persistent_peter" [label=" 0"];
}
subgraph saloon {
"saloon_index_id" [label="OOBTree"];
"saloonmapping" [label="SmallDict
id: '62a29766-3ccc'
location: 'heaven'"];
"table_saloon" -> "saloon_index_id" [label="id"];
"saloon_index_id" -> "saloonmapping" [label="62a29766-3ccc"];
}
}](_images/graphviz-1370e98a94f72b298791b204f243abb77ae52dbb.png)
External model attributes
We just forgot to link the cowboys and the saloons.
>>> class Cowboy(sheraf.Model):
... table = "external_cowboy"
... name = sheraf.StringAttribute().index(unique=True)
... age = sheraf.IntegerAttribute()
... gun = sheraf.StringAttribute().index()
... saloon = sheraf.ModelAttribute(Saloon)
...
>>> with sheraf.connection(commit=True):
... peter = Cowboy.create(name="peter", age=30, gun="remington")
... peter.saloon = saloon
We edited Cowboy to add an external reference to the Saloon model we created before. As external references are not real ZODB references, just the Saloon id is stored.
![digraph cowboy {
node [
shape="box"
];
edge [
arrowhead="none"
];
"root" [label="root"];
"table_saloon" [label="OOBTree"];
"table_cowboy" [label="OOBTree"];
"root" -> "table_saloon" [label=" saloon"];
"root" -> "table_cowboy" [label=" external_cowboy"];
subgraph cowboy {
"cowboy_index_id" [label="OOBTree"];
"cowboy_index_name" [label="OOBTree"];
"cowboy_index_gun" [label="OOBTree"];
"cowboy_index_gun_list" [label="LargeList"];
"table_cowboy" -> "cowboy_index_id" [label=" id"];
"table_cowboy" -> "cowboy_index_name" [label=" name"];
"table_cowboy" -> "cowboy_index_gun" [label=" gun"];
"persistent_peter" [label="SmallDict
id: 238745982085
name: 'peter'
gun: 'remington'
saloon: '62a29766-3ccc'"];
"cowboy_index_id" -> "persistent_peter" [label=" 146de06d-b700"];
"cowboy_index_name" -> "persistent_peter" [label=" peter"];
"cowboy_index_gun" -> "cowboy_index_gun_list" [label=" remington"];
"cowboy_index_gun_list" -> "persistent_peter" [label=" 0"];
}
subgraph saloon {
"saloon_index_id" [label="OOBTree"];
"saloonmapping" [label="SmallDict
id: '62a29766-3ccc'
location: 'heaven'"];
"table_saloon" -> "saloon_index_id" [label="id"];
"saloon_index_id" -> "saloonmapping" [label="62a29766-3ccc"];
}
}](_images/graphviz-4847864e4b2a19a72a4d7721e319b81ac9eafff2.png)
Indexed attribute models
Now we should consider some horses so cowboys can actually go in the saloon.
>>> class Horse(sheraf.AttributeModel):
... name = sheraf.StringAttribute().index(primary=True)
... breed = sheraf.StringAttribute()
...
>>> class Cowboy(sheraf.Model):
... table = "indexed_cowboy"
... name = sheraf.StringAttribute().index()
... age = sheraf.IntegerAttribute()
... gun = sheraf.StringAttribute().index()
... saloon = sheraf.ModelAttribute(Saloon)
... horses = sheraf.IndexedModelAttribute(Horse)
...
>>> with sheraf.connection(commit=True):
... steven = Cowboy.create(name="steven", age=35)
... jolly = steven.horses.create(name="jolly", breed="mustang")
... polly = steven.horses.create(name="polly", breed="shetland")
Here we added a Horse attribute model, with its name as the primary index. We modified Cowboy so it can host Horse instances. Then we created a new cowboy called steven that own two horses.
The indexation mechanism works near the same way for first-level models or
attribute models. So, the index table for Horse is an OOBTree. The Horse
only index is name (there is no id index as this is not a first-level model).
So there is another OOBTree for names in the Horse index table. Then,
the horses SmallDict
are indexed by their names.
![digraph cowboy {
node [
shape="box"
];
edge [
arrowhead="none"
];
"root" [label="root"];
"table_saloon" [label="OOBTree"];
"table_cowboy" [label="OOBTree"];
"root" -> "table_saloon" [label=" saloon"];
"root" -> "table_cowboy" [label=" indexed_cowboy"];
subgraph cowboy {
"cowboy_index_id" [label="OOBTree"];
"cowboy_index_name" [label="OOBTree"];
"cowboy_index_gun" [label="OOBTree"];
"cowboy_index_gun_list" [label="LargeList"];
"table_cowboy" -> "cowboy_index_id" [label=" id"];
"table_cowboy" -> "cowboy_index_name" [label=" name"];
"table_cowboy" -> "cowboy_index_gun" [label=" gun"];
subgraph peter {
"persistent_peter" [label="SmallDict
id: 238745982085
name: 'peter'
saloon: '62a29766-3ccc'"];
"cowboy_index_id" -> "persistent_peter" [label=" 146de06d-b700"];
"cowboy_index_name" -> "persistent_peter" [label=" peter"];
"cowboy_index_gun" -> "cowboy_index_gun_list" [label=" remington"];
"cowboy_index_gun_list" -> "persistent_peter" [label=" 0"];
}
subgraph steven {
"persistent_steven" [label="SmallDict
id: 2715507222553
name: 'steven'"];
"persistent_horses" [label="OOBTree"];
"horse_name_index" [label="OOBTree"];
"jollymapping" [label="SmallDict
name: 'jolly'
breed: 'mustang'"];
"pollymapping" [label="SmallDict
name: 'polly'
breed: 'shetland'"];
"cowboy_index_id" -> "persistent_steven" [label=" a9a6938f-4800"];
"cowboy_index_name" -> "persistent_steven" [label=" steven"];
"persistent_steven" -> "persistent_horses" [label=" horses"];
"persistent_horses" -> "horse_name_index" [label=" name"];
"horse_name_index" -> "jollymapping" [label=" jolly"];
"horse_name_index" -> "pollymapping" [label=" polly"];
}
}
subgraph saloon {
"saloon_index_id" [label="OOBTree"];
"saloonmapping" [label="SmallDict
id: '62a29766-3ccc'
location: 'heaven'"];
"table_saloon" -> "saloon_index_id" [label="id"];
"saloon_index_id" -> "saloonmapping" [label="62a29766-3ccc"];
}
}](_images/graphviz-8418a481e338547e0d7b1c7d76b17490d8f57d98.png)