mirror of
https://github.com/trailbaseio/trailbase.git
synced 2026-02-06 17:19:42 -06:00
Add dedicated documentation on how to model data and relations with SQLite and TrailBase.
This commit is contained in:
797
docs/src/content/docs/documentation/_relations.svg
Normal file
797
docs/src/content/docs/documentation/_relations.svg
Normal file
@@ -0,0 +1,797 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||
|
||||
<svg
|
||||
width="505.58176"
|
||||
height="377.57263"
|
||||
viewBox="0 0 133.7685 99.899433"
|
||||
version="1.1"
|
||||
id="svg5"
|
||||
inkscape:version="1.2.2 (b0a8486541, 2022-12-01)"
|
||||
sodipodi:docname="_relations.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
xmlns:svg="http://www.w3.org/2000/svg">
|
||||
<sodipodi:namedview
|
||||
id="namedview7"
|
||||
pagecolor="#ffffff"
|
||||
bordercolor="#000000"
|
||||
borderopacity="0.25"
|
||||
inkscape:showpageshadow="2"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="false"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="px"
|
||||
showgrid="false"
|
||||
inkscape:zoom="0.78095874"
|
||||
inkscape:cx="617.83034"
|
||||
inkscape:cy="257.37595"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1131"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="g2790" />
|
||||
<defs
|
||||
id="defs2">
|
||||
<marker
|
||||
style="overflow:visible"
|
||||
id="marker6535"
|
||||
refX="0"
|
||||
refY="0"
|
||||
orient="auto-start-reverse"
|
||||
inkscape:stockid="TriangleStart"
|
||||
markerWidth="5.3244081"
|
||||
markerHeight="6.155385"
|
||||
viewBox="0 0 5.3244081 6.1553851"
|
||||
inkscape:isstock="true"
|
||||
inkscape:collect="always"
|
||||
preserveAspectRatio="xMidYMid">
|
||||
<path
|
||||
transform="scale(0.5)"
|
||||
style="fill:context-stroke;fill-rule:evenodd;stroke:context-stroke;stroke-width:1pt"
|
||||
d="M 5.77,0 -2.88,5 V -5 Z"
|
||||
id="path6533" />
|
||||
</marker>
|
||||
<filter
|
||||
id="mask-powermask-path-effect2736_inverse"
|
||||
inkscape:label="filtermask-powermask-path-effect2736"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
height="100"
|
||||
width="100"
|
||||
x="-50"
|
||||
y="-50">
|
||||
<feColorMatrix
|
||||
id="mask-powermask-path-effect2736_primitive1"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="mask-powermask-path-effect2736_primitive2"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||
in="fbSourceGraphic" />
|
||||
</filter>
|
||||
<filter
|
||||
id="mask-powermask-path-effect2758_inverse"
|
||||
inkscape:label="filtermask-powermask-path-effect2758"
|
||||
style="color-interpolation-filters:sRGB"
|
||||
height="100"
|
||||
width="100"
|
||||
x="-50"
|
||||
y="-50">
|
||||
<feColorMatrix
|
||||
id="mask-powermask-path-effect2758_primitive1"
|
||||
values="1"
|
||||
type="saturate"
|
||||
result="fbSourceGraphic" />
|
||||
<feColorMatrix
|
||||
id="mask-powermask-path-effect2758_primitive2"
|
||||
values="-1 0 0 0 1 0 -1 0 0 1 0 0 -1 0 1 0 0 0 1 0 "
|
||||
in="fbSourceGraphic" />
|
||||
</filter>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask2782">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.529169;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2784"
|
||||
width="43.127083"
|
||||
height="47.095833"
|
||||
x="45.057217"
|
||||
y="73.149208"
|
||||
rx="2.6458333"
|
||||
ry="2.6458333" />
|
||||
</mask>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask2782-3">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.529169;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2784-6"
|
||||
width="43.127083"
|
||||
height="47.095833"
|
||||
x="45.057217"
|
||||
y="73.149208"
|
||||
rx="2.6458333"
|
||||
ry="2.6458333" />
|
||||
</mask>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask2782-3-9">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.529169;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2784-6-3"
|
||||
width="43.127083"
|
||||
height="47.095833"
|
||||
x="45.057217"
|
||||
y="73.149208"
|
||||
rx="2.6458333"
|
||||
ry="2.6458333" />
|
||||
</mask>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask2782-3-9-7">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.529169;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2784-6-3-5"
|
||||
width="43.127083"
|
||||
height="47.095833"
|
||||
x="45.057217"
|
||||
y="73.149208"
|
||||
rx="2.6458333"
|
||||
ry="2.6458333" />
|
||||
</mask>
|
||||
<mask
|
||||
maskUnits="userSpaceOnUse"
|
||||
id="mask2782-3-9-7-1">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.529169;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2784-6-3-5-9"
|
||||
width="43.127083"
|
||||
height="47.095833"
|
||||
x="45.057217"
|
||||
y="73.149208"
|
||||
rx="2.6458333"
|
||||
ry="2.6458333" />
|
||||
</mask>
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
transform="translate(-64.98357,-9.6775795)">
|
||||
<g
|
||||
id="g2790"
|
||||
transform="translate(1.6420904,-22.808358)">
|
||||
<g
|
||||
id="g6421"
|
||||
transform="translate(-0.5951938,1.8160305e-5)">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2744-7-6"
|
||||
width="36.97929"
|
||||
height="47.095833"
|
||||
x="69.010262"
|
||||
y="35.987774"
|
||||
rx="2.2686679"
|
||||
ry="2.6458333" />
|
||||
<rect
|
||||
style="fill:#87de87;stroke:#000000;stroke-width:0.571464;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2714-5-0"
|
||||
width="51.557812"
|
||||
height="13.852211"
|
||||
x="40.185043"
|
||||
y="70.225372"
|
||||
rx="2.36678"
|
||||
ry="2.8985407"
|
||||
mask="url(#mask2782-3-9)"
|
||||
transform="matrix(0.85744936,0,0,1,30.375993,-37.161433)" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-weight:bold;font-size:6.35px;line-height:0em;font-family:Inter;-inkscape-font-specification:'Inter Bold';fill:none;stroke:#000000;stroke-width:1.25651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="87.413101"
|
||||
y="43.286297"
|
||||
id="text656-3-6"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan654-5-2"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.35px;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="87.413101"
|
||||
y="43.286297">post</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-weight:bold;font-size:5.29167px;line-height:1.5em;font-family:Inter;-inkscape-font-specification:'Inter Bold';fill:none;stroke:#000000;stroke-width:1.25651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="72.020676"
|
||||
y="54.841846"
|
||||
id="text4262-6-6"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4260-2-1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="72.020676"
|
||||
y="54.841846">• id</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="72.020676"
|
||||
y="62.77935"
|
||||
id="tspan4376-9-8">• author</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="72.020676"
|
||||
y="70.716858"
|
||||
id="tspan6181">• title</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="72.020676"
|
||||
y="78.654358"
|
||||
id="tspan4631-7">• body</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="72.020676"
|
||||
y="86.591866"
|
||||
id="tspan4378-1-9" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="72.020676"
|
||||
y="94.529373"
|
||||
id="tspan4370-2-2" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="72.020676"
|
||||
y="102.46687"
|
||||
id="tspan4366-7-0" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="72.020676"
|
||||
y="110.40438"
|
||||
id="tspan4368-0-2" /></text>
|
||||
</g>
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2744-7-6-9-4"
|
||||
width="35.21933"
|
||||
height="32.737587"
|
||||
x="115.9934"
|
||||
y="35.987774"
|
||||
rx="2.2686682"
|
||||
ry="2.6458333" />
|
||||
<rect
|
||||
style="fill:#ffe680;stroke:#000000;stroke-width:0.585569;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2714-5-0-2-7"
|
||||
width="51.557812"
|
||||
height="13.852211"
|
||||
x="40.185043"
|
||||
y="70.225372"
|
||||
rx="2.4850514"
|
||||
ry="2.8985407"
|
||||
mask="url(#mask2782-3-9-7-1)"
|
||||
transform="matrix(0.81664066,0,0,1,79.197829,-37.161415)" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-weight:bold;font-size:4.7625px;line-height:0em;font-family:Inter;-inkscape-font-specification:'Inter Bold';fill:none;stroke:#000000;stroke-width:1.25651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="133.60304"
|
||||
y="43.286297"
|
||||
id="text656-3-6-2-8"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan654-5-2-8-4"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:4.7625px;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="133.60304"
|
||||
y="43.286297">post_tag</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-weight:bold;font-size:5.29167px;line-height:1.5em;font-family:Inter;-inkscape-font-specification:'Inter Bold';fill:none;stroke:#000000;stroke-width:1.25651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="117.63883"
|
||||
y="54.841846"
|
||||
id="text4262-6-6-9-5"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4260-2-1-7-0"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="117.63883"
|
||||
y="54.841846">• post</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="117.63883"
|
||||
y="62.77935"
|
||||
id="tspan4631-7-6-1">• tag</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="117.63883"
|
||||
y="70.716858"
|
||||
id="tspan4378-1-9-1-0" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="117.63883"
|
||||
y="78.654358"
|
||||
id="tspan4370-2-2-2-6" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="117.63883"
|
||||
y="86.591866"
|
||||
id="tspan4366-7-0-9-3" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="117.63883"
|
||||
y="94.529373"
|
||||
id="tspan4368-0-2-3-2" /></text>
|
||||
<g
|
||||
id="g720-3"
|
||||
transform="translate(72.757314,9.7043009)">
|
||||
<rect
|
||||
style="fill:#ececec;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
id="rect659-6"
|
||||
width="10.015675"
|
||||
height="6.3694124"
|
||||
x="21.495552"
|
||||
y="48.592373"
|
||||
rx="1.5875"
|
||||
ry="1.5875" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.7625px;line-height:0em;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="26.360374"
|
||||
y="53.309708"
|
||||
id="text663-7"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan661-5"
|
||||
style="fill:#000000;stroke:none;stroke-width:0.529167"
|
||||
x="26.360374"
|
||||
y="53.309708">FK</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="g3167"
|
||||
transform="translate(59.546151,4.8493188)">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2744-7"
|
||||
width="41.781292"
|
||||
height="40.334202"
|
||||
x="9.9245825"
|
||||
y="86.937263"
|
||||
rx="2.2686679"
|
||||
ry="2.6458333" />
|
||||
<rect
|
||||
style="fill:#87cdde;stroke:#000000;stroke-width:0.537622;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2714-5"
|
||||
width="51.557812"
|
||||
height="13.852211"
|
||||
x="40.185043"
|
||||
y="70.225372"
|
||||
rx="2.0947616"
|
||||
ry="2.8985407"
|
||||
mask="url(#mask2782-3)"
|
||||
transform="matrix(0.9687947,0,0,1,-33.726604,13.788048)" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-weight:bold;font-size:6.35px;line-height:0em;font-family:Inter;-inkscape-font-specification:'Inter Bold';fill:none;stroke:#000000;stroke-width:1.25651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="31.020739"
|
||||
y="94.235786"
|
||||
id="text656-3"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan654-5"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.35px;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="31.020739"
|
||||
y="94.235786">profile</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-weight:bold;font-size:5.29167px;line-height:1.5em;font-family:Inter;-inkscape-font-specification:'Inter Bold';fill:none;stroke:#000000;stroke-width:1.25651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="11.394978"
|
||||
y="105.7913"
|
||||
id="text4262-6"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4260-2"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="11.394978"
|
||||
y="105.7913">• id</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="11.394978"
|
||||
y="113.72881"
|
||||
id="tspan4376-9">• user</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="11.394978"
|
||||
y="121.66631"
|
||||
id="tspan4631">• name</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="11.394978"
|
||||
y="129.60381"
|
||||
id="tspan5873" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="11.394978"
|
||||
y="137.54132"
|
||||
id="tspan4378-1" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="11.394978"
|
||||
y="145.47882"
|
||||
id="tspan4370-2" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="11.394978"
|
||||
y="153.41632"
|
||||
id="tspan4366-7" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="11.394978"
|
||||
y="161.35384"
|
||||
id="tspan4368-0" /></text>
|
||||
<g
|
||||
id="g720"
|
||||
transform="translate(6.7702711,60.666195)">
|
||||
<rect
|
||||
style="fill:#ececec;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
id="rect659"
|
||||
width="10.015675"
|
||||
height="6.3694124"
|
||||
x="21.495552"
|
||||
y="48.592373"
|
||||
rx="1.5875"
|
||||
ry="1.5875" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.7625px;line-height:0em;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="26.360374"
|
||||
y="53.309708"
|
||||
id="text663"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan661"
|
||||
style="fill:#000000;stroke:none;stroke-width:0.529167"
|
||||
x="26.360374"
|
||||
y="53.309708">FK</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="g720-36"
|
||||
transform="translate(18.56777,60.829586)">
|
||||
<rect
|
||||
style="fill:#ececec;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
id="rect659-7"
|
||||
width="10.015675"
|
||||
height="6.3694124"
|
||||
x="21.495552"
|
||||
y="48.592373"
|
||||
rx="1.5875"
|
||||
ry="1.5875" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:3.70417px;line-height:0em;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="26.360374"
|
||||
y="53.309708"
|
||||
id="text663-5"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan661-3"
|
||||
style="font-size:3.70417px;fill:#000000;stroke:none;stroke-width:0.529167"
|
||||
x="26.360374"
|
||||
y="53.309708">Uniq</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="g1291"
|
||||
transform="translate(0.12494804,52.419428)">
|
||||
<rect
|
||||
style="fill:#ececec;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
id="rect659-6-6"
|
||||
width="10.015675"
|
||||
height="6.3694124"
|
||||
x="21.495552"
|
||||
y="48.592373"
|
||||
rx="1.5875"
|
||||
ry="1.5875" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.7625px;line-height:0em;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="26.360374"
|
||||
y="53.309708"
|
||||
id="text663-7-2"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan661-5-6"
|
||||
style="fill:#000000;stroke:none;stroke-width:0.529167"
|
||||
x="26.360374"
|
||||
y="53.309708">PK</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g1291-9"
|
||||
transform="translate(61.316649,1.369148)">
|
||||
<rect
|
||||
style="fill:#ececec;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
id="rect659-6-6-2"
|
||||
width="10.015675"
|
||||
height="6.3694124"
|
||||
x="21.495552"
|
||||
y="48.592373"
|
||||
rx="1.5875"
|
||||
ry="1.5875" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.7625px;line-height:0em;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="26.360374"
|
||||
y="53.309708"
|
||||
id="text663-7-2-0"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan661-5-6-2"
|
||||
style="fill:#000000;stroke:none;stroke-width:0.529167"
|
||||
x="26.360374"
|
||||
y="53.309708">PK</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="g639"
|
||||
transform="translate(65.284322,-9.6728225)">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2744"
|
||||
width="36.97929"
|
||||
height="33.131294"
|
||||
x="68.767479"
|
||||
y="101.4594"
|
||||
rx="2.2686679"
|
||||
ry="2.6458333" />
|
||||
<rect
|
||||
style="fill:#eeaaff;stroke:#000000;stroke-width:0.571464;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2714"
|
||||
width="51.557812"
|
||||
height="13.852211"
|
||||
x="40.185043"
|
||||
y="70.225372"
|
||||
rx="2.36678"
|
||||
ry="2.8985407"
|
||||
mask="url(#mask2782)"
|
||||
transform="matrix(0.85744936,0,0,1,30.133193,28.310199)" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-weight:bold;font-size:6.35px;line-height:0em;font-family:Inter;-inkscape-font-specification:'Inter Bold';fill:none;stroke:#000000;stroke-width:1.25651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="86.767235"
|
||||
y="108.75793"
|
||||
id="text656"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan654"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.35px;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="86.767235"
|
||||
y="108.75793">_user</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-weight:bold;font-size:5.29167px;line-height:1.5em;font-family:Inter;-inkscape-font-specification:'Inter Bold';fill:none;stroke:#000000;stroke-width:1.25651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="71.777885"
|
||||
y="120.31348"
|
||||
id="text4262"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4260"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="71.777885"
|
||||
y="120.31348">• id</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="71.777885"
|
||||
y="128.25098"
|
||||
id="tspan4376">• ...</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="71.777885"
|
||||
y="136.18849"
|
||||
id="tspan4378" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="71.777885"
|
||||
y="144.12599"
|
||||
id="tspan4370" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="71.777885"
|
||||
y="152.06349"
|
||||
id="tspan4366" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="71.777885"
|
||||
y="160.00101"
|
||||
id="tspan4368" /></text>
|
||||
<g
|
||||
id="g1291-9-3"
|
||||
transform="translate(60.685222,66.720435)">
|
||||
<rect
|
||||
style="fill:#ececec;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
id="rect659-6-6-2-7"
|
||||
width="10.015675"
|
||||
height="6.3694124"
|
||||
x="21.495552"
|
||||
y="48.592373"
|
||||
rx="1.5875"
|
||||
ry="1.5875" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.7625px;line-height:0em;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="26.360374"
|
||||
y="53.309708"
|
||||
id="text663-7-2-0-5"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan661-5-6-2-9"
|
||||
style="fill:#000000;stroke:none;stroke-width:0.529167"
|
||||
x="26.360374"
|
||||
y="53.309708">PK</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g720-3-3"
|
||||
transform="translate(113.65745,1.5521743)">
|
||||
<rect
|
||||
style="fill:#ececec;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
id="rect659-6-5"
|
||||
width="10.015675"
|
||||
height="6.3694124"
|
||||
x="21.495552"
|
||||
y="48.592373"
|
||||
rx="1.5875"
|
||||
ry="1.5875" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.7625px;line-height:0em;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="26.360374"
|
||||
y="53.309708"
|
||||
id="text663-7-6"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan661-5-2"
|
||||
style="fill:#000000;stroke:none;stroke-width:0.529167"
|
||||
x="26.360374"
|
||||
y="53.309708">FK</tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="g700"
|
||||
transform="translate(-6.7304382)">
|
||||
<rect
|
||||
style="fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2744-7-6-9"
|
||||
width="36.97929"
|
||||
height="32.98344"
|
||||
x="166.59656"
|
||||
y="35.987774"
|
||||
rx="2.2686679"
|
||||
ry="2.6458333" />
|
||||
<rect
|
||||
style="fill:#ffccaa;stroke:#000000;stroke-width:0.571464;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
id="rect2714-5-0-2"
|
||||
width="51.557812"
|
||||
height="13.852211"
|
||||
x="40.185043"
|
||||
y="70.225372"
|
||||
rx="2.36678"
|
||||
ry="2.8985407"
|
||||
mask="url(#mask2782-3-9-7)"
|
||||
transform="matrix(0.85744936,0,0,1,127.96296,-37.161423)" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-weight:bold;font-size:6.35px;line-height:0em;font-family:Inter;-inkscape-font-specification:'Inter Bold';fill:none;stroke:#000000;stroke-width:1.25651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="184.99913"
|
||||
y="43.286297"
|
||||
id="text656-3-6-2"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan654-5-2-8"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.35px;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="184.99913"
|
||||
y="43.286297">tag</tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-weight:bold;font-size:5.29167px;line-height:1.5em;font-family:Inter;-inkscape-font-specification:'Inter Bold';fill:none;stroke:#000000;stroke-width:1.25651;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="169.60693"
|
||||
y="54.841846"
|
||||
id="text4262-6-6-9"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan4260-2-1-7"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="169.60693"
|
||||
y="54.841846">• id</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="169.60693"
|
||||
y="62.77935"
|
||||
id="tspan4376-9-8-3">• label</tspan><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="169.60693"
|
||||
y="70.716858"
|
||||
id="tspan4631-7-6" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="169.60693"
|
||||
y="78.654358"
|
||||
id="tspan4378-1-9-1" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="169.60693"
|
||||
y="86.591866"
|
||||
id="tspan4370-2-2-2" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="169.60693"
|
||||
y="94.529373"
|
||||
id="tspan4366-7-0-9" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;font-family:Inter;-inkscape-font-specification:Inter;fill:#000000;stroke:none;stroke-width:1.25651"
|
||||
x="169.60693"
|
||||
y="102.46687"
|
||||
id="tspan4368-0-2-3" /></text>
|
||||
<g
|
||||
id="g1291-9-3-2"
|
||||
transform="translate(158.40392,1.264378)">
|
||||
<rect
|
||||
style="fill:#ececec;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
id="rect659-6-6-2-7-2"
|
||||
width="10.015675"
|
||||
height="6.3694124"
|
||||
x="21.495552"
|
||||
y="48.592373"
|
||||
rx="1.5875"
|
||||
ry="1.5875" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.7625px;line-height:0em;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="26.360374"
|
||||
y="53.309708"
|
||||
id="text663-7-2-0-5-8"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan661-5-6-2-9-9"
|
||||
style="fill:#000000;stroke:none;stroke-width:0.529167"
|
||||
x="26.360374"
|
||||
y="53.309708">PK</tspan></text>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
id="g720-3-3-9"
|
||||
transform="translate(113.68575,9.3405403)">
|
||||
<rect
|
||||
style="fill:#ececec;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
id="rect659-6-5-1"
|
||||
width="10.015675"
|
||||
height="6.3694124"
|
||||
x="21.495552"
|
||||
y="48.592373"
|
||||
rx="1.5875"
|
||||
ry="1.5875" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-size:4.7625px;line-height:0em;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#ffffff;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40"
|
||||
x="26.360374"
|
||||
y="53.309708"
|
||||
id="text663-7-6-2"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan661-5-2-7"
|
||||
style="fill:#000000;stroke:none;stroke-width:0.529167"
|
||||
x="26.360374"
|
||||
y="53.309708">FK</tspan></text>
|
||||
</g>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none;marker-end:url(#marker6535)"
|
||||
d="m 72.865201,61.260787 h -9.254692 v 23.957292 h 68.320101 l 0.078,18.446701 4.46379,3.65987"
|
||||
id="path5929"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:5.29167px;line-height:0em;font-family:Inter;-inkscape-font-specification:Inter;text-align:center;text-anchor:middle;fill:#87de87;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
x="76.259377"
|
||||
y="56.282932"
|
||||
id="text6177"><tspan
|
||||
sodipodi:role="line"
|
||||
id="tspan6175"
|
||||
style="stroke-width:0.529167"
|
||||
x="76.259377"
|
||||
y="56.282932" /><tspan
|
||||
sodipodi:role="line"
|
||||
style="stroke-width:0.529167"
|
||||
id="tspan6179"
|
||||
x="76.259377"
|
||||
y="56.282932" /></text>
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none;marker-end:url(#marker6535)"
|
||||
d="m 118.97093,61.264835 h -7.81308 v 10.932692 h 42.90504 V 53.287849 h 7.8427"
|
||||
id="path6483"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none"
|
||||
d="m 118.99767,53.316534 h -8.32273 V 32.750521 H 63.606063 v 20.524647 h 8.521551"
|
||||
id="path6485"
|
||||
sodipodi:nodetypes="cccccc" />
|
||||
<path
|
||||
style="fill:none;stroke:#000000;stroke-width:0.529167;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:40;stroke-dasharray:none;marker-end:url(#marker6535)"
|
||||
d="M 72.31685,109.0099 H 63.610509 V 88.77312 h 64.736831 v 25.32127 h 3.56458 l 4.40153,-3.43366"
|
||||
id="path1264"
|
||||
sodipodi:nodetypes="ccccccc" />
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 38 KiB |
238
docs/src/content/docs/documentation/models_and_relations.mdx
Normal file
238
docs/src/content/docs/documentation/models_and_relations.mdx
Normal file
@@ -0,0 +1,238 @@
|
||||
---
|
||||
title: Models & Relations
|
||||
---
|
||||
|
||||
import { Image } from "astro:assets";
|
||||
import { Aside } from "@astrojs/starlight/components";
|
||||
|
||||
import foo from "./_relations.svg"
|
||||
|
||||
## Modeling Data
|
||||
|
||||
TrailBase gives you full, untethered access to SQLite, as such data is modeled
|
||||
on top of ISO SQL and SQLite concepts.
|
||||
This means that all data is organized as rows or records within columns of a
|
||||
table as defined by the table schema.
|
||||
Relationships between records are expressed by simply referencing other records
|
||||
via their primary key. Relationships commonly cross table boundaries.
|
||||
Data can then be *joined* together within the same database at query time.
|
||||
If you're new to SQL and this sounds abstract, don't worry it will become clear
|
||||
very soon.
|
||||
|
||||
One of the main benefits of SQL databases is that you can define your models
|
||||
based on intrinsic properties of the underlying data and their relations,
|
||||
rather than having to worry about how the data is or will be used in the future.
|
||||
Instead, SQL *queries* let you flexibly define what data is being accessed, how
|
||||
it's transformed and what's returned.
|
||||
*How* data is accessed is never explicitly defined and is derived by the
|
||||
[*query optimizer*](https://sqlite.org/optoverview.html).
|
||||
This means, if you discover new use-cases in your data that require combining
|
||||
data in ways that would be slow, you can optimize it after the fact by adding
|
||||
ore removing indexes typically without ever having to touch the models, the
|
||||
queries, or the downstream code consuming the data 🎉.
|
||||
|
||||
### Tables, Schemas & Data Types
|
||||
|
||||
When creating a new table to hold your data, you define a table schema telling
|
||||
the database what columns there are, what kind of data they contain, and if
|
||||
there are any constraints on your data. For example[^1],
|
||||
|
||||
```sql
|
||||
CREATE TABLE post (
|
||||
id INTEGER PRIMARY KEY,
|
||||
created INTEGER NOT NULL DEFAULT (UNIXEPOCH()),
|
||||
author INTEGER NOT NULL REFERENCES _user ON DELETE CASCADE,
|
||||
|
||||
title TEXT NOT NULL,
|
||||
body TEXT NOT NULL
|
||||
);
|
||||
```
|
||||
|
||||
This creates a table to hold posts in a blog storing an integer creation
|
||||
timestamp, the author, the title and lastly the contents.
|
||||
We also set it up such that an author deleting their account, will delete their
|
||||
posts as well through cascading deletions.
|
||||
|
||||
Coming from other SQL databases, it may come as a surprise that despite the
|
||||
data types above SQLite isn't strictly typed by default, e.g. the `title`
|
||||
column above may hold values other than `TEXT`.
|
||||
SQLite interprets types merely as *affinities* to judge how literals should be
|
||||
interpreted on insert or or update but will happily accept incompatible values.
|
||||
While "flexible", this has far reaching consequences on downstream code
|
||||
interpreting or transforming data, now having to explicitly deal with
|
||||
unexpected data types.
|
||||
Thus, we strongly encourage working with `STRICT` table schemas whenever
|
||||
possible.
|
||||
In fact, TrailBase APIs are type-safe and thus require the underlying tables to
|
||||
be `STRICT`, i.e.:
|
||||
|
||||
```sql {4}
|
||||
CREATE TABLE post (
|
||||
id INTEGER PRIMARY KEY,
|
||||
-- ...
|
||||
) STRICT;
|
||||
```
|
||||
|
||||
At the fundamental storage-level SQLite supports the following data types:
|
||||
`NULL`, `INTEGER`, `REAL`, `TEXT`, `BLOB`, which all type affinities boil down
|
||||
to.
|
||||
For example, columns with `JSON` or `JSONB` type affinities are stored as
|
||||
`TEXT` or `BLOB`, respectively[^2].
|
||||
|
||||
When defining `STRICT` tables there's no affinity, limiting you to SQLite's
|
||||
fundamental types.
|
||||
However, TrailBase provides you with some extensions to enforce strict schema
|
||||
compatibility on a sub-column-level, e.g.
|
||||
|
||||
```sql {3-4}
|
||||
CREATE TABLE my_table (
|
||||
id INTEGER PRIMARY KEY,
|
||||
any_json TEXT CHECK(is_json(any_json)),
|
||||
my_json TEXT CHECK(jsonschema('MyRegisteredJsonSchema', my_json))
|
||||
) STRICT;
|
||||
```
|
||||
|
||||
### Constraints
|
||||
|
||||
Using TrailBase you can make use of any of SQLite's column and table
|
||||
constraints. We've already encountered some of the former, e.g. `NOT NULL`,
|
||||
`REFERENCES` or `CHECK`, which all *constrain* the values a column may
|
||||
contain.
|
||||
|
||||
Similarly, one can define more complex table constraints constraining tuples of
|
||||
values, e.g.
|
||||
|
||||
```sql {5}
|
||||
CREATE TABLE fully_qualifed_ids (
|
||||
prefix TEXT NOT NULL,
|
||||
suffix TEXT NOT NULL,
|
||||
|
||||
UNIQUE (prefix, suffix)
|
||||
) STRICT;
|
||||
```
|
||||
|
||||
For a complete list of constraints, check out the
|
||||
[SQLite manual](https://www.sqlite.org/lang_createtable.html).
|
||||
|
||||
### Generated Columns
|
||||
|
||||
SQLite's generated columns allow you to either materialized derived columns at
|
||||
modification-time or compute values for virtual columns on the fly. Check out
|
||||
[SQLite manual](https://www.sqlite.org/lang_createtable.html) for comprehensive
|
||||
information.
|
||||
|
||||
|
||||
## Relations
|
||||
|
||||
SQL typically distinguishes between three types of relations:
|
||||
|
||||
* 1:1 relations, e.g. each user has exactly one profile.
|
||||
* 1:M or one-to-many relations, e.g. posts may have many comments but each
|
||||
comment belongs to exactly one post.
|
||||
* N:M or many-to-many relations, e.g. shoppers' wishlists can contain many
|
||||
items, and each item can be in many wishlists.
|
||||
|
||||
In practice, **all relations are simply edges, i.e. tuples of the shape
|
||||
`(parent id, child id)`**.
|
||||
There's some freedom as to where these edges are stored. In case of 1:1 and 1:M
|
||||
relations they can be denormalized into the child record effectively becoming
|
||||
`(primary key, foreign key)`.
|
||||
Alternatively, edges can always be stored in a separate "bridge" table as
|
||||
`(foreign key, foreign key)` edge records.
|
||||
In case of N:M relations, this is even necessary to achieve the required
|
||||
cardinality.
|
||||
This is not a limitation of TrailBase but rather common SQL practice.
|
||||
Linking children to their parents individually[^2] via foreign keys exposes
|
||||
their relationships to the database allowing `ON DELETE` and `ON UPDATE`
|
||||
actions to propagate.
|
||||
For example, a user deletion may trigger related data to be deleted
|
||||
automatically.
|
||||
|
||||
<Aside type="note" title="PocketBase">
|
||||
If you're coming from PocketBase, 1:M and N:M relations are modeled as
|
||||
denormalized lists of primary keys in JSON format.
|
||||
While this *adjacency list* approach may feel more intuitive, it is opaque to
|
||||
SQLite and thus breaking built-in foreign key support.
|
||||
</Aside>
|
||||
|
||||
Let's look at the following *blog* example,
|
||||
|
||||
<div class="flex justify-center">
|
||||
<div class="max-w-[420px]">
|
||||
<Image src={foo} alt="Blog relationship example" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Each block represents a able schema. We can see:
|
||||
* a 1:1 relation between users and user profiles,
|
||||
* a 1:M relationship between posts and users,
|
||||
* and an N:M relationship between posts and tags using the `post_tag` bridge table.
|
||||
|
||||
We could have implemented the 1:1 user-profile and 1:M user-post relationships
|
||||
via separate bridge tables with appropriate uniqueness constraints, however
|
||||
pulling the parent key into the child records leads to less indirection.
|
||||
|
||||
In order to combine related data we can simply join on the keys. For example to
|
||||
get a list of all users with profiles:
|
||||
|
||||
```sql
|
||||
SELECT * FROM _user AS U
|
||||
JOIN profile AS P ON U.id = P.user;
|
||||
```
|
||||
|
||||
To connect posts and tags we have to do two joins across the `post_tag` bridge
|
||||
table:
|
||||
|
||||
```sql
|
||||
SELECT * FROM post AS P
|
||||
LEFT JOIN post_tag AS PT ON P.id = PT.post
|
||||
LEFT JOIN tag AS T ON T.ID = PT.tag;
|
||||
```
|
||||
|
||||
### Record APIs
|
||||
|
||||
There's an elephant in the room: while this is all pretty standard fair, at
|
||||
least in SQL land, connecting relations across TrailBase's record APIs without
|
||||
joins is a lot more painful.
|
||||
For N:M relations, one would have to expose 3 different APIs including the
|
||||
bridge table, manage their ACLs and then traverse the edges manually on the
|
||||
client side.
|
||||
|
||||
This is an area where we're actively exploring how to expand API capabilities,
|
||||
however the most general approach is to push more responsibility to the
|
||||
server.
|
||||
Concretely, we can expose a single API tailored for a specific client use-case
|
||||
implementing the join on the server using `VIEW`s:
|
||||
|
||||
```sql
|
||||
CREATE VIEW post_tag_view AS SELECT * FROM post AS P
|
||||
LEFT JOIN post_tag AS PT ON P.id = PT.post
|
||||
LEFT JOIN tag AS T ON T.ID = PT.tag;
|
||||
```
|
||||
|
||||
More generally, views can be useful to decouple an API from the underlying data
|
||||
model.
|
||||
For example, you may want to restructure your data model or APIs while keeping
|
||||
the other stable.
|
||||
|
||||
Alternatively, custom JS/TS handlers can provide a more free-form approach to
|
||||
push joining edges and other responsibilities to the server.
|
||||
|
||||
<div class="h-[50px]" />
|
||||
|
||||
---
|
||||
|
||||
[^1]:
|
||||
This is not meant as full SQL tutorial. We'll keep it simple so hopefully
|
||||
it is clear from context what any statement intends to do.
|
||||
Note further that SQL keywords are case-insensitive. We'll use all-caps to
|
||||
highlight them.
|
||||
|
||||
[^2]:
|
||||
This also means than one can use SQLite's builtin JSON operators on any
|
||||
TEXT column as long as it parses as JSON.
|
||||
|
||||
[^3]:
|
||||
Even feature rich SQL databases like Postgres
|
||||
[do not support foreign key arrays](https://commitfest.postgresql.org/17/1252/)
|
||||
natively and rely on separate relationship tables.
|
||||
Reference in New Issue
Block a user