@@ -7,7 +7,7 @@ This tutorial is based on a jupyter notebook. You can run it locally
77This tutorial builds a simple python library for visualising TypeDB
88results as graphs using the query representation returned by the analyze
99endpoint, and optionally returned with a query when the
10- `+ include_query_structure+ ` `+ QueryOption+ ` is set. We assume the user
10+ `include_query_structure` `QueryOption` is set. We assume the user
1111is familiar with the TypeDB python driver
1212(https://github.com/typedb/typedb-driver/tree/master/python#example-usage[tutorial]).
1313It uses the python driver for interacting with TypeDB, networkx for
@@ -89,12 +89,12 @@ setup(driver, SCHEMA, DATA)
8989
9090== Background: Analyzing queries and PipelineStructure
9191
92- TypeDB 3.7.0 introduces the `+ analyze+ ` operation. Analyzing a query
92+ TypeDB 3.7.0 introduces the `analyze` operation. Analyzing a query
9393returns its pipeline structure, along with typing information for the
9494variables in each pattern.
9595
96- A pipeline is made up of `+ PipelineStage+ `s, some of which are made of
97- `+ Conjunction+ `s.
96+ A pipeline is made up of `PipelineStage`s, some of which are made of
97+ `Conjunction`s.
9898
9999[source,python]
100100----
@@ -133,7 +133,7 @@ stages
133133----
134134====
135135
136- A conjunction is a collection of `+ Constraint+ `s, which should be
136+ A conjunction is a collection of `Constraint`s, which should be
137137familiar from the TypeQL statements:
138138
139139[source,python]
@@ -156,8 +156,8 @@ class Has(Constraint, ABC):
156156----
157157
158158TypeQL statements can be seen as constraints between one or more
159- `+ ConstraintVertex+ `es - These can be type `+ Label+ `s, `+ Variable+ `s or
160- raw `+ Value+ `s.
159+ `ConstraintVertex`es - These can be type `Label`s, `Variable`s or
160+ raw `Value`s.
161161
162162
163163[source, python]
@@ -185,8 +185,8 @@ constraints
185185
186186Before we get to visualising the data returned by queries, We’ll turn
187187the constraints in our query into a query-graph and visualise it. We’ll
188- use a `+ to_query_edge+ ` function to go from a Constraint to a labelled
189- edge represented by the tuple `+ (from, label, to)+ `
188+ use a `to_query_edge` function to go from a Constraint to a labelled
189+ edge represented by the tuple `(from, label, to)`
190190
191191
192192[source, python]
@@ -219,10 +219,7 @@ query_edges
219219----
220220====
221221
222- We then use networkx and matplotlib to visualise the graph
223-
224-
225- +*In[6]:*+
222+ We then use networkx and matplotlib to visualise the graph:
226223[source, python]
227224----
228225import networkx
@@ -244,8 +241,8 @@ image:output_11_0.png[]
244241== Better formatting
245242
246243That’s the query graph, but it’s of little use to the viewer. We’ll
247- create a `+ node_style+ ` function which specifies some style attributes,
248- and a `+ draw+ ` function which uses these to draw the networkx graph
244+ create a `node_style` function which specifies some style attributes,
245+ and a `draw` function which uses these to draw the networkx graph
249246using matplotlib.
250247
251248
@@ -296,9 +293,9 @@ image:output_13_0.png[]
296293
297294== Visualising Data
298295
299- TypeDB answers are `+ ConceptRow+ `s which map variables in the queries to
296+ TypeDB answers are `ConceptRow`s which map variables in the queries to
300297concepts in the database. It has a similar interface to a python
301- dictionary. If the `+ include_query_structure+ ` `+ QueryOption+ ` is set,
298+ dictionary. If the `include_query_structure` `QueryOption` is set,
302299the pipeline structure will be returned and each row will have shared
303300access to it.
304301
@@ -312,8 +309,7 @@ class ConceptRow:
312309 def query_structure(self) -> Optional["Pipeline"] # Shared access to the pipeline structure
313310----
314311
315-
316- +*In[8]:*+
312+ Run a query:
317313[source, python]
318314----
319315with driver.transaction(DB_NAME, TransactionType.READ) as tx:
@@ -332,12 +328,12 @@ answers
332328----
333329====
334330
331+ Get the pipeline stages from it:
335332[source, python]
336333----
337334# Every answer also has a reference to the pipeline structure
338335list(answers[0].query_structure().stages())
339336----
340-
341337.Output
342338[%collapsible]
343339====
@@ -356,7 +352,7 @@ the query-graph and some subgraph in the database. An answer is thus the
356352mapping between the two graphs. To construct the graph representing the
357353answer, we simply have to substitute the concepts in the answers for the
358354variables in the query-graph. Since there are multiple answers, we take
359- the union of edges
355+ the union of edges.
360356
361357[source, python]
362358----
@@ -376,7 +372,6 @@ answers_as_data_edges = [
376372]
377373answers_as_data_edges
378374----
379-
380375.Output
381376[%collapsible]
382377====
@@ -400,10 +395,8 @@ answers_as_data_edges
400395== Visualising
401396
402397We’ll need a new node style since our vertices are now concepts rather
403- than `+ ConstraintVertex+ `es
398+ than `ConstraintVertex`es.
404399
405-
406- +*In[11]:*+
407400[source, python]
408401----
409402# We need to update our node_style
@@ -435,12 +428,11 @@ node_styles = { n: data_node_style(n) for n in nodes}
435428draw(data_edges, node_styles)
436429
437430----
438-
439-
440- +*Out[11]:*+
441- ----
442- 
443- ----
431+ .Output
432+ [%collapsible]
433+ ====
434+ image:output_20_0.png[]
435+ ====
444436
445437== Disjunctions and optionals
446438
@@ -477,8 +469,7 @@ answers
477469====
478470
479471
480-
481- We can get the constraints in the trunk:
472+ Get the constraints in the trunk:
482473[source, python]
483474----
484475pipeline = answers[0].query_structure()
@@ -502,9 +493,9 @@ trunk = list(pipeline.conjunction(trunk_id).constraints())
502493====
503494
504495
496+ Get the constraints by branch:
505497[source, python]
506498----
507- ## Get those as well:
508499branches = { branch_id: list(pipeline.conjunction(branch_id).constraints()) for branch_id in trunk[1].as_or().branches() }
509500branches
510501----
@@ -528,8 +519,8 @@ branches
528519
529520How do we know which of these branches were satisfied by each answer? If
530521we were to naively draw the constraints from all branches, we’d have
531- some absurd edges such as `+ name:John isa age+ `. Each `+ ConceptRow+ ` has
532- an `+ involved_conjunctions+ ` method which tells us exactly this.
522+ some absurd edges such as `name:John isa age`. Each `ConceptRow` has
523+ an `involved_conjunctions` method which tells us exactly this.
533524
534525
535526[source, python]
@@ -635,7 +626,7 @@ A query can contain function calls. Functions have arguments and return
635626values - all of which are concepts. Unlike a relation, the function call
636627itself is not a concept. We felt the cleanest way to visualise the
637628many-to-many constraint between return values and arguments was to
638- introduce a `+ FunctionCall+ ` vertex for each call. Two function calls
629+ introduce a `FunctionCall` vertex for each call. Two function calls
639630are the same vertex if they have the same (1) function name, (2) tuple
640631of argument concepts, and (3) tuple of returned concepts.
641632
@@ -655,26 +646,27 @@ role serving as the label of the edge.
655646
656647TypeDB allows the use of unscoped role names in links & relates
657648constraints to specify the role. e.g. For the schema below, the query
658- `+ match $r relates subject;+ ` will return both `+ sentence+ ` and
659- `+ email+ ` as answers for `+$r+ `;
649+ `match $r relates subject;` will return both `sentence` and
650+ `email` as answers for `$r `;
660651
661- ....
652+ [source, typeql]
653+ ----
662654define
663655relation sentence relates subject;
664656relation email relates subject;
665- ....
657+ ----
666658
667659Internally, TypeDB introduces an internal variable and constrains it to
668660be any of the role types with the specified name. In the structure
669- returned by the `+ analyze+ ` operation, these variables and their
670- associated names are returned as `+ NamedRole+ ` vertices. (Since the
661+ returned by the `analyze` operation, these variables and their
662+ associated names are returned as `NamedRole` vertices. (Since the
671663variable introduced is anonymous, it is unavailable in the ConceptRow.
672664Since it does not uniquely determine the type, it cannot be considered a
673665label.)
674666
675667=== Is & comparison constraints
676668
677- Since `+is+ ` constraints are always a self-edge on a vertex, we choose
669+ Since `is ` constraints are always a self-edge on a vertex, we choose
678670not to visualise it. We also skip visualising comparison constraints to
679671reduce the number of edges, though they can be useful in certain cases -
680672the comparator symbol is available from the comparison constraint.
@@ -687,12 +679,12 @@ for TypeDB studio. The essence remains the same as what we’ve covered in
687679the tutorial - Find the constraints "`involved`" in each answer, and
688680substitute in the concepts for the variables. Instead of converting a
689681constraint into query edges, and then applying the substitution to form
690- data-edges, We directly apply the substitution on the `+ Constraint+ `s to
691- obtain the corresponding `+ DataConstraint+ ` - These constrain
692- `+ DataVertex+ `es instead of `+ ConstraintVertex+ `es.
682+ data-edges, We directly apply the substitution on the `Constraint`s to
683+ obtain the corresponding `DataConstraint` - These constrain
684+ `DataVertex`es instead of `ConstraintVertex`es.
693685
694- Here’s the signature of an `+ Isa+ ` `+ DataConstraint+ ` from the library,
695- and the `+ Isa+ ` `+ Constraint+ ` from the driver API to compare against:
686+ Here’s the signature of an `Isa` `DataConstraint` from the library,
687+ and the `Isa` `Constraint` from the driver API to compare against:
696688
697689[source,python]
698690----
@@ -709,24 +701,22 @@ class Isa(Constraint, ABC):
709701 def exactness(self) -> ConstraintExactness # isa or isa!
710702----
711703
712- We introduce a `+ TypeDBAnswerConverter[OutputType]+ ` abstract class,
713- which defines methods the user has to implement - One `+ add_+ ` method
714- per data constraint (e.g. `+ add_isa(self, isa: Isa)+ `) and a
715- `+ finish(self) -> OutputType+ ` for whatever your final representation
704+ We introduce a `TypeDBAnswerConverter[OutputType]` abstract class,
705+ which defines methods the user has to implement - One `add_` method
706+ per data constraint (e.g. `add_isa(self, isa: Isa)`) and a
707+ `finish(self) -> OutputType` for whatever your final representation
716708may be.
717709
718710We provide a sample implementation -
719- `+class NetworkXBuilder(TypeDBAnswerConverter[MultiDiGraph])+ ` - which
720- builds a `+ MultiDiGraph+ ` as in this tutorial. We also provide a basic
721- `+matplotlib_visualizer+ ` for inspiration.
711+ `NetworkXBuilder` - which
712+ builds a `MultiDiGraph` as in this tutorial. We also provide a basic
713+ `MatplotlibVisualizer ` for inspiration.
722714
723715== Visualising using the library
724716
725717We recreate the previous example by implementing the
726- `+TypeDBAnswerConverter+` class provided by the library.
727-
718+ `TypeDBAnswerConverter` class provided by the library.
728719
729- +*In[18]:*+
730720[source, python]
731721----
732722from typedb_graph_utils import data_constraint, TypeDBAnswerConverter, MatplotlibVisualizer
0 commit comments