diff options
author | David Robillard <d@drobilla.net> | 2021-03-28 13:42:35 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2023-12-02 18:49:08 -0500 |
commit | d094448c095a59117febc8bd4687df071ce9759a (patch) | |
tree | 08e81a3a9a46627dc8b545c12ebf17ae51ef76f4 /doc/model.rst | |
parent | f74a7448036d6fbe3f6562aa6e87d7e7478f0341 (diff) | |
download | serd-d094448c095a59117febc8bd4687df071ce9759a.tar.gz serd-d094448c095a59117febc8bd4687df071ce9759a.tar.bz2 serd-d094448c095a59117febc8bd4687df071ce9759a.zip |
Add high-level documentation
Diffstat (limited to 'doc/model.rst')
-rw-r--r-- | doc/model.rst | 237 |
1 files changed, 237 insertions, 0 deletions
diff --git a/doc/model.rst b/doc/model.rst new file mode 100644 index 00000000..399f370b --- /dev/null +++ b/doc/model.rst @@ -0,0 +1,237 @@ +Model +===== + +.. default-domain:: c +.. highlight:: c + +A :struct:`SerdModel` is an indexed set of statements. +A model can be used to store any data set, +from a few statements (for example, a protocol message), +to an entire document, +to a database with millions of statements. + +A new model can be created with :func:`serd_model_new`: + +.. literalinclude:: overview_code.c + :start-after: begin model-new + :end-before: end model-new + :dedent: 2 + +The information to store for each statement can be controlled by passing flags. +Additional indices can also be enabled with :func:`serd_model_add_index`. +For example, to be able to quickly search by predicate, +and store a cursor for each statement, +the model can be constructed with the :enumerator:`SERD_STORE_CARETS` flag, +and an additional :enumerator:`SERD_ORDER_PSO` index can be added like so: + +.. literalinclude:: overview_code.c + :start-after: begin fancy-model-new + :end-before: end fancy-model-new + :dedent: 2 + +Accessors +--------- + +The flags set for a model can be accessed with :func:`serd_model_flags`. + +The number of statements can be accessed with :func:`serd_model_size` and :func:`serd_model_empty`: + +.. literalinclude:: overview_code.c + :start-after: begin model-size + :end-before: end model-size + :dedent: 2 + +Adding Statements +----------------- + +Statements can be added to a model with :func:`serd_model_add`: + +.. literalinclude:: overview_code.c + :start-after: begin model-add + :end-before: end model-add + :dedent: 2 + +Alternatively, :func:`serd_model_insert` can be used if you already have a statement. +For example, the first statement in one model could be added to another like so: + +.. literalinclude:: overview_code.c + :start-after: begin model-insert + :end-before: end model-insert + :dedent: 2 + +An entire range of statements can be inserted at once with :func:`serd_model_insert_statements`. +For example, all statements in one model could be copied into another like so: + +.. literalinclude:: overview_code.c + :start-after: begin model-add-range + :end-before: end model-add-range + :dedent: 2 + +Iteration +--------- + +An iterator is a reference to a particular statement in a model. +:func:`serd_model_begin` returns an iterator to the first statement in the model, +and :func:`serd_model_end` returns a sentinel that is one past the last statement in the model: + +.. literalinclude:: overview_code.c + :start-after: begin model-begin-end + :end-before: end model-begin-end + :dedent: 2 + +A cursor can be advanced to the next statement with :func:`serd_cursor_advance`, +which returns :enumerator:`SERD_FAILURE` if the iterator reached the end: + +.. literalinclude:: overview_code.c + :start-after: begin iter-next + :end-before: end iter-next + :dedent: 2 + +Iterators are dynamically allocated, +and must eventually be destroyed with :func:`serd_cursor_free`: + +.. literalinclude:: overview_code.c + :start-after: begin iter-free + :end-before: end iter-free + :dedent: 2 + +Pattern Matching +---------------- + +There are several functions that can be used to quickly find statements in the model that match a pattern. +The simplest is :func:`serd_model_ask` which checks if there is any matching statement: + +.. literalinclude:: overview_code.c + :start-after: begin model-ask + :end-before: end model-ask + :dedent: 2 + +To access the unknown fields, +an iterator to the matching statement can be found with :func:`serd_model_find` instead: + +.. literalinclude:: overview_code.c + :start-after: begin model-find + :end-before: end model-find + :dedent: 2 + +To iterate over the matching statements, +the iterator returned by :func:`serd_model_find` can be advanced. +It will reach its end when it reaches the last matching statement: + +.. literalinclude:: overview_code.c + :start-after: begin model-range + :end-before: end model-range + :dedent: 2 + + +Similar to :func:`serd_model_ask`, +:func:`serd_model_count` can be used to count the number of matching statements: + +.. literalinclude:: overview_code.c + :start-after: begin model-count + :end-before: end model-count + :dedent: 2 + +Indexing +-------- + +A model can contain several indices that use different orderings to support different kinds of queries. +For good performance, +there should be an index where the least significant fields in the ordering correspond to wildcards in the pattern +(or, in other words, one where the most significant fields in the ordering correspond to nodes given in the pattern). +The table below lists the indices that best support a kind of pattern, +where a "?" represents a wildcard in the pattern. + ++---------+--------------+ +| Pattern | Good Indices | ++=========+==============+ +| s p o | Any | ++---------+--------------+ +| s p ? | SPO, PSO | ++---------+--------------+ +| s ? o | SOP, OSP | ++---------+--------------+ +| s ? ? | SPO, SOP | ++---------+--------------+ +| ? p o | POS, OPS | ++---------+--------------+ +| ? p ? | POS, PSO | ++---------+--------------+ +| ? ? o | OSP, OPS | ++---------+--------------+ +| ? ? ? | Any | ++---------+--------------+ + +If graphs are enabled, +then statements are indexed both with and without the graph fields, +so queries with and without a graph wildcard will have similar performance. + +Since indices take up space and slow down insertion, +it is best to enable the fewest indices possible that cover the queries that will be performed. +For example, +an applications might enable just SPO and OPS order, +because they always search for specific subjects or objects, +but never for just a predicate without specifying any other field. + +Getting Values +-------------- + +Sometimes you are only interested in a single node, +and it is cumbersome to first search for a statement and then get the node from it. +A more convenient way is to use :func:`serd_model_get`. +To get a value, specify a triple pattern where exactly one of the subject, predicate, and object is a wildcard. +If a statement matches, then the node that "fills" the wildcard will be returned: + +.. literalinclude:: overview_code.c + :start-after: begin model-get + :end-before: end model-get + :dedent: 2 + +If multiple statements match the pattern, +then the matching node from an arbitrary statement is returned. +It is an error to specify more than one wildcard, excluding the graph. + +The similar :func:`serd_model_get_statement` instead returns the matching statement: + +.. literalinclude:: overview_code.c + :start-after: begin model-get-statement + :end-before: end model-get-statement + :dedent: 2 + +Erasing Statements +------------------ + +Individual statements can be erased with :func:`serd_model_erase`, +which takes a cursor: + +.. literalinclude:: overview_code.c + :start-after: begin model-erase + :end-before: end model-erase + :dedent: 2 + +The similar :func:`serd_model_erase_statements` will erase all statements in the cursor's range: + +.. literalinclude:: overview_code.c + :start-after: begin model-erase-range + :end-before: end model-erase-range + :dedent: 2 + +Lifetime +-------- + +Models are value-like and can be copied with :func:`serd_model_copy` and compared with :func:`serd_model_equals`: + +.. literalinclude:: overview_code.c + :start-after: begin model-copy + :end-before: end model-copy + :dedent: 2 + +When a model is no longer needed, it can be destroyed with :func:`serd_model_free`: + +.. literalinclude:: overview_code.c + :start-after: begin model-free + :end-before: end model-free + :dedent: 2 + +Destroying a model invalidates all nodes and statements within that model, +so care should be taken to ensure that no dangling pointers are created. |