Skip to main content
An ontology describes the concepts of a domain (its classes) and how they relate. gligabao can load ontologies written in OWL, update them (merge into or replace them), and keep them in the graph database next to the memory graph. This is initial support: ontologies are stored and tracked, but remember and query do not use them yet.

POST /v1/gb/ontologies

Loads an ontology under a name.
Response 201 with the metadata of the ontology:
Errors are {"error": "..."}. It makes no LLM call. Example, loading the bundled ontology:

PUT /v1/gb/ontologies/{name}

Updates the ontology stored under name with a new document. By default the document is merged into the stored ontology; with "replace": true it replaces it.
name comes from the path. The other fields are as for POST, plus: Only the owner’s ontology can be updated. Response 200 with the new metadata. The ontology keeps its id, user_id, name and created_at; updated_at is set to now. Statuses are those of POST, except that there is no 409, and 404 means the user has no ontology with that name. An update never creates an ontology; use POST for that.
Either way, the update is all or nothing. The classes and edges are changed in one transaction, so readers never see a mix of old and new, and if the update fails (an unparseable document, a database error) the stored ontology is left exactly as it was.

Merging (the default)

The document is merged into the stored ontology. A merge can add and correct things, but never deletes anything. Identity. What counts as “the same” decides what gets overwritten:
  • A class is identified by its IRI. Two classes with the same IRI are the same class, whatever their name, label or comment say.
  • An edge is identified by its source class IRI, its target class IRI, its kind and its property IRI (for the subclass_of, equivalent_class and disjoint_with kinds, the RDFS/OWL predicate; see How OWL is mapped). So Trade -hasBuyer-> Buyer is one edge, and changing its label in the document does not make a second edge. Two properties with different IRIs between the same two classes are two edges.
Rules, applied to every class and edge of the document: Why mentioned classes are not overwritten: a fragment such as “property tradedOn from Asset to Market” has to name its endpoints, but it says nothing about them. If naming a class overwrote it, such a fragment would erase the stored label and comment of Asset and Market. So a document describes a class only by declaring it. Overwriting changes the stored row in place and keeps its ID, so everything attached to a class (the edges that start or end at it) stays attached. Metadata after a merge:
  • classes, properties and edges describe the ontology after the merge (the totals of what is stored), not the size of the document.
  • size_bytes and sha256 describe the document of this update, the last one applied, not the merged whole. They identify what was last sent. updated_at is set.
  • iri and title (from the document’s owl:Ontology, if it has one) overwrite the stored ones; if the document has none, the stored ones are kept, because a fragment usually has no header.
  • description: the request’s description if given; otherwise the document’s rdfs:comment; otherwise the stored one is kept.
Merging the same document twice gives the same ontology as merging it once. A merge cannot remove a class or an edge, nor clear one of its properties by omitting it (an omitted edge is kept as it is; to blank a label, send the edge with an empty label). To remove things, use replace. Example. The stored ontology has the classes Asset and Market (with labels and comments) and the edge Asset -tradedOn-> Market (label “traded on”, inverse listsAsset). Merging this fragment:
adds the class Stock and the edge Stock -subclass_of-> Security; overwrites the edge Asset -tradedOn-> Market, which now has the label “trades on”, no comment and no inverse; leaves Asset, Market and Security as they were (they are only mentioned); and keeps everything else.

Replacing (replace: true)

The stored ontology is thrown away and the document becomes the ontology. Every stored class and edge that is not in the document is deleted, and the metadata is recomputed from the document alone, as if it had just been loaded with POST: the counts are those of the document, and description, iri and title are the document’s (the old ones are dropped, so an omitted description becomes the document’s rdfs:comment, or empty). Only the identity (id, user_id, name, created_at) is kept. Use it to remove things, or to load a new version of the whole ontology.

What is stored

Loading an ontology writes to two places. If either fails, nothing is kept and the name stays free. Updating writes to the same two places and, on failure, keeps the previous version. Within an ontology a class is identified by its IRI and an edge by (source, target, kind, IRI); this is what the unique constraints below enforce, and what a merge uses to decide what already exists. Metadata, in the relational database. One row in gb_ontologies per ontology: id, user_id, name, description, iri and title (from the document’s owl:Ontology, if any), format, size_bytes, sha256 (of the loaded document, so you can tell which version was loaded), the counts classes, properties and edges, created_at and updated_at (equal until the first update). UNIQUE (user_id, name). The ontology itself, in the graph database. It is a small graph, separate from the dataset graph (gb_entities, gb_edges), and written in one transaction: The graph rows reference the metadata row with ON DELETE CASCADE. Like everything else, the SQL lives in the backends (internal/db/relational/*/db_ontology.go, internal/db/graph/*/db_ontology.go), for SQLite and PostgreSQL.

How OWL is mapped

The document must be OWL in RDF/XML. The parser (internal/ontology) is a small RDF/XML reader plus an interpretation of the OWL terms below; it has no external dependencies. In every edge, iri is the IRI of the property (or, for the first three rows, of the RDFS/OWL predicate). Within an ontology, classes are identified by IRI; name is only the readable local name. Not stored: anything else, in particular restrictions and other anonymous class expressions, owl:intersectionOf, datatype and annotation properties, individuals, rdfs:subPropertyOf, properties missing a domain or a range, and the built-in classes (owl:Thing, …). Documents using them still load; those parts are skipped. A document that yields no class at all is rejected with 400.

The market ontology

build/ontology/market.owl is a basic, general ontology of markets with the 20 most important concepts: Market, Exchange, MarketParticipant, Buyer, Seller, Intermediary, Regulator, Asset, Commodity, Security, Currency, Derivative, Order, Trade, Price, Supply, Demand, MarketIndex, Regulation and Risk. Exchange is a kind of Market; Buyer, Seller and Intermediary are kinds of MarketParticipant; Commodity, Security, Currency and Derivative are kinds of Asset. The other relationships are object properties, for example Trade -hasBuyer-> Buyer, Order -placedBy-> MarketParticipant, Price -denominatedIn-> Currency, Derivative -underlying-> Asset, Regulator -regulates-> Market, and Asset -tradedOn-> Market (inverse of Market -listsAsset-> Asset). Add further ontologies to the build/ontology directory the same way.

Limits

  • There is no endpoint yet to list, read or delete ontologies.
  • A merge cannot delete a class or an edge; replace the ontology to do that.
  • Updating changes the stored ontology in place; there is no version history beyond sha256 (of the last document) and updated_at.
  • Only RDF/XML is read (no Turtle, JSON-LD, …), and there is no reasoning or consistency checking.
  • Ontologies are not connected to datasets, and remember and query ignore them.