jinaga-server 2.3.0 → 2.3.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (35) hide show
  1. package/dist/authentication/authentication-device.d.ts +0 -1
  2. package/dist/authentication/authentication-device.js +0 -1
  3. package/dist/authentication/authentication-device.js.map +1 -1
  4. package/dist/authorization/authorization-keystore.d.ts +1 -1
  5. package/dist/authorization/authorization-keystore.js +4 -1
  6. package/dist/authorization/authorization-keystore.js.map +1 -1
  7. package/dist/index.d.ts +10 -0
  8. package/dist/index.js +17 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/jinaga-server.js +0 -6
  11. package/dist/jinaga-server.js.map +1 -1
  12. package/dist/postgres/postgres-store.js +2 -2
  13. package/dist/postgres/postgres-store.js.map +1 -1
  14. package/dist/postgres/specification-sql.js +17 -9
  15. package/dist/postgres/specification-sql.js.map +1 -1
  16. package/dist/util/fn.d.ts +0 -4
  17. package/dist/util/fn.js +1 -59
  18. package/dist/util/fn.js.map +1 -1
  19. package/docs/sql/SQL Generation.ipynb +2427 -0
  20. package/package.json +1 -1
  21. package/src/authentication/authentication-device.ts +1 -1
  22. package/src/authorization/authorization-keystore.ts +3 -2
  23. package/src/index.ts +10 -0
  24. package/src/jinaga-server.ts +3 -11
  25. package/src/postgres/postgres-store.ts +2 -2
  26. package/src/postgres/specification-sql.ts +18 -14
  27. package/src/util/fn.ts +0 -43
  28. package/test/postgres/postgresReadSpec.ts +51 -0
  29. package/dist/util/maps.d.ts +0 -0
  30. package/dist/util/maps.js +0 -2
  31. package/dist/util/maps.js.map +0 -1
  32. package/docs/registrations.sql +0 -22
  33. package/docs/sql/example.sql +0 -49
  34. package/docs/sql-generator.gv +0 -25
  35. package/src/util/maps.ts +0 -0
@@ -0,0 +1,2427 @@
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "code",
5
+ "execution_count": 57,
6
+ "metadata": {
7
+ "dotnet_interactive": {
8
+ "language": "csharp"
9
+ },
10
+ "polyglot_notebook": {
11
+ "kernelName": "csharp"
12
+ }
13
+ },
14
+ "outputs": [
15
+ {
16
+ "data": {
17
+ "text/html": [
18
+ "<div><div></div><div></div><div><strong>Installed Packages</strong><ul><li><span>Jinaga, 0.3.0</span></li><li><span>Jinaga.Graphviz, 0.3.0</span></li><li><span>Jinaga.UnitTest, 0.3.0</span></li></ul></div></div>"
19
+ ]
20
+ },
21
+ "metadata": {},
22
+ "output_type": "display_data"
23
+ }
24
+ ],
25
+ "source": [
26
+ "#r \"nuget: Jinaga\"\n",
27
+ "#r \"nuget: Jinaga.UnitTest\"\n",
28
+ "#r \"nuget: Jinaga.Graphviz\"\n",
29
+ "using Jinaga;\n",
30
+ "using Jinaga.UnitTest;\n",
31
+ "using Jinaga.Graphviz;\n",
32
+ "\n",
33
+ "var j = JinagaClient.Create();"
34
+ ]
35
+ },
36
+ {
37
+ "attachments": {},
38
+ "cell_type": "markdown",
39
+ "metadata": {},
40
+ "source": [
41
+ "# SQL Generation\n",
42
+ "\n",
43
+ "The Jinaga specification language describes a walk through a graph of facts.\n",
44
+ "To execute this walk, Jinaga needs to generate SQL queries.\n",
45
+ "We'll examine some incrementally more complicated queries to see how Jinaga generates SQL.\n",
46
+ "\n",
47
+ "## Specifications\n",
48
+ "\n",
49
+ "To understand how Jinaga generates SQL, we need to understand Jinaga specifications.\n",
50
+ "Let's start with a model of a school offering courses."
51
+ ]
52
+ },
53
+ {
54
+ "cell_type": "code",
55
+ "execution_count": 58,
56
+ "metadata": {
57
+ "dotnet_interactive": {
58
+ "language": "csharp"
59
+ },
60
+ "polyglot_notebook": {
61
+ "kernelName": "csharp"
62
+ }
63
+ },
64
+ "outputs": [
65
+ {
66
+ "data": {
67
+ "text/html": [
68
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
69
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
70
+ " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
71
+ "<!-- Generated by graphviz version 2.43.0 (0)\n",
72
+ " -->\n",
73
+ "<!-- Title: %3 Pages: 1 -->\n",
74
+ "<svg width=\"98pt\" height=\"131pt\"\n",
75
+ " viewBox=\"0.00 0.00 97.55 131.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
76
+ "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 127)\">\n",
77
+ "<title>%3</title>\n",
78
+ "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-127 93.55,-127 93.55,4 -4,4\"/>\n",
79
+ "<!-- Course -->\n",
80
+ "<g id=\"node1\" class=\"node\">\n",
81
+ "<title>Course</title>\n",
82
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"43.55\" cy=\"-18\" rx=\"43.59\" ry=\"18\"/>\n",
83
+ "<text text-anchor=\"middle\" x=\"43.55\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
84
+ "</g>\n",
85
+ "<!-- School -->\n",
86
+ "<g id=\"node2\" class=\"node\">\n",
87
+ "<title>School</title>\n",
88
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"43.55\" cy=\"-105\" rx=\"41.69\" ry=\"18\"/>\n",
89
+ "<text text-anchor=\"middle\" x=\"43.55\" y=\"-101.3\" font-family=\"Times,serif\" font-size=\"14.00\">School</text>\n",
90
+ "</g>\n",
91
+ "<!-- Course&#45;&gt;School -->\n",
92
+ "<g id=\"edge1\" class=\"edge\">\n",
93
+ "<title>Course&#45;&gt;School</title>\n",
94
+ "<path fill=\"none\" stroke=\"black\" d=\"M43.55,-36.2C43.55,-47.84 43.55,-63.45 43.55,-76.76\"/>\n",
95
+ "<polygon fill=\"black\" stroke=\"black\" points=\"40.05,-76.82 43.55,-86.82 47.05,-76.82 40.05,-76.82\"/>\n",
96
+ "<text text-anchor=\"middle\" x=\"66.55\" y=\"-57.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
97
+ "</g>\n",
98
+ "</g>\n",
99
+ "</svg>\n"
100
+ ]
101
+ },
102
+ "metadata": {},
103
+ "output_type": "display_data"
104
+ }
105
+ ],
106
+ "source": [
107
+ "[FactType(\"School\")]\n",
108
+ "public record School(string name) {}\n",
109
+ "\n",
110
+ "[FactType(\"Course\")]\n",
111
+ "public record Course(School school, string identifier) {}\n",
112
+ "\n",
113
+ "Renderer.RenderTypes(typeof(School), typeof(Course))"
114
+ ]
115
+ },
116
+ {
117
+ "attachments": {},
118
+ "cell_type": "markdown",
119
+ "metadata": {},
120
+ "source": [
121
+ "Suppose that the model contained the following facts."
122
+ ]
123
+ },
124
+ {
125
+ "cell_type": "code",
126
+ "execution_count": 59,
127
+ "metadata": {
128
+ "dotnet_interactive": {
129
+ "language": "csharp"
130
+ },
131
+ "polyglot_notebook": {
132
+ "kernelName": "csharp"
133
+ }
134
+ },
135
+ "outputs": [
136
+ {
137
+ "data": {
138
+ "text/html": [
139
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
140
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
141
+ " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
142
+ "<!-- Generated by graphviz version 2.43.0 (0)\n",
143
+ " -->\n",
144
+ "<!-- Title: %3 Pages: 1 -->\n",
145
+ "<svg width=\"750pt\" height=\"161pt\"\n",
146
+ " viewBox=\"0.00 0.00 750.00 161.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
147
+ "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 157)\">\n",
148
+ "<title>%3</title>\n",
149
+ "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-157 746,-157 746,4 -4,4\"/>\n",
150
+ "<!-- Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
151
+ "<g id=\"node1\" class=\"node\">\n",
152
+ "<title>Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
153
+ "<polygon fill=\"none\" stroke=\"black\" points=\"306,-128 306,-149 436,-149 436,-128 306,-128\"/>\n",
154
+ "<text text-anchor=\"start\" x=\"347\" y=\"-134.8\" font-family=\"Times,serif\" font-size=\"14.00\">School</text>\n",
155
+ "<polygon fill=\"none\" stroke=\"black\" points=\"306,-107 306,-128 352,-128 352,-107 306,-107\"/>\n",
156
+ "<text text-anchor=\"start\" x=\"309\" y=\"-113.8\" font-family=\"Times,serif\" font-size=\"14.00\">name</text>\n",
157
+ "<polygon fill=\"none\" stroke=\"black\" points=\"352,-107 352,-128 436,-128 436,-107 352,-107\"/>\n",
158
+ "<text text-anchor=\"start\" x=\"355\" y=\"-113.8\" font-family=\"Times,serif\" font-size=\"14.00\">LPS Frisco</text>\n",
159
+ "</g>\n",
160
+ "<!-- 3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg== -->\n",
161
+ "<g id=\"node2\" class=\"node\">\n",
162
+ "<title>3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==</title>\n",
163
+ "<polygon fill=\"none\" stroke=\"black\" points=\"9,-26 9,-47 163,-47 163,-26 9,-26\"/>\n",
164
+ "<text text-anchor=\"start\" x=\"60.5\" y=\"-32.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
165
+ "<polygon fill=\"none\" stroke=\"black\" points=\"9,-5 9,-26 81,-26 81,-5 9,-5\"/>\n",
166
+ "<text text-anchor=\"start\" x=\"12\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
167
+ "<polygon fill=\"none\" stroke=\"black\" points=\"81,-5 81,-26 163,-26 163,-5 81,-5\"/>\n",
168
+ "<text text-anchor=\"start\" x=\"84\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 101</text>\n",
169
+ "<polygon fill=\"none\" stroke=\"black\" points=\"8,-4 8,-48 164,-48 164,-4 8,-4\"/>\n",
170
+ "</g>\n",
171
+ "<!-- 3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
172
+ "<g id=\"edge1\" class=\"edge\">\n",
173
+ "<title>3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
174
+ "<path fill=\"none\" stroke=\"black\" d=\"M157.55,-52.11C198.7,-66.54 250.45,-84.7 292.68,-99.52\"/>\n",
175
+ "<polygon fill=\"black\" stroke=\"black\" points=\"291.78,-102.91 302.37,-102.92 294.09,-96.31 291.78,-102.91\"/>\n",
176
+ "<text text-anchor=\"middle\" x=\"269\" y=\"-73.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
177
+ "</g>\n",
178
+ "<!-- i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg== -->\n",
179
+ "<g id=\"node3\" class=\"node\">\n",
180
+ "<title>i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==</title>\n",
181
+ "<polygon fill=\"none\" stroke=\"black\" points=\"199,-26 199,-47 353,-47 353,-26 199,-26\"/>\n",
182
+ "<text text-anchor=\"start\" x=\"250.5\" y=\"-32.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
183
+ "<polygon fill=\"none\" stroke=\"black\" points=\"199,-5 199,-26 271,-26 271,-5 199,-5\"/>\n",
184
+ "<text text-anchor=\"start\" x=\"202\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
185
+ "<polygon fill=\"none\" stroke=\"black\" points=\"271,-5 271,-26 353,-26 353,-5 271,-5\"/>\n",
186
+ "<text text-anchor=\"start\" x=\"274\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 102</text>\n",
187
+ "<polygon fill=\"none\" stroke=\"black\" points=\"198,-4 198,-48 354,-48 354,-4 198,-4\"/>\n",
188
+ "</g>\n",
189
+ "<!-- i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
190
+ "<g id=\"edge2\" class=\"edge\">\n",
191
+ "<title>i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
192
+ "<path fill=\"none\" stroke=\"black\" d=\"M299.97,-52.23C312.4,-65.32 327.71,-81.43 340.92,-95.34\"/>\n",
193
+ "<polygon fill=\"black\" stroke=\"black\" points=\"338.59,-97.97 348.02,-102.81 343.67,-93.15 338.59,-97.97\"/>\n",
194
+ "<text text-anchor=\"middle\" x=\"353\" y=\"-73.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
195
+ "</g>\n",
196
+ "<!-- T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw== -->\n",
197
+ "<g id=\"node4\" class=\"node\">\n",
198
+ "<title>T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==</title>\n",
199
+ "<polygon fill=\"none\" stroke=\"black\" points=\"389,-26 389,-47 543,-47 543,-26 389,-26\"/>\n",
200
+ "<text text-anchor=\"start\" x=\"440.5\" y=\"-32.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
201
+ "<polygon fill=\"none\" stroke=\"black\" points=\"389,-5 389,-26 461,-26 461,-5 389,-5\"/>\n",
202
+ "<text text-anchor=\"start\" x=\"392\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
203
+ "<polygon fill=\"none\" stroke=\"black\" points=\"461,-5 461,-26 543,-26 543,-5 461,-5\"/>\n",
204
+ "<text text-anchor=\"start\" x=\"464\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 201</text>\n",
205
+ "<polygon fill=\"none\" stroke=\"black\" points=\"388,-4 388,-48 544,-48 544,-4 388,-4\"/>\n",
206
+ "</g>\n",
207
+ "<!-- T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
208
+ "<g id=\"edge3\" class=\"edge\">\n",
209
+ "<title>T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
210
+ "<path fill=\"none\" stroke=\"black\" d=\"M442.03,-52.23C429.6,-65.32 414.29,-81.43 401.08,-95.34\"/>\n",
211
+ "<polygon fill=\"black\" stroke=\"black\" points=\"398.33,-93.15 393.98,-102.81 403.41,-97.97 398.33,-93.15\"/>\n",
212
+ "<text text-anchor=\"middle\" x=\"448\" y=\"-73.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
213
+ "</g>\n",
214
+ "<!-- kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ== -->\n",
215
+ "<g id=\"node5\" class=\"node\">\n",
216
+ "<title>kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==</title>\n",
217
+ "<polygon fill=\"none\" stroke=\"black\" points=\"579,-26 579,-47 733,-47 733,-26 579,-26\"/>\n",
218
+ "<text text-anchor=\"start\" x=\"630.5\" y=\"-32.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
219
+ "<polygon fill=\"none\" stroke=\"black\" points=\"579,-5 579,-26 651,-26 651,-5 579,-5\"/>\n",
220
+ "<text text-anchor=\"start\" x=\"582\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
221
+ "<polygon fill=\"none\" stroke=\"black\" points=\"651,-5 651,-26 733,-26 733,-5 651,-5\"/>\n",
222
+ "<text text-anchor=\"start\" x=\"654\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 301</text>\n",
223
+ "<polygon fill=\"none\" stroke=\"black\" points=\"578,-4 578,-48 734,-48 734,-4 578,-4\"/>\n",
224
+ "</g>\n",
225
+ "<!-- kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
226
+ "<g id=\"edge4\" class=\"edge\">\n",
227
+ "<title>kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
228
+ "<path fill=\"none\" stroke=\"black\" d=\"M584.45,-52.11C543.3,-66.54 491.55,-84.7 449.32,-99.52\"/>\n",
229
+ "<polygon fill=\"black\" stroke=\"black\" points=\"447.91,-96.31 439.63,-102.92 450.22,-102.91 447.91,-96.31\"/>\n",
230
+ "<text text-anchor=\"middle\" x=\"553\" y=\"-73.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
231
+ "</g>\n",
232
+ "</g>\n",
233
+ "</svg>\n"
234
+ ]
235
+ },
236
+ "metadata": {},
237
+ "output_type": "display_data"
238
+ }
239
+ ],
240
+ "source": [
241
+ "var lps = await j.Fact(new School(\"LPS Frisco\"));\n",
242
+ "var algebra = await j.Fact(new Course(lps, \"MATH 101\"));\n",
243
+ "var geometry = await j.Fact(new Course(lps, \"MATH 102\"));\n",
244
+ "var trigonometry = await j.Fact(new Course(lps, \"MATH 201\"));\n",
245
+ "var calculus = await j.Fact(new Course(lps, \"MATH 301\"));\n",
246
+ "\n",
247
+ "Renderer.RenderFacts(algebra, geometry, trigonometry, calculus)"
248
+ ]
249
+ },
250
+ {
251
+ "attachments": {},
252
+ "cell_type": "markdown",
253
+ "metadata": {},
254
+ "source": [
255
+ "Write a query to return the catalog of courses offered by the school."
256
+ ]
257
+ },
258
+ {
259
+ "cell_type": "code",
260
+ "execution_count": 60,
261
+ "metadata": {
262
+ "dotnet_interactive": {
263
+ "language": "csharp"
264
+ },
265
+ "polyglot_notebook": {
266
+ "kernelName": "csharp"
267
+ }
268
+ },
269
+ "outputs": [
270
+ {
271
+ "data": {
272
+ "text/html": [
273
+ "<table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Course { school = School { name = LPS Frisco }, identifier = MATH 101 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>school</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>School { name = LPS Frisco }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>LPS Frisco</td></tr></tbody></table></div></details></td></tr><tr><td>identifier</td><td>MATH 101</td></tr></tbody></table></div></details></td></tr><tr><td>1</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Course { school = School { name = LPS Frisco }, identifier = MATH 102 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>school</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>School { name = LPS Frisco }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>LPS Frisco</td></tr></tbody></table></div></details></td></tr><tr><td>identifier</td><td>MATH 102</td></tr></tbody></table></div></details></td></tr><tr><td>2</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Course { school = School { name = LPS Frisco }, identifier = MATH 201 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>school</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>School { name = LPS Frisco }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>LPS Frisco</td></tr></tbody></table></div></details></td></tr><tr><td>identifier</td><td>MATH 201</td></tr></tbody></table></div></details></td></tr><tr><td>3</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Course { school = School { name = LPS Frisco }, identifier = MATH 301 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>school</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>School { name = LPS Frisco }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>LPS Frisco</td></tr></tbody></table></div></details></td></tr><tr><td>identifier</td><td>MATH 301</td></tr></tbody></table></div></details></td></tr></tbody></table><style>\r\n",
274
+ ".dni-code-hint {\r\n",
275
+ " font-style: italic;\r\n",
276
+ " overflow: hidden;\r\n",
277
+ " white-space: nowrap;\r\n",
278
+ "}\r\n",
279
+ ".dni-treeview {\r\n",
280
+ " white-space: nowrap;\r\n",
281
+ "}\r\n",
282
+ ".dni-treeview td {\r\n",
283
+ " vertical-align: top;\r\n",
284
+ " text-align: start;\r\n",
285
+ "}\r\n",
286
+ "details.dni-treeview {\r\n",
287
+ " padding-left: 1em;\r\n",
288
+ "}\r\n",
289
+ "table td {\r\n",
290
+ " text-align: start;\r\n",
291
+ "}\r\n",
292
+ "table tr { \r\n",
293
+ " vertical-align: top; \r\n",
294
+ " margin: 0em 0px;\r\n",
295
+ "}\r\n",
296
+ "table tr td pre \r\n",
297
+ "{ \r\n",
298
+ " vertical-align: top !important; \r\n",
299
+ " margin: 0em 0px !important;\r\n",
300
+ "} \r\n",
301
+ "table th {\r\n",
302
+ " text-align: start;\r\n",
303
+ "}\r\n",
304
+ "</style>"
305
+ ]
306
+ },
307
+ "metadata": {},
308
+ "output_type": "display_data"
309
+ }
310
+ ],
311
+ "source": [
312
+ "var coursesInSchool = Given<School>.Match((school, facts) =>\n",
313
+ " from course in facts.OfType<Course>()\n",
314
+ " where course.school == school\n",
315
+ " select course\n",
316
+ ");\n",
317
+ "\n",
318
+ "var catalog = await j.Query(lps, coursesInSchool);\n",
319
+ "catalog"
320
+ ]
321
+ },
322
+ {
323
+ "attachments": {},
324
+ "cell_type": "markdown",
325
+ "metadata": {},
326
+ "source": [
327
+ "This query can be expressed using the Jinaga specification language."
328
+ ]
329
+ },
330
+ {
331
+ "cell_type": "code",
332
+ "execution_count": 61,
333
+ "metadata": {
334
+ "dotnet_interactive": {
335
+ "language": "csharp"
336
+ },
337
+ "polyglot_notebook": {
338
+ "kernelName": "csharp"
339
+ }
340
+ },
341
+ "outputs": [
342
+ {
343
+ "data": {
344
+ "text/plain": [
345
+ "(school: School) {\n",
346
+ " course: Course [\n",
347
+ " course->school: School = school\n",
348
+ " ]\n",
349
+ "} => course\n"
350
+ ]
351
+ },
352
+ "metadata": {},
353
+ "output_type": "display_data"
354
+ }
355
+ ],
356
+ "source": [
357
+ "coursesInSchool.ToDescriptiveString()"
358
+ ]
359
+ },
360
+ {
361
+ "attachments": {},
362
+ "cell_type": "markdown",
363
+ "metadata": {},
364
+ "source": [
365
+ "Within the Jinaga runtime, this specification is represented as a data structure.\n",
366
+ "A specification has three parts:\n",
367
+ "- Given\n",
368
+ "- Matches\n",
369
+ "- Projection\n",
370
+ "\n",
371
+ "The `given` is a collection of labels that are placeholders for known facts.\n",
372
+ "In this particular specification, the given is the label `school` of type `School`.\n",
373
+ "\n",
374
+ "```\n",
375
+ "(school: School)\n",
376
+ "```\n",
377
+ "\n",
378
+ "The `matches` collection contains a set of labels for unknown facts, and their corresponding conditions.\n",
379
+ "This specification has a single match, which has the label `course` of type `Course`.\n",
380
+ "The condition is that the course `school` is the same as the given `school`.\n",
381
+ "\n",
382
+ "```\n",
383
+ "course: Course [\n",
384
+ " course->school: School = school\n",
385
+ "]\n",
386
+ "```\n",
387
+ "\n",
388
+ "The collection of unknowns declared in the matches form a tuple.\n",
389
+ "That is to say that when the specification is run, the result is the set of all tuples that simultaneously satisfy the conditions.\n",
390
+ "In this case, there is only one unknown.\n",
391
+ "Each tuple therefore contains a single fact.\n",
392
+ "\n",
393
+ "```javascript\n",
394
+ "[\n",
395
+ " { course: algebra },\n",
396
+ " { course: geometry },\n",
397
+ " { course: trigonometry },\n",
398
+ " { course: calculus }\n",
399
+ "]\n",
400
+ "```\n",
401
+ "\n",
402
+ "The `projection` is a function that takes a tuple and returns a value.\n",
403
+ "In this case, the projection is a function that returns the `course` fact.\n",
404
+ "\n",
405
+ "```\n",
406
+ "=> course\n",
407
+ "```\n",
408
+ "\n",
409
+ "The result is therefore a set of facts.\n",
410
+ "\n",
411
+ "```javascript\n",
412
+ "[\n",
413
+ " algebra,\n",
414
+ " geometry,\n",
415
+ " trigonometry,\n",
416
+ " calculus\n",
417
+ "]\n",
418
+ "```"
419
+ ]
420
+ },
421
+ {
422
+ "attachments": {},
423
+ "cell_type": "markdown",
424
+ "metadata": {},
425
+ "source": [
426
+ "### Feed\n",
427
+ "\n",
428
+ "Jinaga runs two different kinds of SQL queries to satisfy a specification.\n",
429
+ "The first is a feed query.\n",
430
+ "A feed query returns the tuples that satisfy the specification.\n",
431
+ "It does not run the projection function.\n",
432
+ "\n",
433
+ "To run the feed query, Jinaga first builds a Query Description.\n",
434
+ "The Query Description has five parts:\n",
435
+ "- Inputs\n",
436
+ "- Parameters\n",
437
+ "- Outputs\n",
438
+ "- Edges\n",
439
+ "- Not exists conditions\n",
440
+ "\n",
441
+ "The `inputs` are the given facts.\n",
442
+ "They represent the `given` labels in the specification.\n",
443
+ "They also record a specific fact instance for each of those labels.\n",
444
+ "In the example above, the input is the `school` fact \"LPS Frisco\".\n",
445
+ "\n",
446
+ "For each input, Jinaga allocates a join to the fact table.\n",
447
+ "It doesn't turn these into SQL immediately.\n",
448
+ "Instead, it simply reserves an index.\n",
449
+ "\n",
450
+ "It also allocates two parameters.\n",
451
+ "The first is for the fact type, and the second is for the fact hash.\n",
452
+ "It stores the parameters in a parallel collection called `parameters`, and keeps the indices in the `inputs` collection.\n",
453
+ "\n",
454
+ "For the example query, the resulting `inputs` and `parameters` arrays are as follows.\n",
455
+ "The number 1001 would be the ID of the fact type `School`.\n",
456
+ "The string \"XYZ123\" would be the hash of the \"LPS Frisco\" fact instance.\n",
457
+ "\n",
458
+ "```javascript\n",
459
+ "inputs: [\n",
460
+ " {\n",
461
+ " factIndex: 1, // school\n",
462
+ " factTypeParameter: 1,\n",
463
+ " factHashParameter: 2\n",
464
+ " }\n",
465
+ "]\n",
466
+ "\n",
467
+ "parameters: [\n",
468
+ " 1001, // School\n",
469
+ " \"XYZ123\" // Hash of LPS Frisco\n",
470
+ "]\n",
471
+ "```\n",
472
+ "\n",
473
+ "Next Jinaga builds the `edges` collection.\n",
474
+ "An edge description represents a relationship between two labels.\n",
475
+ "In the example above, there is a single edge.\n",
476
+ "It connects the unknown `course` to the given `school`.\n",
477
+ "\n",
478
+ "To build the edges, Jinaga walks the join conditions within the matches.\n",
479
+ "For each step, it allocates a join to both the fact table and the edge table.\n",
480
+ "In the above example, it allocates fact index 2 to the `course` label and edge index 1 to the step from course to school.\n",
481
+ "\n",
482
+ "It also allocates one parameter for each edge join.\n",
483
+ "That parameter is for the role.\n",
484
+ "\n",
485
+ "For the example query, the resulting `edges` and `parameters` arrays are as follows.\n",
486
+ "The number 2001 would be the ID of the role defined by fact type `Course` named `school`.\n",
487
+ "\n",
488
+ "```javascript\n",
489
+ "edges: [\n",
490
+ " {\n",
491
+ " edgeIndex: 1, // course -> school\n",
492
+ " predecessorIndex: 1, // school\n",
493
+ " successorIndex: 2, // course\n",
494
+ " roleParameter: 3\n",
495
+ " }\n",
496
+ "]\n",
497
+ "\n",
498
+ "parameters: [\n",
499
+ " 1001, // School\n",
500
+ " \"XYZ123\", // Hash of LPS Frisco\n",
501
+ " 2001 // Course -> school\n",
502
+ "]\n",
503
+ "```\n",
504
+ "\n",
505
+ "The `outputs` are the unknowns.\n",
506
+ "For each of these, Jinaga records the index of the fact table join.\n",
507
+ "\n",
508
+ "For the example query, the resulting `outputs` array is as follows.\n",
509
+ "The fact index 2 was allocated to the `course` label.\n",
510
+ "\n",
511
+ "```javascript\n",
512
+ "outputs: [\n",
513
+ " {\n",
514
+ " factIndex: 2 // course\n",
515
+ " }\n",
516
+ "]\n",
517
+ "```\n",
518
+ "\n",
519
+ "We'll examine the `not exists` conditions later.\n",
520
+ "They do not apply to this example."
521
+ ]
522
+ },
523
+ {
524
+ "attachments": {},
525
+ "cell_type": "markdown",
526
+ "metadata": {},
527
+ "source": [
528
+ "### Feed SQL\n",
529
+ "\n",
530
+ "Now that Jinaga has built the Query Description, it can turn it into SQL.\n",
531
+ "The `SELECT` clause is based on the `outputs` collection.\n",
532
+ "For each output, Jinaga adds a column for the fact hash.\n",
533
+ "\n",
534
+ "It also selects a sorted array of fact IDs as the bookmark.\n",
535
+ "The bookmark is a decreasing list of the fact IDs in the tuple.\n",
536
+ "The bookmark will also appear at the end of the generated SQL for paging and sorting.\n",
537
+ "\n",
538
+ "```sql\n",
539
+ "SELECT\n",
540
+ " f2.hash as hash2,\n",
541
+ " sort(array[f2.fact_id], 'desc') as bookmark\n",
542
+ "```\n",
543
+ "\n",
544
+ "Then it generates the `FROM` clause using the first fact index.\n",
545
+ "\n",
546
+ "```sql\n",
547
+ "FROM fact f1 -- school\n",
548
+ "```\n",
549
+ "\n",
550
+ "Then it generates the `WHERE` clause.\n",
551
+ "To do so, it iterates through the `edges` collection.\n",
552
+ "It makes sure that at least one of the fact indexes -- the predecessor or successor -- has already been written.\n",
553
+ "Then it writes out the edge join and the remaining fact join.\n",
554
+ "Here it uses the edge role parameter.\n",
555
+ "\n",
556
+ "```sql\n",
557
+ "JOIN edge e1 -- course -> school\n",
558
+ " ON e1.predecessor_fact_id = f1.fact_id\n",
559
+ " AND e1.role_id = $3\n",
560
+ "JOIN fact f2 -- course\n",
561
+ " ON f2.fact_id = e1.successor_fact_id\n",
562
+ "```\n",
563
+ "\n",
564
+ "Next it generates the `WHERE` clause for the `inputs`.\n",
565
+ "It uses the fact type and fact hash parameters.\n",
566
+ "It includes the bookmark in the where clause to continue reading the feed from the point that the previous page ended.\n",
567
+ "\n",
568
+ "```sql\n",
569
+ "WHERE f1.fact_type_id = $1 AND f1.hash = $2\n",
570
+ " AND sort(array[f2.fact_id], 'desc') > $4\n",
571
+ "```\n",
572
+ "\n",
573
+ "Finally, it sorts and pages the results using the bookmark.\n",
574
+ "\n",
575
+ "```sql\n",
576
+ "ORDER BY bookmark ASC LIMIT $5\n",
577
+ "```\n",
578
+ "\n",
579
+ "The resulting SQL is as follows:\n",
580
+ "\n",
581
+ "```sql\n",
582
+ "SELECT\n",
583
+ " f2.hash as hash2,\n",
584
+ " sort(array[f2.fact_id], 'desc') as bookmark\n",
585
+ "FROM fact f1 -- school\n",
586
+ "JOIN edge e1 -- course -> school\n",
587
+ " ON e1.predecessor_fact_id = f1.fact_id\n",
588
+ " AND e1.role_id = $3\n",
589
+ "JOIN fact f2 -- course\n",
590
+ " ON f2.fact_id = e1.successor_fact_id\n",
591
+ "WHERE f1.fact_type_id = $1 AND f1.hash = $2\n",
592
+ " AND sort(array[f2.fact_id], 'desc') > $4\n",
593
+ "ORDER BY bookmark ASC LIMIT $5\n",
594
+ "```"
595
+ ]
596
+ },
597
+ {
598
+ "attachments": {},
599
+ "cell_type": "markdown",
600
+ "metadata": {},
601
+ "source": [
602
+ "### Result SQL\n",
603
+ "\n",
604
+ "The Feed SQL query is intended to fetch tuples a page at a time.\n",
605
+ "It is executed on the server.\n",
606
+ "The Result SQL query, on the other hand, is intended to fetch the results of the projection function.\n",
607
+ "It is executed on the client.\n",
608
+ "The one exception is the `/read` endpoint, which runs the projection on the server for human consumption.\n",
609
+ "\n",
610
+ "The Result SQL query is more complex than the Feed SQL query.\n",
611
+ "It can contain nested existential queries to any depth.\n",
612
+ "This simple example does not contain any existential queries, so the differences are less significant.\n",
613
+ "\n",
614
+ "The `inputs` and `outputs` collections differ from the Feed SQL generator in that they also contain the labels.\n",
615
+ "For the example query, the `inputs` and `outputs` collections are as follows.\n",
616
+ "\n",
617
+ "```javascript\n",
618
+ "inputs: [\n",
619
+ " {\n",
620
+ " label: \"school\",\n",
621
+ " factIndex: 1,\n",
622
+ " factTypeParameter: 1,\n",
623
+ " factHashParameter: 2\n",
624
+ " }\n",
625
+ "]\n",
626
+ "\n",
627
+ "outputs: [\n",
628
+ " {\n",
629
+ " label: \"course\",\n",
630
+ " factIndex: 2\n",
631
+ " }\n",
632
+ "]\n",
633
+ "```\n",
634
+ "\n",
635
+ "This extra information helps the result composer to execute the projection function.\n",
636
+ "The composer will need the field values of the facts, which are in the `data` column.\n",
637
+ "It will also need the fact IDs to link child projections to their parents.\n",
638
+ "It therefore adds those columns to the `SELECT` clause.\n",
639
+ "\n",
640
+ "```sql\n",
641
+ "SELECT\n",
642
+ " f2.hash as hash2, f2.fact_id as id2, f2.data as data2\n",
643
+ "```\n",
644
+ "\n",
645
+ "Then it generates the `FROM`, `JOIN`, and `WHERE` clauses as before.\n",
646
+ "The Result SQL, however, does not include the bookmark in the `WHERE` clause.\n",
647
+ "\n",
648
+ "```sql\n",
649
+ "FROM fact f1 -- school\n",
650
+ "JOIN edge e1 -- course -> school\n",
651
+ " ON e1.predecessor_fact_id = f1.fact_id\n",
652
+ " AND e1.role_id = $3\n",
653
+ "JOIN fact f2 -- course\n",
654
+ " ON f2.fact_id = e1.successor_fact_id\n",
655
+ "WHERE f1.fact_type_id = $1 AND f1.hash = $2\n",
656
+ "```\n",
657
+ "\n",
658
+ "The Result SQL is not sorted by bookmark.\n",
659
+ "Instead, it is sorted by the fact ID.\n",
660
+ "\n",
661
+ "```sql\n",
662
+ "ORDER BY f2.fact_id ASC\n",
663
+ "```\n",
664
+ "\n",
665
+ "The resulting SQL is as follows:\n",
666
+ "\n",
667
+ "```sql\n",
668
+ "SELECT\n",
669
+ " f2.hash as hash2, f2.fact_id as id2, f2.data as data2\n",
670
+ "FROM fact f1\n",
671
+ "JOIN edge e1\n",
672
+ " ON e1.predecessor_fact_id = f1.fact_id\n",
673
+ " AND e1.role_id = $3\n",
674
+ "JOIN fact f2\n",
675
+ " ON f2.fact_id = e1.successor_fact_id\n",
676
+ "WHERE f1.fact_type_id = $1 AND f1.hash = $2\n",
677
+ "ORDER BY f2.fact_id ASC\n",
678
+ "```"
679
+ ]
680
+ },
681
+ {
682
+ "attachments": {},
683
+ "cell_type": "markdown",
684
+ "metadata": {},
685
+ "source": [
686
+ "## Existential Conditions\n",
687
+ "\n",
688
+ "Any realistic specification in a Jinaga application will likely contain an existential condition, often a negative one.\n",
689
+ "A negative existential condition discards results based on certain successors.\n",
690
+ "For example, a specification might discard courses that have been deleted."
691
+ ]
692
+ },
693
+ {
694
+ "cell_type": "code",
695
+ "execution_count": 62,
696
+ "metadata": {
697
+ "dotnet_interactive": {
698
+ "language": "csharp"
699
+ },
700
+ "polyglot_notebook": {
701
+ "kernelName": "csharp"
702
+ }
703
+ },
704
+ "outputs": [
705
+ {
706
+ "data": {
707
+ "text/html": [
708
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
709
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
710
+ " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
711
+ "<!-- Generated by graphviz version 2.43.0 (0)\n",
712
+ " -->\n",
713
+ "<!-- Title: %3 Pages: 1 -->\n",
714
+ "<svg width=\"173pt\" height=\"218pt\"\n",
715
+ " viewBox=\"0.00 0.00 173.09 218.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
716
+ "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 214)\">\n",
717
+ "<title>%3</title>\n",
718
+ "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-214 169.09,-214 169.09,4 -4,4\"/>\n",
719
+ "<!-- Course.Deleted -->\n",
720
+ "<g id=\"node1\" class=\"node\">\n",
721
+ "<title>Course.Deleted</title>\n",
722
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"82.54\" cy=\"-18\" rx=\"82.59\" ry=\"18\"/>\n",
723
+ "<text text-anchor=\"middle\" x=\"82.54\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Deleted</text>\n",
724
+ "</g>\n",
725
+ "<!-- Course -->\n",
726
+ "<g id=\"node2\" class=\"node\">\n",
727
+ "<title>Course</title>\n",
728
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"82.54\" cy=\"-105\" rx=\"43.59\" ry=\"18\"/>\n",
729
+ "<text text-anchor=\"middle\" x=\"82.54\" y=\"-101.3\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
730
+ "</g>\n",
731
+ "<!-- Course.Deleted&#45;&gt;Course -->\n",
732
+ "<g id=\"edge1\" class=\"edge\">\n",
733
+ "<title>Course.Deleted&#45;&gt;Course</title>\n",
734
+ "<path fill=\"none\" stroke=\"black\" d=\"M82.54,-36.2C82.54,-47.84 82.54,-63.45 82.54,-76.76\"/>\n",
735
+ "<polygon fill=\"black\" stroke=\"black\" points=\"79.04,-76.82 82.54,-86.82 86.04,-76.82 79.04,-76.82\"/>\n",
736
+ "<text text-anchor=\"middle\" x=\"106.54\" y=\"-57.8\" font-family=\"Times,serif\" font-size=\"14.00\">course</text>\n",
737
+ "</g>\n",
738
+ "<!-- School -->\n",
739
+ "<g id=\"node3\" class=\"node\">\n",
740
+ "<title>School</title>\n",
741
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"82.54\" cy=\"-192\" rx=\"41.69\" ry=\"18\"/>\n",
742
+ "<text text-anchor=\"middle\" x=\"82.54\" y=\"-188.3\" font-family=\"Times,serif\" font-size=\"14.00\">School</text>\n",
743
+ "</g>\n",
744
+ "<!-- Course&#45;&gt;School -->\n",
745
+ "<g id=\"edge2\" class=\"edge\">\n",
746
+ "<title>Course&#45;&gt;School</title>\n",
747
+ "<path fill=\"none\" stroke=\"black\" d=\"M82.54,-123.2C82.54,-134.84 82.54,-150.45 82.54,-163.76\"/>\n",
748
+ "<polygon fill=\"black\" stroke=\"black\" points=\"79.04,-163.82 82.54,-173.82 86.04,-163.82 79.04,-163.82\"/>\n",
749
+ "<text text-anchor=\"middle\" x=\"105.54\" y=\"-144.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
750
+ "</g>\n",
751
+ "</g>\n",
752
+ "</svg>\n"
753
+ ]
754
+ },
755
+ "metadata": {},
756
+ "output_type": "display_data"
757
+ }
758
+ ],
759
+ "source": [
760
+ "[FactType(\"Course.Deleted\")]\n",
761
+ "public record CourseDeleted(Course course, DateTime deletedAt) {}\n",
762
+ "\n",
763
+ "Renderer.RenderTypes(typeof(CourseDeleted))"
764
+ ]
765
+ },
766
+ {
767
+ "attachments": {},
768
+ "cell_type": "markdown",
769
+ "metadata": {},
770
+ "source": [
771
+ "If the school wishes to no longer offer Trigonometry, then it would represent that with a course deletion fact."
772
+ ]
773
+ },
774
+ {
775
+ "cell_type": "code",
776
+ "execution_count": 63,
777
+ "metadata": {
778
+ "dotnet_interactive": {
779
+ "language": "csharp"
780
+ },
781
+ "polyglot_notebook": {
782
+ "kernelName": "csharp"
783
+ }
784
+ },
785
+ "outputs": [
786
+ {
787
+ "data": {
788
+ "text/html": [
789
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
790
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
791
+ " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
792
+ "<!-- Generated by graphviz version 2.43.0 (0)\n",
793
+ " -->\n",
794
+ "<!-- Title: %3 Pages: 1 -->\n",
795
+ "<svg width=\"748pt\" height=\"264pt\"\n",
796
+ " viewBox=\"0.00 0.00 748.00 264.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
797
+ "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 260)\">\n",
798
+ "<title>%3</title>\n",
799
+ "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-260 744,-260 744,4 -4,4\"/>\n",
800
+ "<!-- Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
801
+ "<g id=\"node1\" class=\"node\">\n",
802
+ "<title>Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
803
+ "<polygon fill=\"none\" stroke=\"black\" points=\"275,-231 275,-252 405,-252 405,-231 275,-231\"/>\n",
804
+ "<text text-anchor=\"start\" x=\"316\" y=\"-237.8\" font-family=\"Times,serif\" font-size=\"14.00\">School</text>\n",
805
+ "<polygon fill=\"none\" stroke=\"black\" points=\"275,-210 275,-231 321,-231 321,-210 275,-210\"/>\n",
806
+ "<text text-anchor=\"start\" x=\"278\" y=\"-216.8\" font-family=\"Times,serif\" font-size=\"14.00\">name</text>\n",
807
+ "<polygon fill=\"none\" stroke=\"black\" points=\"321,-210 321,-231 405,-231 405,-210 321,-210\"/>\n",
808
+ "<text text-anchor=\"start\" x=\"324\" y=\"-216.8\" font-family=\"Times,serif\" font-size=\"14.00\">LPS Frisco</text>\n",
809
+ "</g>\n",
810
+ "<!-- 3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg== -->\n",
811
+ "<g id=\"node2\" class=\"node\">\n",
812
+ "<title>3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==</title>\n",
813
+ "<polygon fill=\"none\" stroke=\"black\" points=\"9,-129 9,-150 163,-150 163,-129 9,-129\"/>\n",
814
+ "<text text-anchor=\"start\" x=\"60.5\" y=\"-135.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
815
+ "<polygon fill=\"none\" stroke=\"black\" points=\"9,-108 9,-129 81,-129 81,-108 9,-108\"/>\n",
816
+ "<text text-anchor=\"start\" x=\"12\" y=\"-114.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
817
+ "<polygon fill=\"none\" stroke=\"black\" points=\"81,-108 81,-129 163,-129 163,-108 81,-108\"/>\n",
818
+ "<text text-anchor=\"start\" x=\"84\" y=\"-114.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 101</text>\n",
819
+ "<polygon fill=\"none\" stroke=\"black\" points=\"8,-107 8,-151 164,-151 164,-107 8,-107\"/>\n",
820
+ "</g>\n",
821
+ "<!-- 3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
822
+ "<g id=\"edge1\" class=\"edge\">\n",
823
+ "<title>3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
824
+ "<path fill=\"none\" stroke=\"black\" d=\"M149.77,-155.11C186.21,-169.45 231.98,-187.47 269.49,-202.24\"/>\n",
825
+ "<polygon fill=\"black\" stroke=\"black\" points=\"268.25,-205.51 278.84,-205.92 270.81,-199 268.25,-205.51\"/>\n",
826
+ "<text text-anchor=\"middle\" x=\"252\" y=\"-176.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
827
+ "</g>\n",
828
+ "<!-- i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg== -->\n",
829
+ "<g id=\"node3\" class=\"node\">\n",
830
+ "<title>i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==</title>\n",
831
+ "<polygon fill=\"none\" stroke=\"black\" points=\"199,-129 199,-150 353,-150 353,-129 199,-129\"/>\n",
832
+ "<text text-anchor=\"start\" x=\"250.5\" y=\"-135.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
833
+ "<polygon fill=\"none\" stroke=\"black\" points=\"199,-108 199,-129 271,-129 271,-108 199,-108\"/>\n",
834
+ "<text text-anchor=\"start\" x=\"202\" y=\"-114.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
835
+ "<polygon fill=\"none\" stroke=\"black\" points=\"271,-108 271,-129 353,-129 353,-108 271,-108\"/>\n",
836
+ "<text text-anchor=\"start\" x=\"274\" y=\"-114.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 102</text>\n",
837
+ "<polygon fill=\"none\" stroke=\"black\" points=\"198,-107 198,-151 354,-151 354,-107 198,-107\"/>\n",
838
+ "</g>\n",
839
+ "<!-- i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
840
+ "<g id=\"edge2\" class=\"edge\">\n",
841
+ "<title>i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
842
+ "<path fill=\"none\" stroke=\"black\" d=\"M292.15,-155.23C300.29,-167.95 310.26,-183.54 318.99,-197.18\"/>\n",
843
+ "<polygon fill=\"black\" stroke=\"black\" points=\"316.18,-199.27 324.52,-205.81 322.07,-195.5 316.18,-199.27\"/>\n",
844
+ "<text text-anchor=\"middle\" x=\"335\" y=\"-176.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
845
+ "</g>\n",
846
+ "<!-- T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw== -->\n",
847
+ "<g id=\"node4\" class=\"node\">\n",
848
+ "<title>T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==</title>\n",
849
+ "<polygon fill=\"none\" stroke=\"black\" points=\"388,-129 388,-150 542,-150 542,-129 388,-129\"/>\n",
850
+ "<text text-anchor=\"start\" x=\"439.5\" y=\"-135.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
851
+ "<polygon fill=\"none\" stroke=\"black\" points=\"388,-108 388,-129 460,-129 460,-108 388,-108\"/>\n",
852
+ "<text text-anchor=\"start\" x=\"391\" y=\"-114.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
853
+ "<polygon fill=\"none\" stroke=\"black\" points=\"460,-108 460,-129 542,-129 542,-108 460,-108\"/>\n",
854
+ "<text text-anchor=\"start\" x=\"463\" y=\"-114.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 201</text>\n",
855
+ "</g>\n",
856
+ "<!-- T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
857
+ "<g id=\"edge3\" class=\"edge\">\n",
858
+ "<title>T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
859
+ "<path fill=\"none\" stroke=\"black\" d=\"M434.74,-154.21C417.82,-167.74 396.57,-184.75 378.48,-199.22\"/>\n",
860
+ "<polygon fill=\"black\" stroke=\"black\" points=\"376.04,-196.69 370.41,-205.67 380.41,-202.16 376.04,-196.69\"/>\n",
861
+ "<text text-anchor=\"middle\" x=\"433\" y=\"-176.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
862
+ "</g>\n",
863
+ "<!-- nJcwOXaAmB3e5dtd2UrhA4Xwoh6DA0c9ksEkVoAoauV4N7yevNFjb0xk+6KzmWpmZNBj+XaV6zKAok8kHWGrSw== -->\n",
864
+ "<g id=\"node5\" class=\"node\">\n",
865
+ "<title>nJcwOXaAmB3e5dtd2UrhA4Xwoh6DA0c9ksEkVoAoauV4N7yevNFjb0xk+6KzmWpmZNBj+XaV6zKAok8kHWGrSw==</title>\n",
866
+ "<polygon fill=\"none\" stroke=\"black\" points=\"327,-26 327,-47 604,-47 604,-26 327,-26\"/>\n",
867
+ "<text text-anchor=\"start\" x=\"410\" y=\"-32.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Deleted</text>\n",
868
+ "<polygon fill=\"none\" stroke=\"black\" points=\"327,-5 327,-26 403,-26 403,-5 327,-5\"/>\n",
869
+ "<text text-anchor=\"start\" x=\"330\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">deletedAt</text>\n",
870
+ "<polygon fill=\"none\" stroke=\"black\" points=\"403,-5 403,-26 604,-26 604,-5 403,-5\"/>\n",
871
+ "<text text-anchor=\"start\" x=\"406\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">2023&#45;04&#45;02T20:02:06.321Z</text>\n",
872
+ "<polygon fill=\"none\" stroke=\"black\" points=\"325.5,-4 325.5,-48 604.5,-48 604.5,-4 325.5,-4\"/>\n",
873
+ "</g>\n",
874
+ "<!-- nJcwOXaAmB3e5dtd2UrhA4Xwoh6DA0c9ksEkVoAoauV4N7yevNFjb0xk+6KzmWpmZNBj+XaV6zKAok8kHWGrSw==&#45;&gt;T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw== -->\n",
875
+ "<g id=\"edge4\" class=\"edge\">\n",
876
+ "<title>nJcwOXaAmB3e5dtd2UrhA4Xwoh6DA0c9ksEkVoAoauV4N7yevNFjb0xk+6KzmWpmZNBj+XaV6zKAok8kHWGrSw==&#45;&gt;T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==</title>\n",
877
+ "<path fill=\"none\" stroke=\"black\" d=\"M465,-52.23C465,-64.67 465,-79.88 465,-93.4\"/>\n",
878
+ "<polygon fill=\"black\" stroke=\"black\" points=\"461.5,-93.79 465,-103.79 468.5,-93.79 461.5,-93.79\"/>\n",
879
+ "<text text-anchor=\"middle\" x=\"489\" y=\"-73.8\" font-family=\"Times,serif\" font-size=\"14.00\">course</text>\n",
880
+ "</g>\n",
881
+ "<!-- kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ== -->\n",
882
+ "<g id=\"node6\" class=\"node\">\n",
883
+ "<title>kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==</title>\n",
884
+ "<polygon fill=\"none\" stroke=\"black\" points=\"577,-129 577,-150 731,-150 731,-129 577,-129\"/>\n",
885
+ "<text text-anchor=\"start\" x=\"628.5\" y=\"-135.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
886
+ "<polygon fill=\"none\" stroke=\"black\" points=\"577,-108 577,-129 649,-129 649,-108 577,-108\"/>\n",
887
+ "<text text-anchor=\"start\" x=\"580\" y=\"-114.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
888
+ "<polygon fill=\"none\" stroke=\"black\" points=\"649,-108 649,-129 731,-129 731,-108 649,-108\"/>\n",
889
+ "<text text-anchor=\"start\" x=\"652\" y=\"-114.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 301</text>\n",
890
+ "<polygon fill=\"none\" stroke=\"black\" points=\"576,-107 576,-151 732,-151 732,-107 576,-107\"/>\n",
891
+ "</g>\n",
892
+ "<!-- kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
893
+ "<g id=\"edge5\" class=\"edge\">\n",
894
+ "<title>kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
895
+ "<path fill=\"none\" stroke=\"black\" d=\"M575.17,-155.11C528.69,-169.91 469.93,-188.62 422.78,-203.64\"/>\n",
896
+ "<polygon fill=\"black\" stroke=\"black\" points=\"421.67,-200.32 413.2,-206.69 423.79,-206.99 421.67,-200.32\"/>\n",
897
+ "<text text-anchor=\"middle\" x=\"539\" y=\"-176.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
898
+ "</g>\n",
899
+ "</g>\n",
900
+ "</svg>\n"
901
+ ]
902
+ },
903
+ "metadata": {},
904
+ "output_type": "display_data"
905
+ }
906
+ ],
907
+ "source": [
908
+ "var trigonometryDeleted = await j.Fact(new CourseDeleted(trigonometry, DateTime.Now));\n",
909
+ "\n",
910
+ "Renderer.RenderFacts(algebra, geometry, trigonometryDeleted, calculus)"
911
+ ]
912
+ },
913
+ {
914
+ "attachments": {},
915
+ "cell_type": "markdown",
916
+ "metadata": {},
917
+ "source": [
918
+ "We can revise the specification to return only courses that have not been deleted."
919
+ ]
920
+ },
921
+ {
922
+ "cell_type": "code",
923
+ "execution_count": 64,
924
+ "metadata": {
925
+ "dotnet_interactive": {
926
+ "language": "csharp"
927
+ },
928
+ "polyglot_notebook": {
929
+ "kernelName": "csharp"
930
+ }
931
+ },
932
+ "outputs": [
933
+ {
934
+ "data": {
935
+ "text/plain": [
936
+ "(school: School) {\n",
937
+ " course: Course [\n",
938
+ " course->school: School = school\n",
939
+ " !E {\n",
940
+ " deleted: Course.Deleted [\n",
941
+ " deleted->course: Course = course\n",
942
+ " ]\n",
943
+ " }\n",
944
+ " ]\n",
945
+ "} => course\n"
946
+ ]
947
+ },
948
+ "metadata": {},
949
+ "output_type": "display_data"
950
+ }
951
+ ],
952
+ "source": [
953
+ "var coursesInSchoolNotDeleted = Given<School>.Match((school, facts) =>\n",
954
+ " from course in facts.OfType<Course>()\n",
955
+ " where course.school == school\n",
956
+ " where !facts.OfType<CourseDeleted>(deleted => deleted.course == course).Any()\n",
957
+ " select course\n",
958
+ ");\n",
959
+ "\n",
960
+ "coursesInSchoolNotDeleted.ToDescriptiveString()"
961
+ ]
962
+ },
963
+ {
964
+ "attachments": {},
965
+ "cell_type": "markdown",
966
+ "metadata": {},
967
+ "source": [
968
+ "When we run this specification for the school \"LPS Frisco\", we get back the three courses that have not been deleted."
969
+ ]
970
+ },
971
+ {
972
+ "cell_type": "code",
973
+ "execution_count": 65,
974
+ "metadata": {
975
+ "dotnet_interactive": {
976
+ "language": "csharp"
977
+ },
978
+ "polyglot_notebook": {
979
+ "kernelName": "csharp"
980
+ }
981
+ },
982
+ "outputs": [
983
+ {
984
+ "data": {
985
+ "text/html": [
986
+ "<table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Course { school = School { name = LPS Frisco }, identifier = MATH 101 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>school</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>School { name = LPS Frisco }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>LPS Frisco</td></tr></tbody></table></div></details></td></tr><tr><td>identifier</td><td>MATH 101</td></tr></tbody></table></div></details></td></tr><tr><td>1</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Course { school = School { name = LPS Frisco }, identifier = MATH 102 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>school</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>School { name = LPS Frisco }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>LPS Frisco</td></tr></tbody></table></div></details></td></tr><tr><td>identifier</td><td>MATH 102</td></tr></tbody></table></div></details></td></tr><tr><td>2</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Course { school = School { name = LPS Frisco }, identifier = MATH 301 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>school</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>School { name = LPS Frisco }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>LPS Frisco</td></tr></tbody></table></div></details></td></tr><tr><td>identifier</td><td>MATH 301</td></tr></tbody></table></div></details></td></tr></tbody></table><style>\r\n",
987
+ ".dni-code-hint {\r\n",
988
+ " font-style: italic;\r\n",
989
+ " overflow: hidden;\r\n",
990
+ " white-space: nowrap;\r\n",
991
+ "}\r\n",
992
+ ".dni-treeview {\r\n",
993
+ " white-space: nowrap;\r\n",
994
+ "}\r\n",
995
+ ".dni-treeview td {\r\n",
996
+ " vertical-align: top;\r\n",
997
+ " text-align: start;\r\n",
998
+ "}\r\n",
999
+ "details.dni-treeview {\r\n",
1000
+ " padding-left: 1em;\r\n",
1001
+ "}\r\n",
1002
+ "table td {\r\n",
1003
+ " text-align: start;\r\n",
1004
+ "}\r\n",
1005
+ "table tr { \r\n",
1006
+ " vertical-align: top; \r\n",
1007
+ " margin: 0em 0px;\r\n",
1008
+ "}\r\n",
1009
+ "table tr td pre \r\n",
1010
+ "{ \r\n",
1011
+ " vertical-align: top !important; \r\n",
1012
+ " margin: 0em 0px !important;\r\n",
1013
+ "} \r\n",
1014
+ "table th {\r\n",
1015
+ " text-align: start;\r\n",
1016
+ "}\r\n",
1017
+ "</style>"
1018
+ ]
1019
+ },
1020
+ "metadata": {},
1021
+ "output_type": "display_data"
1022
+ }
1023
+ ],
1024
+ "source": [
1025
+ "var catalogNotDeleted = await j.Query(lps, coursesInSchoolNotDeleted);\n",
1026
+ "\n",
1027
+ "catalogNotDeleted"
1028
+ ]
1029
+ },
1030
+ {
1031
+ "attachments": {},
1032
+ "cell_type": "markdown",
1033
+ "metadata": {},
1034
+ "source": [
1035
+ "### Feeds with Existential Conditions\n",
1036
+ "\n",
1037
+ "Existential conditions affect feeds differently than they affect results.\n",
1038
+ "The reason is that a negative existential condition *removes* results.\n",
1039
+ "The fact that *caused* the deletion is not returned.\n",
1040
+ "\n",
1041
+ "To rectify this issue, a specification with a negative existential condition produces an additional feed.\n",
1042
+ "This feed includes the facts that caused the deletion.\n",
1043
+ "For this example, the feed returns the tuple of course and deletion.\n"
1044
+ ]
1045
+ },
1046
+ {
1047
+ "cell_type": "code",
1048
+ "execution_count": 66,
1049
+ "metadata": {
1050
+ "dotnet_interactive": {
1051
+ "language": "csharp"
1052
+ },
1053
+ "polyglot_notebook": {
1054
+ "kernelName": "csharp"
1055
+ }
1056
+ },
1057
+ "outputs": [
1058
+ {
1059
+ "data": {
1060
+ "text/plain": [
1061
+ "(school: School) {\n",
1062
+ " course: Course [\n",
1063
+ " course->school: School = school\n",
1064
+ " ]\n",
1065
+ " deleted: Course.Deleted [\n",
1066
+ " deleted->course: Course = course\n",
1067
+ " ]\n",
1068
+ "} => {\n",
1069
+ " course = course\n",
1070
+ " deleted = deleted\n",
1071
+ "}\n"
1072
+ ]
1073
+ },
1074
+ "metadata": {},
1075
+ "output_type": "display_data"
1076
+ }
1077
+ ],
1078
+ "source": [
1079
+ "Given<School>.Match((school, facts) =>\n",
1080
+ " from course in facts.OfType<Course>()\n",
1081
+ " where course.school == school\n",
1082
+ " from deleted in facts.OfType<CourseDeleted>()\n",
1083
+ " where deleted.course == course\n",
1084
+ " select new { course, deleted }\n",
1085
+ ").ToDescriptiveString()"
1086
+ ]
1087
+ },
1088
+ {
1089
+ "attachments": {},
1090
+ "cell_type": "markdown",
1091
+ "metadata": {},
1092
+ "source": [
1093
+ "Generating SQL for this feed is no different than the example we saw earlier.\n",
1094
+ "It simply traverses two edges instead of one.\n",
1095
+ "But generating SQL for the other feed brings in one additional step.\n",
1096
+ "It must generate a `NOT EXISTS` clause for the existential condition.\n",
1097
+ "\n",
1098
+ "As was mentioned before, the Query Description contains `not exists` condition descriptions.\n",
1099
+ "Each one contains a list of `edges`.\n",
1100
+ "The example of deleting a course introduces one `not exists` condition having one edge.\n",
1101
+ "\n",
1102
+ "Jinaga allocates a second edge for the `not exists` condition.\n",
1103
+ "This represents the connection between the `course` label (fact index 2) and the newly allocated `deleted` label (fact index 3).\n",
1104
+ "It also allocates a parameter (index 4) for the role of the edge.\n",
1105
+ "The resulting clause is as follows.\n",
1106
+ "\n",
1107
+ "```javascript\n",
1108
+ "notExists: [\n",
1109
+ " {\n",
1110
+ " edges: [\n",
1111
+ " {\n",
1112
+ " edgeIndex: 2, // deleted -> course\n",
1113
+ " predecessorFactIndex: 2, // course\n",
1114
+ " successorFactIndex: 3, // deleted\n",
1115
+ " roleParameter: 4\n",
1116
+ " }\n",
1117
+ " ]\n",
1118
+ " }\n",
1119
+ "]\n",
1120
+ "```\n",
1121
+ "\n",
1122
+ "To turn the `notExists` condition into SQL, Jinaga generates a `NOT EXISTS` condition within the `WHERE` clause.\n",
1123
+ "It connects the condition to the outer query using a `WHERE` clause.\n",
1124
+ "\n",
1125
+ "```sql\n",
1126
+ "AND NOT EXISTS (\n",
1127
+ " SELECT 1\n",
1128
+ " FROM edge e2 -- deleted -> course\n",
1129
+ " JOIN fact f3 -- deleted\n",
1130
+ " ON f3.fact_id = e2.successor_fact_id\n",
1131
+ " WHERE e2.predecessor_fact_id = f2.fact_id\n",
1132
+ " AND e2.role_id = $4\n",
1133
+ ")\n",
1134
+ "```\n",
1135
+ "\n",
1136
+ "The rest of the SQL generation is the same as before.\n",
1137
+ "The resulting SQL is as follows:\n",
1138
+ "\n",
1139
+ "```sql\n",
1140
+ "SELECT\n",
1141
+ " f2.hash as hash2,\n",
1142
+ " sort(array[f2.fact_id], 'desc') as bookmark\n",
1143
+ "FROM fact f1 -- school\n",
1144
+ "JOIN edge e1 -- course -> school\n",
1145
+ " ON e1.predecessor_fact_id = f1.fact_id\n",
1146
+ " AND e1.role_id = $3\n",
1147
+ "JOIN fact f2 -- course\n",
1148
+ " ON f2.fact_id = e1.successor_fact_id\n",
1149
+ "WHERE\n",
1150
+ " f1.fact_type_id = $1 AND f1.hash = $2\n",
1151
+ " AND NOT EXISTS (\n",
1152
+ " SELECT 1\n",
1153
+ " FROM edge e2 -- deleted -> course\n",
1154
+ " JOIN fact f3 -- deleted\n",
1155
+ " ON f3.fact_id = e2.successor_fact_id\n",
1156
+ " WHERE e2.predecessor_fact_id = f2.fact_id\n",
1157
+ " AND e2.role_id = $4)\n",
1158
+ " AND sort(array[f2.fact_id], 'desc') > $5\n",
1159
+ "ORDER BY bookmark ASC LIMIT $6\n",
1160
+ "```\n",
1161
+ "\n",
1162
+ "A feed does not contain nested existential conditions.\n",
1163
+ "A nested existential condition usually represents undoing a previous deletion.\n",
1164
+ "If the feed executed those nested conditions, then tuples would be re-inserted into the middle of a feed when the deletion was undone.\n",
1165
+ "This would violate the feed's integrity.\n",
1166
+ "A client that had already read past the point of re-insertion would never see the re-inserted tuples.\n",
1167
+ "\n",
1168
+ "The solution instead is to generate a separate feed for each nested existential condition.\n",
1169
+ "This would result in three separate feeds: one for the courses, one for the deletions, and one to undo deletions."
1170
+ ]
1171
+ },
1172
+ {
1173
+ "attachments": {},
1174
+ "cell_type": "markdown",
1175
+ "metadata": {},
1176
+ "source": [
1177
+ "### Results with Existential Conditions\n",
1178
+ "\n",
1179
+ "Results queries differ from feed queries in two ways as it relates to existential conditions.\n",
1180
+ "First, they apply both positive and negative conditions.\n",
1181
+ "Second, they allow nesting of existential conditions to any depth.\n",
1182
+ "\n",
1183
+ "Since results execute both positive and negative conditions, the `notExists` list is instead called `existentialConditions`.\n",
1184
+ "Each element contains a flag called `exists` indicating whether the condition is positive or negative.\n",
1185
+ "\n",
1186
+ "Then to accommodate nesting, each existential condition definition contains both edges and a child `existentialConditions` list.\n",
1187
+ "In this particular scenario, the child list is empty.\n",
1188
+ "\n",
1189
+ "```javascript\n",
1190
+ "existentialConditions: [\n",
1191
+ " {\n",
1192
+ " exists: false,\n",
1193
+ " edges: [\n",
1194
+ " {\n",
1195
+ " edgeIndex: 2, // deleted -> course\n",
1196
+ " predecessorFactIndex: 2, // course\n",
1197
+ " successorFactIndex: 3, // deleted\n",
1198
+ " roleParameter: 4\n",
1199
+ " }\n",
1200
+ " ],\n",
1201
+ " existentialConditions: []\n",
1202
+ " }\n",
1203
+ "]\n",
1204
+ "```\n",
1205
+ "\n",
1206
+ "To see how nested existential conditions would affect a result query, let's consider a specification that allows a deleted course to be restored."
1207
+ ]
1208
+ },
1209
+ {
1210
+ "cell_type": "code",
1211
+ "execution_count": 67,
1212
+ "metadata": {
1213
+ "dotnet_interactive": {
1214
+ "language": "csharp"
1215
+ },
1216
+ "polyglot_notebook": {
1217
+ "kernelName": "csharp"
1218
+ }
1219
+ },
1220
+ "outputs": [
1221
+ {
1222
+ "data": {
1223
+ "text/html": [
1224
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
1225
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
1226
+ " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
1227
+ "<!-- Generated by graphviz version 2.43.0 (0)\n",
1228
+ " -->\n",
1229
+ "<!-- Title: %3 Pages: 1 -->\n",
1230
+ "<svg width=\"185pt\" height=\"305pt\"\n",
1231
+ " viewBox=\"0.00 0.00 184.78 305.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
1232
+ "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 301)\">\n",
1233
+ "<title>%3</title>\n",
1234
+ "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-301 180.78,-301 180.78,4 -4,4\"/>\n",
1235
+ "<!-- Course.Restored -->\n",
1236
+ "<g id=\"node1\" class=\"node\">\n",
1237
+ "<title>Course.Restored</title>\n",
1238
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"88.39\" cy=\"-18\" rx=\"88.28\" ry=\"18\"/>\n",
1239
+ "<text text-anchor=\"middle\" x=\"88.39\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Restored</text>\n",
1240
+ "</g>\n",
1241
+ "<!-- Course.Deleted -->\n",
1242
+ "<g id=\"node2\" class=\"node\">\n",
1243
+ "<title>Course.Deleted</title>\n",
1244
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"88.39\" cy=\"-105\" rx=\"82.59\" ry=\"18\"/>\n",
1245
+ "<text text-anchor=\"middle\" x=\"88.39\" y=\"-101.3\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Deleted</text>\n",
1246
+ "</g>\n",
1247
+ "<!-- Course.Restored&#45;&gt;Course.Deleted -->\n",
1248
+ "<g id=\"edge1\" class=\"edge\">\n",
1249
+ "<title>Course.Restored&#45;&gt;Course.Deleted</title>\n",
1250
+ "<path fill=\"none\" stroke=\"black\" d=\"M88.39,-36.2C88.39,-47.84 88.39,-63.45 88.39,-76.76\"/>\n",
1251
+ "<polygon fill=\"black\" stroke=\"black\" points=\"84.89,-76.82 88.39,-86.82 91.89,-76.82 84.89,-76.82\"/>\n",
1252
+ "<text text-anchor=\"middle\" x=\"115.39\" y=\"-57.8\" font-family=\"Times,serif\" font-size=\"14.00\">deleted</text>\n",
1253
+ "</g>\n",
1254
+ "<!-- Course -->\n",
1255
+ "<g id=\"node3\" class=\"node\">\n",
1256
+ "<title>Course</title>\n",
1257
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"88.39\" cy=\"-192\" rx=\"43.59\" ry=\"18\"/>\n",
1258
+ "<text text-anchor=\"middle\" x=\"88.39\" y=\"-188.3\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
1259
+ "</g>\n",
1260
+ "<!-- Course.Deleted&#45;&gt;Course -->\n",
1261
+ "<g id=\"edge2\" class=\"edge\">\n",
1262
+ "<title>Course.Deleted&#45;&gt;Course</title>\n",
1263
+ "<path fill=\"none\" stroke=\"black\" d=\"M88.39,-123.2C88.39,-134.84 88.39,-150.45 88.39,-163.76\"/>\n",
1264
+ "<polygon fill=\"black\" stroke=\"black\" points=\"84.89,-163.82 88.39,-173.82 91.89,-163.82 84.89,-163.82\"/>\n",
1265
+ "<text text-anchor=\"middle\" x=\"112.39\" y=\"-144.8\" font-family=\"Times,serif\" font-size=\"14.00\">course</text>\n",
1266
+ "</g>\n",
1267
+ "<!-- School -->\n",
1268
+ "<g id=\"node4\" class=\"node\">\n",
1269
+ "<title>School</title>\n",
1270
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"88.39\" cy=\"-279\" rx=\"41.69\" ry=\"18\"/>\n",
1271
+ "<text text-anchor=\"middle\" x=\"88.39\" y=\"-275.3\" font-family=\"Times,serif\" font-size=\"14.00\">School</text>\n",
1272
+ "</g>\n",
1273
+ "<!-- Course&#45;&gt;School -->\n",
1274
+ "<g id=\"edge3\" class=\"edge\">\n",
1275
+ "<title>Course&#45;&gt;School</title>\n",
1276
+ "<path fill=\"none\" stroke=\"black\" d=\"M88.39,-210.2C88.39,-221.84 88.39,-237.45 88.39,-250.76\"/>\n",
1277
+ "<polygon fill=\"black\" stroke=\"black\" points=\"84.89,-250.82 88.39,-260.82 91.89,-250.82 84.89,-250.82\"/>\n",
1278
+ "<text text-anchor=\"middle\" x=\"111.39\" y=\"-231.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
1279
+ "</g>\n",
1280
+ "</g>\n",
1281
+ "</svg>\n"
1282
+ ]
1283
+ },
1284
+ "metadata": {},
1285
+ "output_type": "display_data"
1286
+ }
1287
+ ],
1288
+ "source": [
1289
+ "[FactType(\"Course.Restored\")]\n",
1290
+ "public record CourseRestored(CourseDeleted deleted) {}\n",
1291
+ "\n",
1292
+ "Renderer.RenderTypes(typeof(CourseRestored))"
1293
+ ]
1294
+ },
1295
+ {
1296
+ "cell_type": "code",
1297
+ "execution_count": 68,
1298
+ "metadata": {
1299
+ "dotnet_interactive": {
1300
+ "language": "csharp"
1301
+ },
1302
+ "polyglot_notebook": {
1303
+ "kernelName": "csharp"
1304
+ }
1305
+ },
1306
+ "outputs": [
1307
+ {
1308
+ "data": {
1309
+ "text/html": [
1310
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
1311
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
1312
+ " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
1313
+ "<!-- Generated by graphviz version 2.43.0 (0)\n",
1314
+ " -->\n",
1315
+ "<!-- Title: %3 Pages: 1 -->\n",
1316
+ "<svg width=\"748pt\" height=\"349pt\"\n",
1317
+ " viewBox=\"0.00 0.00 748.00 349.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
1318
+ "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 345)\">\n",
1319
+ "<title>%3</title>\n",
1320
+ "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-345 744,-345 744,4 -4,4\"/>\n",
1321
+ "<!-- Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
1322
+ "<g id=\"node1\" class=\"node\">\n",
1323
+ "<title>Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
1324
+ "<polygon fill=\"none\" stroke=\"black\" points=\"275,-316 275,-337 405,-337 405,-316 275,-316\"/>\n",
1325
+ "<text text-anchor=\"start\" x=\"316\" y=\"-322.8\" font-family=\"Times,serif\" font-size=\"14.00\">School</text>\n",
1326
+ "<polygon fill=\"none\" stroke=\"black\" points=\"275,-295 275,-316 321,-316 321,-295 275,-295\"/>\n",
1327
+ "<text text-anchor=\"start\" x=\"278\" y=\"-301.8\" font-family=\"Times,serif\" font-size=\"14.00\">name</text>\n",
1328
+ "<polygon fill=\"none\" stroke=\"black\" points=\"321,-295 321,-316 405,-316 405,-295 321,-295\"/>\n",
1329
+ "<text text-anchor=\"start\" x=\"324\" y=\"-301.8\" font-family=\"Times,serif\" font-size=\"14.00\">LPS Frisco</text>\n",
1330
+ "</g>\n",
1331
+ "<!-- 3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg== -->\n",
1332
+ "<g id=\"node2\" class=\"node\">\n",
1333
+ "<title>3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==</title>\n",
1334
+ "<polygon fill=\"none\" stroke=\"black\" points=\"9,-214 9,-235 163,-235 163,-214 9,-214\"/>\n",
1335
+ "<text text-anchor=\"start\" x=\"60.5\" y=\"-220.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
1336
+ "<polygon fill=\"none\" stroke=\"black\" points=\"9,-193 9,-214 81,-214 81,-193 9,-193\"/>\n",
1337
+ "<text text-anchor=\"start\" x=\"12\" y=\"-199.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
1338
+ "<polygon fill=\"none\" stroke=\"black\" points=\"81,-193 81,-214 163,-214 163,-193 81,-193\"/>\n",
1339
+ "<text text-anchor=\"start\" x=\"84\" y=\"-199.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 101</text>\n",
1340
+ "<polygon fill=\"none\" stroke=\"black\" points=\"8,-192 8,-236 164,-236 164,-192 8,-192\"/>\n",
1341
+ "</g>\n",
1342
+ "<!-- 3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
1343
+ "<g id=\"edge1\" class=\"edge\">\n",
1344
+ "<title>3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
1345
+ "<path fill=\"none\" stroke=\"black\" d=\"M149.77,-240.11C186.21,-254.45 231.98,-272.47 269.49,-287.24\"/>\n",
1346
+ "<polygon fill=\"black\" stroke=\"black\" points=\"268.25,-290.51 278.84,-290.92 270.81,-284 268.25,-290.51\"/>\n",
1347
+ "<text text-anchor=\"middle\" x=\"252\" y=\"-261.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
1348
+ "</g>\n",
1349
+ "<!-- i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg== -->\n",
1350
+ "<g id=\"node3\" class=\"node\">\n",
1351
+ "<title>i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==</title>\n",
1352
+ "<polygon fill=\"none\" stroke=\"black\" points=\"199,-214 199,-235 353,-235 353,-214 199,-214\"/>\n",
1353
+ "<text text-anchor=\"start\" x=\"250.5\" y=\"-220.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
1354
+ "<polygon fill=\"none\" stroke=\"black\" points=\"199,-193 199,-214 271,-214 271,-193 199,-193\"/>\n",
1355
+ "<text text-anchor=\"start\" x=\"202\" y=\"-199.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
1356
+ "<polygon fill=\"none\" stroke=\"black\" points=\"271,-193 271,-214 353,-214 353,-193 271,-193\"/>\n",
1357
+ "<text text-anchor=\"start\" x=\"274\" y=\"-199.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 102</text>\n",
1358
+ "<polygon fill=\"none\" stroke=\"black\" points=\"198,-192 198,-236 354,-236 354,-192 198,-192\"/>\n",
1359
+ "</g>\n",
1360
+ "<!-- i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
1361
+ "<g id=\"edge2\" class=\"edge\">\n",
1362
+ "<title>i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
1363
+ "<path fill=\"none\" stroke=\"black\" d=\"M292.15,-240.23C300.29,-252.95 310.26,-268.54 318.99,-282.18\"/>\n",
1364
+ "<polygon fill=\"black\" stroke=\"black\" points=\"316.18,-284.27 324.52,-290.81 322.07,-280.5 316.18,-284.27\"/>\n",
1365
+ "<text text-anchor=\"middle\" x=\"335\" y=\"-261.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
1366
+ "</g>\n",
1367
+ "<!-- T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw== -->\n",
1368
+ "<g id=\"node4\" class=\"node\">\n",
1369
+ "<title>T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==</title>\n",
1370
+ "<polygon fill=\"none\" stroke=\"black\" points=\"388,-214 388,-235 542,-235 542,-214 388,-214\"/>\n",
1371
+ "<text text-anchor=\"start\" x=\"439.5\" y=\"-220.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
1372
+ "<polygon fill=\"none\" stroke=\"black\" points=\"388,-193 388,-214 460,-214 460,-193 388,-193\"/>\n",
1373
+ "<text text-anchor=\"start\" x=\"391\" y=\"-199.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
1374
+ "<polygon fill=\"none\" stroke=\"black\" points=\"460,-193 460,-214 542,-214 542,-193 460,-193\"/>\n",
1375
+ "<text text-anchor=\"start\" x=\"463\" y=\"-199.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 201</text>\n",
1376
+ "</g>\n",
1377
+ "<!-- T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
1378
+ "<g id=\"edge3\" class=\"edge\">\n",
1379
+ "<title>T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
1380
+ "<path fill=\"none\" stroke=\"black\" d=\"M434.74,-239.21C417.82,-252.74 396.57,-269.75 378.48,-284.22\"/>\n",
1381
+ "<polygon fill=\"black\" stroke=\"black\" points=\"376.04,-281.69 370.41,-290.67 380.41,-287.16 376.04,-281.69\"/>\n",
1382
+ "<text text-anchor=\"middle\" x=\"433\" y=\"-261.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
1383
+ "</g>\n",
1384
+ "<!-- nJcwOXaAmB3e5dtd2UrhA4Xwoh6DA0c9ksEkVoAoauV4N7yevNFjb0xk+6KzmWpmZNBj+XaV6zKAok8kHWGrSw== -->\n",
1385
+ "<g id=\"node5\" class=\"node\">\n",
1386
+ "<title>nJcwOXaAmB3e5dtd2UrhA4Xwoh6DA0c9ksEkVoAoauV4N7yevNFjb0xk+6KzmWpmZNBj+XaV6zKAok8kHWGrSw==</title>\n",
1387
+ "<polygon fill=\"none\" stroke=\"black\" points=\"327,-112 327,-133 604,-133 604,-112 327,-112\"/>\n",
1388
+ "<text text-anchor=\"start\" x=\"410\" y=\"-118.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Deleted</text>\n",
1389
+ "<polygon fill=\"none\" stroke=\"black\" points=\"327,-91 327,-112 403,-112 403,-91 327,-91\"/>\n",
1390
+ "<text text-anchor=\"start\" x=\"330\" y=\"-97.8\" font-family=\"Times,serif\" font-size=\"14.00\">deletedAt</text>\n",
1391
+ "<polygon fill=\"none\" stroke=\"black\" points=\"403,-91 403,-112 604,-112 604,-91 403,-91\"/>\n",
1392
+ "<text text-anchor=\"start\" x=\"406\" y=\"-97.8\" font-family=\"Times,serif\" font-size=\"14.00\">2023&#45;04&#45;02T20:02:06.321Z</text>\n",
1393
+ "</g>\n",
1394
+ "<!-- nJcwOXaAmB3e5dtd2UrhA4Xwoh6DA0c9ksEkVoAoauV4N7yevNFjb0xk+6KzmWpmZNBj+XaV6zKAok8kHWGrSw==&#45;&gt;T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw== -->\n",
1395
+ "<g id=\"edge4\" class=\"edge\">\n",
1396
+ "<title>nJcwOXaAmB3e5dtd2UrhA4Xwoh6DA0c9ksEkVoAoauV4N7yevNFjb0xk+6KzmWpmZNBj+XaV6zKAok8kHWGrSw==&#45;&gt;T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==</title>\n",
1397
+ "<path fill=\"none\" stroke=\"black\" d=\"M465,-137.21C465,-149.64 465,-165.01 465,-178.65\"/>\n",
1398
+ "<polygon fill=\"black\" stroke=\"black\" points=\"461.5,-178.67 465,-188.67 468.5,-178.67 461.5,-178.67\"/>\n",
1399
+ "<text text-anchor=\"middle\" x=\"489\" y=\"-158.8\" font-family=\"Times,serif\" font-size=\"14.00\">course</text>\n",
1400
+ "</g>\n",
1401
+ "<!-- drXyRYftMyvQZqRRDV28PWOy5rhZa4AxxnIH7+iIF4p/FGKujsU7gocc0w24Mg72lxEpwX+h1WSeKhiBxd+0Kg== -->\n",
1402
+ "<g id=\"node6\" class=\"node\">\n",
1403
+ "<title>drXyRYftMyvQZqRRDV28PWOy5rhZa4AxxnIH7+iIF4p/FGKujsU7gocc0w24Mg72lxEpwX+h1WSeKhiBxd+0Kg==</title>\n",
1404
+ "<polygon fill=\"none\" stroke=\"black\" points=\"402,-7 402,-28 528,-28 528,-7 402,-7\"/>\n",
1405
+ "<text text-anchor=\"start\" x=\"405\" y=\"-13.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Restored</text>\n",
1406
+ "<polygon fill=\"none\" stroke=\"black\" points=\"401,-6.5 401,-29.5 529,-29.5 529,-6.5 401,-6.5\"/>\n",
1407
+ "</g>\n",
1408
+ "<!-- drXyRYftMyvQZqRRDV28PWOy5rhZa4AxxnIH7+iIF4p/FGKujsU7gocc0w24Mg72lxEpwX+h1WSeKhiBxd+0Kg==&#45;&gt;nJcwOXaAmB3e5dtd2UrhA4Xwoh6DA0c9ksEkVoAoauV4N7yevNFjb0xk+6KzmWpmZNBj+XaV6zKAok8kHWGrSw== -->\n",
1409
+ "<g id=\"edge5\" class=\"edge\">\n",
1410
+ "<title>drXyRYftMyvQZqRRDV28PWOy5rhZa4AxxnIH7+iIF4p/FGKujsU7gocc0w24Mg72lxEpwX+h1WSeKhiBxd+0Kg==&#45;&gt;nJcwOXaAmB3e5dtd2UrhA4Xwoh6DA0c9ksEkVoAoauV4N7yevNFjb0xk+6KzmWpmZNBj+XaV6zKAok8kHWGrSw==</title>\n",
1411
+ "<path fill=\"none\" stroke=\"black\" d=\"M465,-36.3C465,-47.67 465,-62.93 465,-76.69\"/>\n",
1412
+ "<polygon fill=\"black\" stroke=\"black\" points=\"461.5,-76.83 465,-86.83 468.5,-76.83 461.5,-76.83\"/>\n",
1413
+ "<text text-anchor=\"middle\" x=\"492\" y=\"-57.8\" font-family=\"Times,serif\" font-size=\"14.00\">deleted</text>\n",
1414
+ "</g>\n",
1415
+ "<!-- kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ== -->\n",
1416
+ "<g id=\"node7\" class=\"node\">\n",
1417
+ "<title>kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==</title>\n",
1418
+ "<polygon fill=\"none\" stroke=\"black\" points=\"577,-214 577,-235 731,-235 731,-214 577,-214\"/>\n",
1419
+ "<text text-anchor=\"start\" x=\"628.5\" y=\"-220.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
1420
+ "<polygon fill=\"none\" stroke=\"black\" points=\"577,-193 577,-214 649,-214 649,-193 577,-193\"/>\n",
1421
+ "<text text-anchor=\"start\" x=\"580\" y=\"-199.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
1422
+ "<polygon fill=\"none\" stroke=\"black\" points=\"649,-193 649,-214 731,-214 731,-193 649,-193\"/>\n",
1423
+ "<text text-anchor=\"start\" x=\"652\" y=\"-199.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 301</text>\n",
1424
+ "<polygon fill=\"none\" stroke=\"black\" points=\"576,-192 576,-236 732,-236 732,-192 576,-192\"/>\n",
1425
+ "</g>\n",
1426
+ "<!-- kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
1427
+ "<g id=\"edge6\" class=\"edge\">\n",
1428
+ "<title>kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
1429
+ "<path fill=\"none\" stroke=\"black\" d=\"M575.17,-240.11C528.69,-254.91 469.93,-273.62 422.78,-288.64\"/>\n",
1430
+ "<polygon fill=\"black\" stroke=\"black\" points=\"421.67,-285.32 413.2,-291.69 423.79,-291.99 421.67,-285.32\"/>\n",
1431
+ "<text text-anchor=\"middle\" x=\"539\" y=\"-261.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
1432
+ "</g>\n",
1433
+ "</g>\n",
1434
+ "</svg>\n"
1435
+ ]
1436
+ },
1437
+ "metadata": {},
1438
+ "output_type": "display_data"
1439
+ }
1440
+ ],
1441
+ "source": [
1442
+ "var trigonometryRestored = await j.Fact(new CourseRestored(trigonometryDeleted));\n",
1443
+ "\n",
1444
+ "Renderer.RenderFacts(algebra, geometry, trigonometryRestored, calculus)"
1445
+ ]
1446
+ },
1447
+ {
1448
+ "cell_type": "code",
1449
+ "execution_count": 69,
1450
+ "metadata": {
1451
+ "dotnet_interactive": {
1452
+ "language": "csharp"
1453
+ },
1454
+ "polyglot_notebook": {
1455
+ "kernelName": "csharp"
1456
+ }
1457
+ },
1458
+ "outputs": [
1459
+ {
1460
+ "data": {
1461
+ "text/plain": [
1462
+ "(school: School) {\n",
1463
+ " course: Course [\n",
1464
+ " course->school: School = school\n",
1465
+ " !E {\n",
1466
+ " deleted: Course.Deleted [\n",
1467
+ " deleted->course: Course = course\n",
1468
+ " !E {\n",
1469
+ " restored: Course.Restored [\n",
1470
+ " restored->deleted: Course.Deleted = deleted\n",
1471
+ " ]\n",
1472
+ " }\n",
1473
+ " ]\n",
1474
+ " }\n",
1475
+ " ]\n",
1476
+ "} => course\n"
1477
+ ]
1478
+ },
1479
+ "metadata": {},
1480
+ "output_type": "display_data"
1481
+ }
1482
+ ],
1483
+ "source": [
1484
+ "var coursesInSchoolNotDeletedOrRestored = Given<School>.Match((school, facts) =>\n",
1485
+ " from course in facts.OfType<Course>()\n",
1486
+ " where course.school == school\n",
1487
+ " where !(\n",
1488
+ " from deleted in facts.OfType<CourseDeleted>()\n",
1489
+ " where deleted.course == course\n",
1490
+ " where !facts.OfType<CourseRestored>(restored => restored.deleted == deleted).Any()\n",
1491
+ " select deleted\n",
1492
+ " ).Any()\n",
1493
+ " select course\n",
1494
+ ");\n",
1495
+ "\n",
1496
+ "coursesInSchoolNotDeletedOrRestored.ToDescriptiveString()"
1497
+ ]
1498
+ },
1499
+ {
1500
+ "attachments": {},
1501
+ "cell_type": "markdown",
1502
+ "metadata": {},
1503
+ "source": [
1504
+ "When this specification is run for the school \"LPS Frisco\", it returns all four courses, since trigonometry has now been restored."
1505
+ ]
1506
+ },
1507
+ {
1508
+ "cell_type": "code",
1509
+ "execution_count": 70,
1510
+ "metadata": {
1511
+ "dotnet_interactive": {
1512
+ "language": "csharp"
1513
+ },
1514
+ "polyglot_notebook": {
1515
+ "kernelName": "csharp"
1516
+ }
1517
+ },
1518
+ "outputs": [
1519
+ {
1520
+ "data": {
1521
+ "text/html": [
1522
+ "<table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Course { school = School { name = LPS Frisco }, identifier = MATH 101 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>school</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>School { name = LPS Frisco }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>LPS Frisco</td></tr></tbody></table></div></details></td></tr><tr><td>identifier</td><td>MATH 101</td></tr></tbody></table></div></details></td></tr><tr><td>1</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Course { school = School { name = LPS Frisco }, identifier = MATH 102 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>school</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>School { name = LPS Frisco }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>LPS Frisco</td></tr></tbody></table></div></details></td></tr><tr><td>identifier</td><td>MATH 102</td></tr></tbody></table></div></details></td></tr><tr><td>2</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Course { school = School { name = LPS Frisco }, identifier = MATH 201 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>school</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>School { name = LPS Frisco }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>LPS Frisco</td></tr></tbody></table></div></details></td></tr><tr><td>identifier</td><td>MATH 201</td></tr></tbody></table></div></details></td></tr><tr><td>3</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>Course { school = School { name = LPS Frisco }, identifier = MATH 301 }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>school</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>School { name = LPS Frisco }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>LPS Frisco</td></tr></tbody></table></div></details></td></tr><tr><td>identifier</td><td>MATH 301</td></tr></tbody></table></div></details></td></tr></tbody></table><style>\r\n",
1523
+ ".dni-code-hint {\r\n",
1524
+ " font-style: italic;\r\n",
1525
+ " overflow: hidden;\r\n",
1526
+ " white-space: nowrap;\r\n",
1527
+ "}\r\n",
1528
+ ".dni-treeview {\r\n",
1529
+ " white-space: nowrap;\r\n",
1530
+ "}\r\n",
1531
+ ".dni-treeview td {\r\n",
1532
+ " vertical-align: top;\r\n",
1533
+ " text-align: start;\r\n",
1534
+ "}\r\n",
1535
+ "details.dni-treeview {\r\n",
1536
+ " padding-left: 1em;\r\n",
1537
+ "}\r\n",
1538
+ "table td {\r\n",
1539
+ " text-align: start;\r\n",
1540
+ "}\r\n",
1541
+ "table tr { \r\n",
1542
+ " vertical-align: top; \r\n",
1543
+ " margin: 0em 0px;\r\n",
1544
+ "}\r\n",
1545
+ "table tr td pre \r\n",
1546
+ "{ \r\n",
1547
+ " vertical-align: top !important; \r\n",
1548
+ " margin: 0em 0px !important;\r\n",
1549
+ "} \r\n",
1550
+ "table th {\r\n",
1551
+ " text-align: start;\r\n",
1552
+ "}\r\n",
1553
+ "</style>"
1554
+ ]
1555
+ },
1556
+ "metadata": {},
1557
+ "output_type": "display_data"
1558
+ }
1559
+ ],
1560
+ "source": [
1561
+ "var catalogNotDeletedOrRestored = await j.Query(lps, coursesInSchoolNotDeletedOrRestored);\n",
1562
+ "\n",
1563
+ "catalogNotDeletedOrRestored"
1564
+ ]
1565
+ },
1566
+ {
1567
+ "attachments": {},
1568
+ "cell_type": "markdown",
1569
+ "metadata": {},
1570
+ "source": [
1571
+ "To translate this specification into SQL, Jinaga allocates edge index 2 for the relationship between `deleted` and `course`.\n",
1572
+ "Then it allocates fact index 3 for the `deleted` label.\n",
1573
+ "\n",
1574
+ "For the nested existential condition, it allocates edge index 3 for the relationship between `restored` and `deleted`.\n",
1575
+ "Then it allocates fact index 4 for the `restored` label.\n",
1576
+ "\n",
1577
+ "This produces the following list in the Query Description.\n",
1578
+ "\n",
1579
+ "```javascript\n",
1580
+ "existentialConditions: [\n",
1581
+ " {\n",
1582
+ " exists: false,\n",
1583
+ " edges: [\n",
1584
+ " {\n",
1585
+ " edgeIndex: 2, // deleted -> course\n",
1586
+ " predecessorFactIndex: 2, // course\n",
1587
+ " successorFactIndex: 3, // deleted\n",
1588
+ " roleParameter: 4\n",
1589
+ " }\n",
1590
+ " ],\n",
1591
+ " existentialConditions: [\n",
1592
+ " {\n",
1593
+ " exists: false,\n",
1594
+ " edges: [\n",
1595
+ " {\n",
1596
+ " edgeIndex: 3, // restored -> deleted\n",
1597
+ " predecessorFactIndex: 3, // deleted\n",
1598
+ " successorFactIndex: 4, // restored\n",
1599
+ " roleParameter: 5\n",
1600
+ " }\n",
1601
+ " ],\n",
1602
+ " existentialConditions: []\n",
1603
+ " }\n",
1604
+ " ]\n",
1605
+ " }\n",
1606
+ "]\n",
1607
+ "```\n",
1608
+ "\n",
1609
+ "That translates into the following condition, which is emitted as part of the `WHERE` clause.\n",
1610
+ "\n",
1611
+ "```sql\n",
1612
+ "AND NOT EXISTS (\n",
1613
+ " SELECT 1\n",
1614
+ " FROM edge e2 -- deleted -> course\n",
1615
+ " JOIN fact f3 -- deleted\n",
1616
+ " ON f3.fact_id = e2.successor_fact_id\n",
1617
+ " WHERE e2.predecessor_fact_id = f2.fact_id\n",
1618
+ " AND e2.role_id = $4\n",
1619
+ " AND NOT EXISTS (\n",
1620
+ " SELECT 1\n",
1621
+ " FROM edge e3 -- restored -> deleted\n",
1622
+ " JOIN fact f4 -- restored\n",
1623
+ " ON f4.fact_id = e3.successor_fact_id\n",
1624
+ " WHERE e3.predecessor_fact_id = f3.fact_id\n",
1625
+ " AND e3.role_id = $5\n",
1626
+ " )\n",
1627
+ ")\n",
1628
+ "```"
1629
+ ]
1630
+ },
1631
+ {
1632
+ "attachments": {},
1633
+ "cell_type": "markdown",
1634
+ "metadata": {},
1635
+ "source": [
1636
+ "## Child Specifications\n",
1637
+ "\n",
1638
+ "A projection may contain child specifications.\n",
1639
+ "These specifications produce a list of child results.\n",
1640
+ "To illustrate, let's consider giving each course a name.\n",
1641
+ "Since the name can change, we will use the mutable property pattern."
1642
+ ]
1643
+ },
1644
+ {
1645
+ "cell_type": "code",
1646
+ "execution_count": 71,
1647
+ "metadata": {
1648
+ "dotnet_interactive": {
1649
+ "language": "csharp"
1650
+ },
1651
+ "polyglot_notebook": {
1652
+ "kernelName": "csharp"
1653
+ }
1654
+ },
1655
+ "outputs": [
1656
+ {
1657
+ "data": {
1658
+ "text/html": [
1659
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
1660
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
1661
+ " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
1662
+ "<!-- Generated by graphviz version 2.43.0 (0)\n",
1663
+ " -->\n",
1664
+ "<!-- Title: %3 Pages: 1 -->\n",
1665
+ "<svg width=\"398pt\" height=\"305pt\"\n",
1666
+ " viewBox=\"0.00 0.00 397.84 305.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
1667
+ "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 301)\">\n",
1668
+ "<title>%3</title>\n",
1669
+ "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-301 393.84,-301 393.84,4 -4,4\"/>\n",
1670
+ "<!-- Course.Name -->\n",
1671
+ "<g id=\"node1\" class=\"node\">\n",
1672
+ "<title>Course.Name</title>\n",
1673
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"73.44\" cy=\"-105\" rx=\"73.39\" ry=\"18\"/>\n",
1674
+ "<text text-anchor=\"middle\" x=\"73.44\" y=\"-101.3\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Name</text>\n",
1675
+ "</g>\n",
1676
+ "<!-- Course.Name&#45;&gt;Course.Name -->\n",
1677
+ "<g id=\"edge2\" class=\"edge\">\n",
1678
+ "<title>Course.Name&#45;&gt;Course.Name</title>\n",
1679
+ "<path fill=\"none\" stroke=\"black\" d=\"M140.02,-97.36C154.44,-98.1 164.89,-100.65 164.89,-105 164.89,-108.26 159.01,-110.51 149.99,-111.74\"/>\n",
1680
+ "<polygon fill=\"black\" stroke=\"black\" points=\"149.67,-108.26 140.02,-112.64 150.3,-115.23 149.67,-108.26\"/>\n",
1681
+ "<text text-anchor=\"middle\" x=\"182.89\" y=\"-101.3\" font-family=\"Times,serif\" font-size=\"14.00\">prior</text>\n",
1682
+ "</g>\n",
1683
+ "<!-- Course -->\n",
1684
+ "<g id=\"node2\" class=\"node\">\n",
1685
+ "<title>Course</title>\n",
1686
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"186.44\" cy=\"-192\" rx=\"43.59\" ry=\"18\"/>\n",
1687
+ "<text text-anchor=\"middle\" x=\"186.44\" y=\"-188.3\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
1688
+ "</g>\n",
1689
+ "<!-- Course.Name&#45;&gt;Course -->\n",
1690
+ "<g id=\"edge1\" class=\"edge\">\n",
1691
+ "<title>Course.Name&#45;&gt;Course</title>\n",
1692
+ "<path fill=\"none\" stroke=\"black\" d=\"M95.23,-122.39C113.11,-135.84 138.56,-154.98 158.03,-169.63\"/>\n",
1693
+ "<polygon fill=\"black\" stroke=\"black\" points=\"156.15,-172.59 166.24,-175.8 160.35,-167 156.15,-172.59\"/>\n",
1694
+ "<text text-anchor=\"middle\" x=\"161.44\" y=\"-144.8\" font-family=\"Times,serif\" font-size=\"14.00\">course</text>\n",
1695
+ "</g>\n",
1696
+ "<!-- School -->\n",
1697
+ "<g id=\"node5\" class=\"node\">\n",
1698
+ "<title>School</title>\n",
1699
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"186.44\" cy=\"-279\" rx=\"41.69\" ry=\"18\"/>\n",
1700
+ "<text text-anchor=\"middle\" x=\"186.44\" y=\"-275.3\" font-family=\"Times,serif\" font-size=\"14.00\">School</text>\n",
1701
+ "</g>\n",
1702
+ "<!-- Course&#45;&gt;School -->\n",
1703
+ "<g id=\"edge4\" class=\"edge\">\n",
1704
+ "<title>Course&#45;&gt;School</title>\n",
1705
+ "<path fill=\"none\" stroke=\"black\" d=\"M186.44,-210.2C186.44,-221.84 186.44,-237.45 186.44,-250.76\"/>\n",
1706
+ "<polygon fill=\"black\" stroke=\"black\" points=\"182.94,-250.82 186.44,-260.82 189.94,-250.82 182.94,-250.82\"/>\n",
1707
+ "<text text-anchor=\"middle\" x=\"209.44\" y=\"-231.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
1708
+ "</g>\n",
1709
+ "<!-- Course.Restored -->\n",
1710
+ "<g id=\"node3\" class=\"node\">\n",
1711
+ "<title>Course.Restored</title>\n",
1712
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"301.44\" cy=\"-18\" rx=\"88.28\" ry=\"18\"/>\n",
1713
+ "<text text-anchor=\"middle\" x=\"301.44\" y=\"-14.3\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Restored</text>\n",
1714
+ "</g>\n",
1715
+ "<!-- Course.Deleted -->\n",
1716
+ "<g id=\"node4\" class=\"node\">\n",
1717
+ "<title>Course.Deleted</title>\n",
1718
+ "<ellipse fill=\"none\" stroke=\"black\" cx=\"301.44\" cy=\"-105\" rx=\"82.59\" ry=\"18\"/>\n",
1719
+ "<text text-anchor=\"middle\" x=\"301.44\" y=\"-101.3\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Deleted</text>\n",
1720
+ "</g>\n",
1721
+ "<!-- Course.Restored&#45;&gt;Course.Deleted -->\n",
1722
+ "<g id=\"edge3\" class=\"edge\">\n",
1723
+ "<title>Course.Restored&#45;&gt;Course.Deleted</title>\n",
1724
+ "<path fill=\"none\" stroke=\"black\" d=\"M301.44,-36.2C301.44,-47.84 301.44,-63.45 301.44,-76.76\"/>\n",
1725
+ "<polygon fill=\"black\" stroke=\"black\" points=\"297.94,-76.82 301.44,-86.82 304.94,-76.82 297.94,-76.82\"/>\n",
1726
+ "<text text-anchor=\"middle\" x=\"328.44\" y=\"-57.8\" font-family=\"Times,serif\" font-size=\"14.00\">deleted</text>\n",
1727
+ "</g>\n",
1728
+ "<!-- Course.Deleted&#45;&gt;Course -->\n",
1729
+ "<g id=\"edge5\" class=\"edge\">\n",
1730
+ "<title>Course.Deleted&#45;&gt;Course</title>\n",
1731
+ "<path fill=\"none\" stroke=\"black\" d=\"M279.27,-122.39C261,-135.9 234.95,-155.15 215.09,-169.82\"/>\n",
1732
+ "<polygon fill=\"black\" stroke=\"black\" points=\"212.96,-167.05 207,-175.8 217.13,-172.67 212.96,-167.05\"/>\n",
1733
+ "<text text-anchor=\"middle\" x=\"274.44\" y=\"-144.8\" font-family=\"Times,serif\" font-size=\"14.00\">course</text>\n",
1734
+ "</g>\n",
1735
+ "</g>\n",
1736
+ "</svg>\n"
1737
+ ]
1738
+ },
1739
+ "metadata": {},
1740
+ "output_type": "display_data"
1741
+ }
1742
+ ],
1743
+ "source": [
1744
+ "[FactType(\"Course.Name\")]\n",
1745
+ "public record CourseName(Course course, string value, CourseName[] prior) {}\n",
1746
+ "\n",
1747
+ "Renderer.RenderTypes(typeof(CourseName), typeof(CourseRestored))"
1748
+ ]
1749
+ },
1750
+ {
1751
+ "cell_type": "code",
1752
+ "execution_count": 72,
1753
+ "metadata": {
1754
+ "dotnet_interactive": {
1755
+ "language": "csharp"
1756
+ },
1757
+ "polyglot_notebook": {
1758
+ "kernelName": "csharp"
1759
+ }
1760
+ },
1761
+ "outputs": [
1762
+ {
1763
+ "data": {
1764
+ "text/html": [
1765
+ "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n",
1766
+ "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"\n",
1767
+ " \"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\">\n",
1768
+ "<!-- Generated by graphviz version 2.43.0 (0)\n",
1769
+ " -->\n",
1770
+ "<!-- Title: %3 Pages: 1 -->\n",
1771
+ "<svg width=\"742pt\" height=\"262pt\"\n",
1772
+ " viewBox=\"0.00 0.00 742.00 262.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n",
1773
+ "<g id=\"graph0\" class=\"graph\" transform=\"scale(1 1) rotate(0) translate(4 258)\">\n",
1774
+ "<title>%3</title>\n",
1775
+ "<polygon fill=\"white\" stroke=\"transparent\" points=\"-4,4 -4,-258 738,-258 738,4 -4,4\"/>\n",
1776
+ "<!-- Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
1777
+ "<g id=\"node1\" class=\"node\">\n",
1778
+ "<title>Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
1779
+ "<polygon fill=\"none\" stroke=\"black\" points=\"302,-229 302,-250 432,-250 432,-229 302,-229\"/>\n",
1780
+ "<text text-anchor=\"start\" x=\"343\" y=\"-235.8\" font-family=\"Times,serif\" font-size=\"14.00\">School</text>\n",
1781
+ "<polygon fill=\"none\" stroke=\"black\" points=\"302,-208 302,-229 348,-229 348,-208 302,-208\"/>\n",
1782
+ "<text text-anchor=\"start\" x=\"305\" y=\"-214.8\" font-family=\"Times,serif\" font-size=\"14.00\">name</text>\n",
1783
+ "<polygon fill=\"none\" stroke=\"black\" points=\"348,-208 348,-229 432,-229 432,-208 348,-208\"/>\n",
1784
+ "<text text-anchor=\"start\" x=\"351\" y=\"-214.8\" font-family=\"Times,serif\" font-size=\"14.00\">LPS Frisco</text>\n",
1785
+ "</g>\n",
1786
+ "<!-- 3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg== -->\n",
1787
+ "<g id=\"node2\" class=\"node\">\n",
1788
+ "<title>3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==</title>\n",
1789
+ "<polygon fill=\"none\" stroke=\"black\" points=\"8,-128 8,-149 162,-149 162,-128 8,-128\"/>\n",
1790
+ "<text text-anchor=\"start\" x=\"59.5\" y=\"-134.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
1791
+ "<polygon fill=\"none\" stroke=\"black\" points=\"8,-107 8,-128 80,-128 80,-107 8,-107\"/>\n",
1792
+ "<text text-anchor=\"start\" x=\"11\" y=\"-113.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
1793
+ "<polygon fill=\"none\" stroke=\"black\" points=\"80,-107 80,-128 162,-128 162,-107 80,-107\"/>\n",
1794
+ "<text text-anchor=\"start\" x=\"83\" y=\"-113.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 101</text>\n",
1795
+ "</g>\n",
1796
+ "<!-- 3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
1797
+ "<g id=\"edge1\" class=\"edge\">\n",
1798
+ "<title>3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
1799
+ "<path fill=\"none\" stroke=\"black\" d=\"M153.62,-153.09C194.55,-167.46 246.63,-185.74 289.1,-200.65\"/>\n",
1800
+ "<polygon fill=\"black\" stroke=\"black\" points=\"287.96,-203.96 298.56,-203.97 290.28,-197.36 287.96,-203.96\"/>\n",
1801
+ "<text text-anchor=\"middle\" x=\"266\" y=\"-174.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
1802
+ "</g>\n",
1803
+ "<!-- pUqklGMiDc6YL0cfflosFfbt5HmL0PZFDF4ubEOxwYoESxw3dd/3G55nNKPodIZY+1TgLLzbbUHeDpPDQ8pIxw== -->\n",
1804
+ "<g id=\"node3\" class=\"node\">\n",
1805
+ "<title>pUqklGMiDc6YL0cfflosFfbt5HmL0PZFDF4ubEOxwYoESxw3dd/3G55nNKPodIZY+1TgLLzbbUHeDpPDQ8pIxw==</title>\n",
1806
+ "<polygon fill=\"none\" stroke=\"black\" points=\"32,-26 32,-47 139,-47 139,-26 32,-26\"/>\n",
1807
+ "<text text-anchor=\"start\" x=\"37\" y=\"-32.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Name</text>\n",
1808
+ "<polygon fill=\"none\" stroke=\"black\" points=\"32,-5 32,-26 77,-26 77,-5 32,-5\"/>\n",
1809
+ "<text text-anchor=\"start\" x=\"35\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">value</text>\n",
1810
+ "<polygon fill=\"none\" stroke=\"black\" points=\"77,-5 77,-26 139,-26 139,-5 77,-5\"/>\n",
1811
+ "<text text-anchor=\"start\" x=\"80\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">Algebra</text>\n",
1812
+ "<polygon fill=\"none\" stroke=\"black\" points=\"30.5,-4 30.5,-48 139.5,-48 139.5,-4 30.5,-4\"/>\n",
1813
+ "</g>\n",
1814
+ "<!-- pUqklGMiDc6YL0cfflosFfbt5HmL0PZFDF4ubEOxwYoESxw3dd/3G55nNKPodIZY+1TgLLzbbUHeDpPDQ8pIxw==&#45;&gt;3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg== -->\n",
1815
+ "<g id=\"edge2\" class=\"edge\">\n",
1816
+ "<title>pUqklGMiDc6YL0cfflosFfbt5HmL0PZFDF4ubEOxwYoESxw3dd/3G55nNKPodIZY+1TgLLzbbUHeDpPDQ8pIxw==&#45;&gt;3nDvbOUcWyAgS5+kFM0LSdDaHyJ6+/kSpTnpWspJA3NB22qfOIkhc/KSBs8QYwLhd9j7+AbhVnMhWMIfGeDbFg==</title>\n",
1817
+ "<path fill=\"none\" stroke=\"black\" d=\"M85,-52.23C85,-64.47 85,-79.35 85,-92.61\"/>\n",
1818
+ "<polygon fill=\"black\" stroke=\"black\" points=\"81.5,-92.81 85,-102.81 88.5,-92.81 81.5,-92.81\"/>\n",
1819
+ "<text text-anchor=\"middle\" x=\"109\" y=\"-73.8\" font-family=\"Times,serif\" font-size=\"14.00\">course</text>\n",
1820
+ "</g>\n",
1821
+ "<!-- i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg== -->\n",
1822
+ "<g id=\"node4\" class=\"node\">\n",
1823
+ "<title>i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==</title>\n",
1824
+ "<polygon fill=\"none\" stroke=\"black\" points=\"196,-128 196,-149 350,-149 350,-128 196,-128\"/>\n",
1825
+ "<text text-anchor=\"start\" x=\"247.5\" y=\"-134.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
1826
+ "<polygon fill=\"none\" stroke=\"black\" points=\"196,-107 196,-128 268,-128 268,-107 196,-107\"/>\n",
1827
+ "<text text-anchor=\"start\" x=\"199\" y=\"-113.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
1828
+ "<polygon fill=\"none\" stroke=\"black\" points=\"268,-107 268,-128 350,-128 350,-107 268,-107\"/>\n",
1829
+ "<text text-anchor=\"start\" x=\"271\" y=\"-113.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 102</text>\n",
1830
+ "</g>\n",
1831
+ "<!-- i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
1832
+ "<g id=\"edge3\" class=\"edge\">\n",
1833
+ "<title>i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
1834
+ "<path fill=\"none\" stroke=\"black\" d=\"M295.99,-153.22C308.29,-166.16 323.57,-182.26 336.81,-196.2\"/>\n",
1835
+ "<polygon fill=\"black\" stroke=\"black\" points=\"334.49,-198.85 343.92,-203.69 339.57,-194.03 334.49,-198.85\"/>\n",
1836
+ "<text text-anchor=\"middle\" x=\"349\" y=\"-174.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
1837
+ "</g>\n",
1838
+ "<!-- HEnrriwilIsukxH/9vKO2+aQ6Pbl5Nk/u9VeOxVw8Iv4/Eq6GFVlQ4CJIrcz7v5h4i47BRHMJzCthyA9K4sJLw== -->\n",
1839
+ "<g id=\"node5\" class=\"node\">\n",
1840
+ "<title>HEnrriwilIsukxH/9vKO2+aQ6Pbl5Nk/u9VeOxVw8Iv4/Eq6GFVlQ4CJIrcz7v5h4i47BRHMJzCthyA9K4sJLw==</title>\n",
1841
+ "<polygon fill=\"none\" stroke=\"black\" points=\"212,-26 212,-47 334,-47 334,-26 212,-26\"/>\n",
1842
+ "<text text-anchor=\"start\" x=\"224.5\" y=\"-32.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Name</text>\n",
1843
+ "<polygon fill=\"none\" stroke=\"black\" points=\"212,-5 212,-26 257,-26 257,-5 212,-5\"/>\n",
1844
+ "<text text-anchor=\"start\" x=\"215\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">value</text>\n",
1845
+ "<polygon fill=\"none\" stroke=\"black\" points=\"257,-5 257,-26 334,-26 334,-5 257,-5\"/>\n",
1846
+ "<text text-anchor=\"start\" x=\"260\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">Geometry</text>\n",
1847
+ "<polygon fill=\"none\" stroke=\"black\" points=\"211,-4 211,-48 335,-48 335,-4 211,-4\"/>\n",
1848
+ "</g>\n",
1849
+ "<!-- HEnrriwilIsukxH/9vKO2+aQ6Pbl5Nk/u9VeOxVw8Iv4/Eq6GFVlQ4CJIrcz7v5h4i47BRHMJzCthyA9K4sJLw==&#45;&gt;i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg== -->\n",
1850
+ "<g id=\"edge4\" class=\"edge\">\n",
1851
+ "<title>HEnrriwilIsukxH/9vKO2+aQ6Pbl5Nk/u9VeOxVw8Iv4/Eq6GFVlQ4CJIrcz7v5h4i47BRHMJzCthyA9K4sJLw==&#45;&gt;i4bD0lcETEhyNl7+agqmySwaWJ/iR41ao2Li5wdBlebaQoJTksNdKijPFwb8uJNxSJXWRP/c7b5HjcVdikdQtg==</title>\n",
1852
+ "<path fill=\"none\" stroke=\"black\" d=\"M273,-52.23C273,-64.47 273,-79.35 273,-92.61\"/>\n",
1853
+ "<polygon fill=\"black\" stroke=\"black\" points=\"269.5,-92.81 273,-102.81 276.5,-92.81 269.5,-92.81\"/>\n",
1854
+ "<text text-anchor=\"middle\" x=\"297\" y=\"-73.8\" font-family=\"Times,serif\" font-size=\"14.00\">course</text>\n",
1855
+ "</g>\n",
1856
+ "<!-- T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw== -->\n",
1857
+ "<g id=\"node6\" class=\"node\">\n",
1858
+ "<title>T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==</title>\n",
1859
+ "<polygon fill=\"none\" stroke=\"black\" points=\"384,-128 384,-149 538,-149 538,-128 384,-128\"/>\n",
1860
+ "<text text-anchor=\"start\" x=\"435.5\" y=\"-134.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
1861
+ "<polygon fill=\"none\" stroke=\"black\" points=\"384,-107 384,-128 456,-128 456,-107 384,-107\"/>\n",
1862
+ "<text text-anchor=\"start\" x=\"387\" y=\"-113.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
1863
+ "<polygon fill=\"none\" stroke=\"black\" points=\"456,-107 456,-128 538,-128 538,-107 456,-107\"/>\n",
1864
+ "<text text-anchor=\"start\" x=\"459\" y=\"-113.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 201</text>\n",
1865
+ "</g>\n",
1866
+ "<!-- T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
1867
+ "<g id=\"edge5\" class=\"edge\">\n",
1868
+ "<title>T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
1869
+ "<path fill=\"none\" stroke=\"black\" d=\"M438.01,-153.22C425.71,-166.16 410.43,-182.26 397.19,-196.2\"/>\n",
1870
+ "<polygon fill=\"black\" stroke=\"black\" points=\"394.43,-194.03 390.08,-203.69 399.51,-198.85 394.43,-194.03\"/>\n",
1871
+ "<text text-anchor=\"middle\" x=\"443\" y=\"-174.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
1872
+ "</g>\n",
1873
+ "<!-- rIWOiE98BC+ullNXn28ajkr/8XOvFnxbvyCyxXGTiUmn/61Aipaqy6Ul00Xk9cB0NWUQ0H+YfaW04OSoX20E3g== -->\n",
1874
+ "<g id=\"node7\" class=\"node\">\n",
1875
+ "<title>rIWOiE98BC+ullNXn28ajkr/8XOvFnxbvyCyxXGTiUmn/61Aipaqy6Ul00Xk9cB0NWUQ0H+YfaW04OSoX20E3g==</title>\n",
1876
+ "<polygon fill=\"none\" stroke=\"black\" points=\"387,-26 387,-47 536,-47 536,-26 387,-26\"/>\n",
1877
+ "<text text-anchor=\"start\" x=\"413\" y=\"-32.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Name</text>\n",
1878
+ "<polygon fill=\"none\" stroke=\"black\" points=\"387,-5 387,-26 432,-26 432,-5 387,-5\"/>\n",
1879
+ "<text text-anchor=\"start\" x=\"390\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">value</text>\n",
1880
+ "<polygon fill=\"none\" stroke=\"black\" points=\"432,-5 432,-26 536,-26 536,-5 432,-5\"/>\n",
1881
+ "<text text-anchor=\"start\" x=\"435\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">Trigonometry</text>\n",
1882
+ "<polygon fill=\"none\" stroke=\"black\" points=\"385.5,-4 385.5,-48 536.5,-48 536.5,-4 385.5,-4\"/>\n",
1883
+ "</g>\n",
1884
+ "<!-- rIWOiE98BC+ullNXn28ajkr/8XOvFnxbvyCyxXGTiUmn/61Aipaqy6Ul00Xk9cB0NWUQ0H+YfaW04OSoX20E3g==&#45;&gt;T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw== -->\n",
1885
+ "<g id=\"edge6\" class=\"edge\">\n",
1886
+ "<title>rIWOiE98BC+ullNXn28ajkr/8XOvFnxbvyCyxXGTiUmn/61Aipaqy6Ul00Xk9cB0NWUQ0H+YfaW04OSoX20E3g==&#45;&gt;T3Z0WTDXhGo6D3aTjxQLjR4NRzSnEZDsaP97nD2ok2GYNn7rHGD5LdzooUffO04a8DR1bCUvfc/UF4c2Y2OFkw==</title>\n",
1887
+ "<path fill=\"none\" stroke=\"black\" d=\"M461,-52.23C461,-64.47 461,-79.35 461,-92.61\"/>\n",
1888
+ "<polygon fill=\"black\" stroke=\"black\" points=\"457.5,-92.81 461,-102.81 464.5,-92.81 457.5,-92.81\"/>\n",
1889
+ "<text text-anchor=\"middle\" x=\"485\" y=\"-73.8\" font-family=\"Times,serif\" font-size=\"14.00\">course</text>\n",
1890
+ "</g>\n",
1891
+ "<!-- kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ== -->\n",
1892
+ "<g id=\"node8\" class=\"node\">\n",
1893
+ "<title>kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==</title>\n",
1894
+ "<polygon fill=\"none\" stroke=\"black\" points=\"572,-128 572,-149 726,-149 726,-128 572,-128\"/>\n",
1895
+ "<text text-anchor=\"start\" x=\"623.5\" y=\"-134.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course</text>\n",
1896
+ "<polygon fill=\"none\" stroke=\"black\" points=\"572,-107 572,-128 644,-128 644,-107 572,-107\"/>\n",
1897
+ "<text text-anchor=\"start\" x=\"575\" y=\"-113.8\" font-family=\"Times,serif\" font-size=\"14.00\">identifier</text>\n",
1898
+ "<polygon fill=\"none\" stroke=\"black\" points=\"644,-107 644,-128 726,-128 726,-107 644,-107\"/>\n",
1899
+ "<text text-anchor=\"start\" x=\"647\" y=\"-113.8\" font-family=\"Times,serif\" font-size=\"14.00\">MATH 301</text>\n",
1900
+ "</g>\n",
1901
+ "<!-- kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw== -->\n",
1902
+ "<g id=\"edge7\" class=\"edge\">\n",
1903
+ "<title>kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==&#45;&gt;Y+njFMdFuJ+srMmRbiuwWP4EgODTyDqp0n2WWUPwP0celcFLjEl4VAyvHodSo0BYjb8n70Dmm+8kBfkBBvqJDw==</title>\n",
1904
+ "<path fill=\"none\" stroke=\"black\" d=\"M580.38,-153.09C539.45,-167.46 487.37,-185.74 444.9,-200.65\"/>\n",
1905
+ "<polygon fill=\"black\" stroke=\"black\" points=\"443.72,-197.36 435.44,-203.97 446.04,-203.96 443.72,-197.36\"/>\n",
1906
+ "<text text-anchor=\"middle\" x=\"548\" y=\"-174.8\" font-family=\"Times,serif\" font-size=\"14.00\">school</text>\n",
1907
+ "</g>\n",
1908
+ "<!-- TkXpuLsFX9Vh+Kpc6v6RJgZ/bomN3KWmMCPTJq8Ym1vCWkEX8gYabhRtEtjBKHIkdwLw8X3jlrX1vLPDZaipbg== -->\n",
1909
+ "<g id=\"node9\" class=\"node\">\n",
1910
+ "<title>TkXpuLsFX9Vh+Kpc6v6RJgZ/bomN3KWmMCPTJq8Ym1vCWkEX8gYabhRtEtjBKHIkdwLw8X3jlrX1vLPDZaipbg==</title>\n",
1911
+ "<polygon fill=\"none\" stroke=\"black\" points=\"593,-26 593,-47 705,-47 705,-26 593,-26\"/>\n",
1912
+ "<text text-anchor=\"start\" x=\"600.5\" y=\"-32.8\" font-family=\"Times,serif\" font-size=\"14.00\">Course.Name</text>\n",
1913
+ "<polygon fill=\"none\" stroke=\"black\" points=\"593,-5 593,-26 638,-26 638,-5 593,-5\"/>\n",
1914
+ "<text text-anchor=\"start\" x=\"596\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">value</text>\n",
1915
+ "<polygon fill=\"none\" stroke=\"black\" points=\"638,-5 638,-26 705,-26 705,-5 638,-5\"/>\n",
1916
+ "<text text-anchor=\"start\" x=\"641\" y=\"-11.8\" font-family=\"Times,serif\" font-size=\"14.00\">Calculus</text>\n",
1917
+ "<polygon fill=\"none\" stroke=\"black\" points=\"592,-4 592,-48 706,-48 706,-4 592,-4\"/>\n",
1918
+ "</g>\n",
1919
+ "<!-- TkXpuLsFX9Vh+Kpc6v6RJgZ/bomN3KWmMCPTJq8Ym1vCWkEX8gYabhRtEtjBKHIkdwLw8X3jlrX1vLPDZaipbg==&#45;&gt;kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ== -->\n",
1920
+ "<g id=\"edge8\" class=\"edge\">\n",
1921
+ "<title>TkXpuLsFX9Vh+Kpc6v6RJgZ/bomN3KWmMCPTJq8Ym1vCWkEX8gYabhRtEtjBKHIkdwLw8X3jlrX1vLPDZaipbg==&#45;&gt;kx9coOGM5rOjFI9dxNAsrNU1l9oQzEjeszW43KlFqoGxKZ3+G7oqTZAVZjhzXMB+oL6RTF0TDBRQjRhAqg7WlQ==</title>\n",
1922
+ "<path fill=\"none\" stroke=\"black\" d=\"M649,-52.23C649,-64.47 649,-79.35 649,-92.61\"/>\n",
1923
+ "<polygon fill=\"black\" stroke=\"black\" points=\"645.5,-92.81 649,-102.81 652.5,-92.81 645.5,-92.81\"/>\n",
1924
+ "<text text-anchor=\"middle\" x=\"673\" y=\"-73.8\" font-family=\"Times,serif\" font-size=\"14.00\">course</text>\n",
1925
+ "</g>\n",
1926
+ "</g>\n",
1927
+ "</svg>\n"
1928
+ ]
1929
+ },
1930
+ "metadata": {},
1931
+ "output_type": "display_data"
1932
+ }
1933
+ ],
1934
+ "source": [
1935
+ "var algebraName = await j.Fact(new CourseName(algebra, \"Algebra\", new CourseName[0]));\n",
1936
+ "var geometryName = await j.Fact(new CourseName(geometry, \"Geometry\", new CourseName[0]));\n",
1937
+ "var trigonometryName = await j.Fact(new CourseName(trigonometry, \"Trigonometry\", new CourseName[0]));\n",
1938
+ "var calculusName = await j.Fact(new CourseName(calculus, \"Calculus\", new CourseName[0]));\n",
1939
+ "\n",
1940
+ "Renderer.RenderFacts(algebraName, geometryName, trigonometryName, calculusName)"
1941
+ ]
1942
+ },
1943
+ {
1944
+ "attachments": {},
1945
+ "cell_type": "markdown",
1946
+ "metadata": {},
1947
+ "source": [
1948
+ "Now let's write the simplest specification that returns the candidate names of each course.\n",
1949
+ "We will ignore the existential conditions that would ordinarily be part of the mutable property pattern."
1950
+ ]
1951
+ },
1952
+ {
1953
+ "cell_type": "code",
1954
+ "execution_count": 73,
1955
+ "metadata": {
1956
+ "dotnet_interactive": {
1957
+ "language": "csharp"
1958
+ },
1959
+ "polyglot_notebook": {
1960
+ "kernelName": "csharp"
1961
+ }
1962
+ },
1963
+ "outputs": [
1964
+ {
1965
+ "data": {
1966
+ "text/plain": [
1967
+ "(school: School) {\n",
1968
+ " course: Course [\n",
1969
+ " course->school: School = school\n",
1970
+ " ]\n",
1971
+ "} => {\n",
1972
+ " identifier = course.identifier\n",
1973
+ " names = {\n",
1974
+ " name: Course.Name [\n",
1975
+ " name->course: Course = course\n",
1976
+ " ]\n",
1977
+ " } => name.value\n",
1978
+ "}\n"
1979
+ ]
1980
+ },
1981
+ "metadata": {},
1982
+ "output_type": "display_data"
1983
+ }
1984
+ ],
1985
+ "source": [
1986
+ "var coursesInSchoolWithNames = Given<School>.Match((school, facts) =>\n",
1987
+ " from course in facts.OfType<Course>()\n",
1988
+ " where course.school == school\n",
1989
+ " select new {\n",
1990
+ " identifier = course.identifier,\n",
1991
+ " names =\n",
1992
+ " from name in facts.OfType<CourseName>()\n",
1993
+ " where name.course == course\n",
1994
+ " select name.value\n",
1995
+ " }\n",
1996
+ ");\n",
1997
+ "\n",
1998
+ "coursesInSchoolWithNames.ToDescriptiveString()"
1999
+ ]
2000
+ },
2001
+ {
2002
+ "cell_type": "code",
2003
+ "execution_count": 74,
2004
+ "metadata": {
2005
+ "dotnet_interactive": {
2006
+ "language": "csharp"
2007
+ },
2008
+ "polyglot_notebook": {
2009
+ "kernelName": "csharp"
2010
+ }
2011
+ },
2012
+ "outputs": [
2013
+ {
2014
+ "data": {
2015
+ "text/html": [
2016
+ "<table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ identifier = MATH 101, names = System.Linq.Enumerable+<OfTypeIterator>d__65`1[System.String] }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>identifier</td><td>MATH 101</td></tr><tr><td>names</td><td><div class=\"dni-plaintext\"><pre>[ Algebra ]</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>1</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ identifier = MATH 102, names = System.Linq.Enumerable+<OfTypeIterator>d__65`1[System.String] }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>identifier</td><td>MATH 102</td></tr><tr><td>names</td><td><div class=\"dni-plaintext\"><pre>[ Geometry ]</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>2</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ identifier = MATH 201, names = System.Linq.Enumerable+<OfTypeIterator>d__65`1[System.String] }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>identifier</td><td>MATH 201</td></tr><tr><td>names</td><td><div class=\"dni-plaintext\"><pre>[ Trigonometry ]</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>3</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ identifier = MATH 301, names = System.Linq.Enumerable+<OfTypeIterator>d__65`1[System.String] }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>identifier</td><td>MATH 301</td></tr><tr><td>names</td><td><div class=\"dni-plaintext\"><pre>[ Calculus ]</pre></div></td></tr></tbody></table></div></details></td></tr></tbody></table><style>\r\n",
2017
+ ".dni-code-hint {\r\n",
2018
+ " font-style: italic;\r\n",
2019
+ " overflow: hidden;\r\n",
2020
+ " white-space: nowrap;\r\n",
2021
+ "}\r\n",
2022
+ ".dni-treeview {\r\n",
2023
+ " white-space: nowrap;\r\n",
2024
+ "}\r\n",
2025
+ ".dni-treeview td {\r\n",
2026
+ " vertical-align: top;\r\n",
2027
+ " text-align: start;\r\n",
2028
+ "}\r\n",
2029
+ "details.dni-treeview {\r\n",
2030
+ " padding-left: 1em;\r\n",
2031
+ "}\r\n",
2032
+ "table td {\r\n",
2033
+ " text-align: start;\r\n",
2034
+ "}\r\n",
2035
+ "table tr { \r\n",
2036
+ " vertical-align: top; \r\n",
2037
+ " margin: 0em 0px;\r\n",
2038
+ "}\r\n",
2039
+ "table tr td pre \r\n",
2040
+ "{ \r\n",
2041
+ " vertical-align: top !important; \r\n",
2042
+ " margin: 0em 0px !important;\r\n",
2043
+ "} \r\n",
2044
+ "table th {\r\n",
2045
+ " text-align: start;\r\n",
2046
+ "}\r\n",
2047
+ "</style>"
2048
+ ]
2049
+ },
2050
+ "metadata": {},
2051
+ "output_type": "display_data"
2052
+ }
2053
+ ],
2054
+ "source": [
2055
+ "var catalogWithNames = await j.Query(lps, coursesInSchoolWithNames);\n",
2056
+ "\n",
2057
+ "catalogWithNames"
2058
+ ]
2059
+ },
2060
+ {
2061
+ "attachments": {},
2062
+ "cell_type": "markdown",
2063
+ "metadata": {},
2064
+ "source": [
2065
+ "### Feeds from Child Specifications\n",
2066
+ "\n",
2067
+ "Child specifications result in additional feeds.\n",
2068
+ "The example above will produce two feeds: one for the courses and one for the names."
2069
+ ]
2070
+ },
2071
+ {
2072
+ "cell_type": "code",
2073
+ "execution_count": 75,
2074
+ "metadata": {
2075
+ "dotnet_interactive": {
2076
+ "language": "csharp"
2077
+ },
2078
+ "polyglot_notebook": {
2079
+ "kernelName": "csharp"
2080
+ }
2081
+ },
2082
+ "outputs": [
2083
+ {
2084
+ "data": {
2085
+ "text/plain": [
2086
+ "(school: School) {\n",
2087
+ " course: Course [\n",
2088
+ " course->school: School = school\n",
2089
+ " ]\n",
2090
+ "} => course\n"
2091
+ ]
2092
+ },
2093
+ "metadata": {},
2094
+ "output_type": "display_data"
2095
+ }
2096
+ ],
2097
+ "source": [
2098
+ "Given<School>.Match((school, facts) =>\n",
2099
+ " from course in facts.OfType<Course>()\n",
2100
+ " where course.school == school\n",
2101
+ " select course\n",
2102
+ ").ToDescriptiveString()"
2103
+ ]
2104
+ },
2105
+ {
2106
+ "cell_type": "code",
2107
+ "execution_count": 76,
2108
+ "metadata": {
2109
+ "dotnet_interactive": {
2110
+ "language": "csharp"
2111
+ },
2112
+ "polyglot_notebook": {
2113
+ "kernelName": "csharp"
2114
+ }
2115
+ },
2116
+ "outputs": [
2117
+ {
2118
+ "data": {
2119
+ "text/plain": [
2120
+ "(school: School) {\n",
2121
+ " course: Course [\n",
2122
+ " course->school: School = school\n",
2123
+ " ]\n",
2124
+ " name: Course.Name [\n",
2125
+ " name->course: Course = course\n",
2126
+ " ]\n",
2127
+ "} => {\n",
2128
+ " course = course\n",
2129
+ " name = name\n",
2130
+ "}\n"
2131
+ ]
2132
+ },
2133
+ "metadata": {},
2134
+ "output_type": "display_data"
2135
+ }
2136
+ ],
2137
+ "source": [
2138
+ "Given<School>.Match((school, facts) =>\n",
2139
+ " from course in facts.OfType<Course>()\n",
2140
+ " where course.school == school\n",
2141
+ " from name in facts.OfType<CourseName>()\n",
2142
+ " where name.course == course\n",
2143
+ " select new { course, name }\n",
2144
+ ").ToDescriptiveString()"
2145
+ ]
2146
+ },
2147
+ {
2148
+ "attachments": {},
2149
+ "cell_type": "markdown",
2150
+ "metadata": {},
2151
+ "source": [
2152
+ "The SQL for these two feeds is produced as we have seen before.\n",
2153
+ "Once the client has the evidence from both feeds, it can execute the result queries.\n",
2154
+ "\n",
2155
+ "### Results from Child Specifications\n",
2156
+ "\n",
2157
+ "To produce results from child specifications, Jinaga combines the results of several queries.\n",
2158
+ "The first query produces top-level results.\n",
2159
+ "Each subsequent query produces child results.\n",
2160
+ "A *result composer* splits the child results into lists, one for each parent result.\n",
2161
+ "\n",
2162
+ "In the example described, the first query produces the course results."
2163
+ ]
2164
+ },
2165
+ {
2166
+ "cell_type": "code",
2167
+ "execution_count": 77,
2168
+ "metadata": {
2169
+ "dotnet_interactive": {
2170
+ "language": "csharp"
2171
+ },
2172
+ "polyglot_notebook": {
2173
+ "kernelName": "csharp"
2174
+ }
2175
+ },
2176
+ "outputs": [
2177
+ {
2178
+ "data": {
2179
+ "text/plain": [
2180
+ "(school: School) {\n",
2181
+ " course: Course [\n",
2182
+ " course->school: School = school\n",
2183
+ " ]\n",
2184
+ "} => {\n",
2185
+ " identifier = course.identifier\n",
2186
+ "}\n"
2187
+ ]
2188
+ },
2189
+ "metadata": {},
2190
+ "output_type": "display_data"
2191
+ }
2192
+ ],
2193
+ "source": [
2194
+ "Given<School>.Match((school, facts) =>\n",
2195
+ " from course in facts.OfType<Course>()\n",
2196
+ " where course.school == school\n",
2197
+ " select new { identifier = course.identifier }\n",
2198
+ ").ToDescriptiveString()"
2199
+ ]
2200
+ },
2201
+ {
2202
+ "attachments": {},
2203
+ "cell_type": "markdown",
2204
+ "metadata": {},
2205
+ "source": [
2206
+ "The second query produces the values of all course names."
2207
+ ]
2208
+ },
2209
+ {
2210
+ "cell_type": "code",
2211
+ "execution_count": 79,
2212
+ "metadata": {
2213
+ "dotnet_interactive": {
2214
+ "language": "csharp"
2215
+ },
2216
+ "polyglot_notebook": {
2217
+ "kernelName": "csharp"
2218
+ }
2219
+ },
2220
+ "outputs": [
2221
+ {
2222
+ "data": {
2223
+ "text/plain": [
2224
+ "(school: School) {\n",
2225
+ " course: Course [\n",
2226
+ " course->school: School = school\n",
2227
+ " ]\n",
2228
+ " name: Course.Name [\n",
2229
+ " name->course: Course = course\n",
2230
+ " ]\n",
2231
+ "} => {\n",
2232
+ " name = name.value\n",
2233
+ "}\n"
2234
+ ]
2235
+ },
2236
+ "metadata": {},
2237
+ "output_type": "display_data"
2238
+ }
2239
+ ],
2240
+ "source": [
2241
+ "var namesOfCourses = Given<School>.Match((school, facts) =>\n",
2242
+ " from course in facts.OfType<Course>()\n",
2243
+ " where course.school == school\n",
2244
+ " from name in facts.OfType<CourseName>()\n",
2245
+ " where name.course == course\n",
2246
+ " select new { name = name.value }\n",
2247
+ ");\n",
2248
+ "\n",
2249
+ "namesOfCourses.ToDescriptiveString()"
2250
+ ]
2251
+ },
2252
+ {
2253
+ "cell_type": "code",
2254
+ "execution_count": 80,
2255
+ "metadata": {
2256
+ "dotnet_interactive": {
2257
+ "language": "csharp"
2258
+ },
2259
+ "polyglot_notebook": {
2260
+ "kernelName": "csharp"
2261
+ }
2262
+ },
2263
+ "outputs": [
2264
+ {
2265
+ "data": {
2266
+ "text/html": [
2267
+ "<table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ name = Algebra }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>Algebra</td></tr></tbody></table></div></details></td></tr><tr><td>1</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ name = Geometry }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>Geometry</td></tr></tbody></table></div></details></td></tr><tr><td>2</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ name = Trigonometry }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>Trigonometry</td></tr></tbody></table></div></details></td></tr><tr><td>3</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ name = Calculus }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>name</td><td>Calculus</td></tr></tbody></table></div></details></td></tr></tbody></table><style>\r\n",
2268
+ ".dni-code-hint {\r\n",
2269
+ " font-style: italic;\r\n",
2270
+ " overflow: hidden;\r\n",
2271
+ " white-space: nowrap;\r\n",
2272
+ "}\r\n",
2273
+ ".dni-treeview {\r\n",
2274
+ " white-space: nowrap;\r\n",
2275
+ "}\r\n",
2276
+ ".dni-treeview td {\r\n",
2277
+ " vertical-align: top;\r\n",
2278
+ " text-align: start;\r\n",
2279
+ "}\r\n",
2280
+ "details.dni-treeview {\r\n",
2281
+ " padding-left: 1em;\r\n",
2282
+ "}\r\n",
2283
+ "table td {\r\n",
2284
+ " text-align: start;\r\n",
2285
+ "}\r\n",
2286
+ "table tr { \r\n",
2287
+ " vertical-align: top; \r\n",
2288
+ " margin: 0em 0px;\r\n",
2289
+ "}\r\n",
2290
+ "table tr td pre \r\n",
2291
+ "{ \r\n",
2292
+ " vertical-align: top !important; \r\n",
2293
+ " margin: 0em 0px !important;\r\n",
2294
+ "} \r\n",
2295
+ "table th {\r\n",
2296
+ " text-align: start;\r\n",
2297
+ "}\r\n",
2298
+ "</style>"
2299
+ ]
2300
+ },
2301
+ "metadata": {},
2302
+ "output_type": "display_data"
2303
+ }
2304
+ ],
2305
+ "source": [
2306
+ "var names = await j.Query(lps, namesOfCourses);\n",
2307
+ "\n",
2308
+ "names"
2309
+ ]
2310
+ },
2311
+ {
2312
+ "attachments": {},
2313
+ "cell_type": "markdown",
2314
+ "metadata": {},
2315
+ "source": [
2316
+ "The SQL that these two specifications produce is nothing new.\n",
2317
+ "We have seen how this SQL is produced in previous examples.\n",
2318
+ "What is new is how the result composer splits the name results into lists.\n",
2319
+ "\n",
2320
+ "The result composer uses the IDs of the facts in the tuple to associate child results with their parents.\n",
2321
+ "It then subdivides the list of child results by parent ID, and adds each list to the corresponding parent result.\n",
2322
+ "The result is the object that we saw above."
2323
+ ]
2324
+ },
2325
+ {
2326
+ "cell_type": "code",
2327
+ "execution_count": 81,
2328
+ "metadata": {
2329
+ "dotnet_interactive": {
2330
+ "language": "csharp"
2331
+ },
2332
+ "polyglot_notebook": {
2333
+ "kernelName": "csharp"
2334
+ }
2335
+ },
2336
+ "outputs": [
2337
+ {
2338
+ "data": {
2339
+ "text/html": [
2340
+ "<table><thead><tr><th><i>index</i></th><th>value</th></tr></thead><tbody><tr><td>0</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ identifier = MATH 101, names = System.Linq.Enumerable+<OfTypeIterator>d__65`1[System.String] }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>identifier</td><td>MATH 101</td></tr><tr><td>names</td><td><div class=\"dni-plaintext\"><pre>[ Algebra ]</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>1</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ identifier = MATH 102, names = System.Linq.Enumerable+<OfTypeIterator>d__65`1[System.String] }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>identifier</td><td>MATH 102</td></tr><tr><td>names</td><td><div class=\"dni-plaintext\"><pre>[ Geometry ]</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>2</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ identifier = MATH 201, names = System.Linq.Enumerable+<OfTypeIterator>d__65`1[System.String] }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>identifier</td><td>MATH 201</td></tr><tr><td>names</td><td><div class=\"dni-plaintext\"><pre>[ Trigonometry ]</pre></div></td></tr></tbody></table></div></details></td></tr><tr><td>3</td><td><details class=\"dni-treeview\"><summary><span class=\"dni-code-hint\"><code>{ identifier = MATH 301, names = System.Linq.Enumerable+<OfTypeIterator>d__65`1[System.String] }</code></span></summary><div><table><thead><tr></tr></thead><tbody><tr><td>identifier</td><td>MATH 301</td></tr><tr><td>names</td><td><div class=\"dni-plaintext\"><pre>[ Calculus ]</pre></div></td></tr></tbody></table></div></details></td></tr></tbody></table><style>\r\n",
2341
+ ".dni-code-hint {\r\n",
2342
+ " font-style: italic;\r\n",
2343
+ " overflow: hidden;\r\n",
2344
+ " white-space: nowrap;\r\n",
2345
+ "}\r\n",
2346
+ ".dni-treeview {\r\n",
2347
+ " white-space: nowrap;\r\n",
2348
+ "}\r\n",
2349
+ ".dni-treeview td {\r\n",
2350
+ " vertical-align: top;\r\n",
2351
+ " text-align: start;\r\n",
2352
+ "}\r\n",
2353
+ "details.dni-treeview {\r\n",
2354
+ " padding-left: 1em;\r\n",
2355
+ "}\r\n",
2356
+ "table td {\r\n",
2357
+ " text-align: start;\r\n",
2358
+ "}\r\n",
2359
+ "table tr { \r\n",
2360
+ " vertical-align: top; \r\n",
2361
+ " margin: 0em 0px;\r\n",
2362
+ "}\r\n",
2363
+ "table tr td pre \r\n",
2364
+ "{ \r\n",
2365
+ " vertical-align: top !important; \r\n",
2366
+ " margin: 0em 0px !important;\r\n",
2367
+ "} \r\n",
2368
+ "table th {\r\n",
2369
+ " text-align: start;\r\n",
2370
+ "}\r\n",
2371
+ "</style>"
2372
+ ]
2373
+ },
2374
+ "metadata": {},
2375
+ "output_type": "display_data"
2376
+ }
2377
+ ],
2378
+ "source": [
2379
+ "catalogWithNames"
2380
+ ]
2381
+ },
2382
+ {
2383
+ "attachments": {},
2384
+ "cell_type": "markdown",
2385
+ "metadata": {},
2386
+ "source": [
2387
+ "## Conclusion\n",
2388
+ "\n",
2389
+ "Presented here are all of the rules that Jinaga uses to generate SQL from specifications.\n",
2390
+ "These rules are combined to handle specifications of any complexity.\n",
2391
+ "\n",
2392
+ "Jinaga produces two kinds of SQL: feeds and results.\n",
2393
+ "Feeds are used to keep the client up to date with the latest changes.\n",
2394
+ "Results are used to answer questions about the current state of the system.\n",
2395
+ "\n",
2396
+ "Feed queries are executed on the server.\n",
2397
+ "The client uses a bookmark to fetch pages of tuples from each feed.\n",
2398
+ "It stores those facts in its own database.\n",
2399
+ "Then it executes result queries and composes the results for the application."
2400
+ ]
2401
+ }
2402
+ ],
2403
+ "metadata": {
2404
+ "kernelspec": {
2405
+ "display_name": ".NET (C#)",
2406
+ "language": "C#",
2407
+ "name": ".net-csharp"
2408
+ },
2409
+ "language_info": {
2410
+ "name": "polyglot-notebook"
2411
+ },
2412
+ "polyglot_notebook": {
2413
+ "kernelInfo": {
2414
+ "defaultKernelName": "csharp",
2415
+ "items": [
2416
+ {
2417
+ "aliases": [],
2418
+ "languageName": "csharp",
2419
+ "name": "csharp"
2420
+ }
2421
+ ]
2422
+ }
2423
+ }
2424
+ },
2425
+ "nbformat": 4,
2426
+ "nbformat_minor": 2
2427
+ }