PT EN
Back to site

Multi-source context

A DATTA context can bring together a graph, search indices, analytical tables and event streams, and the whole platform then sees that set as a single domain. Real data rarely lives in a single source, but until now each context pointed to exactly one graph database — combining sources required manual workarounds. With the multi-source model, you declare in the context registration as many heterogeneous sources as you need (Neo4j + OpenSearch + Trino + Kafka), and search, chat, screening and dashboards start considering all of them.


Concept

Before the multi-source model, "context" was a synonym for "domain" and for a single graph database (a 1:1:1 relation). Now a context can reference several sources of different types. Examples:

ContextNeo4j databasesSearch indicesTrino schemasKafka topics
Processosprocessos, cpc, cpp, cdcdattaprocessos*——
Assistencia Socialassistencia-social, datta-ontologydattaassistencia*—datta.cdc.assistencia.*
CNPJcnpjdattacnpj*iceberg.public.cnpj_eventos—

Where to configure

In SistemaContextos, when creating or editing a context, the form includes the "Fontes de dados deste contexto" block, with four collapsible sections:

  1. Neo4j databases — the graph databases discovered by the platform.
  2. OpenSearch indices — the available datta_* indices (the system's internal indices are already filtered out).
  3. Trino tables/schemas — shown at the catalog.schema level.
  4. Kafka topics — the non-internal topics.

Each section has a search box to filter and a checkbox per item. For OpenSearch there is also an extra field where you can enter a manual pattern (e.g. datta_meucontexto_*). The counter at the top summarizes the selection: "X fonte(s) selecionada(s) (Y Neo4j, Z OS, W Trino, V Kafka)".

The ingestion index follows the convention. The context's primary OpenSearch source defines where ingestion writes — and the platform refuses to save, with a message in Portuguese, if the value diverges from the datta_{normalized name} convention. The reason: ingestion always writes to the convention index, so a custom pattern would make documents "disappear" from search. Leave the field blank (or select the convention index) when saving. Additional (non-primary) OpenSearch sources remain free for multi-source composition.

An index created from the form is born with vector search. The button that creates a new source creates the index before the context itself — and an index's vector-search setting (index.knn) only counts if it comes at creation; it cannot be turned on later. Since 2026-09-24 the index created there is born with it. An index created earlier by the same button accepts documents, but none of them shows up in semantic search; the fix is the reindex in the OpenSearch operations runbook (section on indices without vector search), and with the index empty it is enough to recreate it.


Worked example — adding an analytical source to a context

The "CNPJ" context already has the graph and search, and now the team wants to cross-reference the corporate-events table in Trino:

  1. In SistemaContextos, select "CNPJ" and open it for editing.
  2. In the "Fontes de dados deste contexto" block, open the Tabelas/Schemas Trino section and search for "cnpj".
  3. Check iceberg.public.cnpj_eventos — the counter now includes the new source.
  4. Save. Within about 2 minutes the platform's queries start considering the context's new source.

Legacy contexts migrate on their own

Contexts created before the multi-source model have no source list in their registration. When they are loaded, the platform populates the list synthetically:

  • one Neo4j entry from the graph database already configured (role "primary");
  • one OpenSearch entry from the index pattern already configured (role "primary").

Important: that migration happens in memory only. The registration is only rewritten when an administrator saves any context (creating a new one or editing an existing one). This makes rollback trivial — just go back to the previous version of the platform and the original configuration is read intact.

Forcing the migration to persist

Edit any context through the interface and click Salvar. The registration is rewritten with the sources populated for all contexts loaded in memory.


How changes propagate

After editing a context's sources, the platform's modules refresh their view in short windows:

ModuleWindowNote
Chat / Copilotup to ~60 slifetime of the context settings cache
Graph query routingup to ~2 minre-read interval, adjustable via DATTA_GRAPH_DOMAIN_REFRESH_MS

Nothing needs restarting: wait for the window and repeat the query. To speed it up, the platform exposes a source-aggregator invalidation resource used by integrations — see the API reference.

If you need to confirm the behaviour, the platform's log records show: the migration of legacy contexts on load, the registration of the context → graph database mappings when routing starts up, and the source aggregator's cache hits.


Current version limitations

  • No external Neo4j address. The multiple graph databases live in the platform's primary cluster; pointing to an external Neo4j by its own address is planned evolution.
  • Trino at schema level. Selection is by catalog.schema; tables are resolved dynamically by Trino when referenced in SIQL.
  • Kafka topics are listed and registered in the context configuration, but real-time consumption by the search screens is planned evolution.
  • Re-read every ~2 min for graph routing. Edits are rare and do not justify a dedicated event channel; worst case, a new Neo4j source becomes visible to routing within 2 minutes.
  • No "Atualizar dados" by URL. A context with more than one Neo4j source (one database per origin) has no single URL standing for all its data: the Atualizar button in SistemaContextos opens the modal with "Atualizar agora" and "Agendar" disabled and the notice "the context has more than one data source". Each origin is refreshed by its own DATTA Extract load — see managing contexts.

Procedural code database

What it is

Besides the context's data sources (the case-records graph, search indices, etc.), a context can declare a separate graph database holding the procedural code — the law articles and the decisions related to each article. Typical examples: cpc (Code of Civil Procedure), cpp, cdc.

That database feeds the Appeal copilot: when drafting an appeal brief, it identifies the articles cited in the case text and enriches the instruction sent to the model with each article's caput and up to 4 recent rulings per article (court, rapporteur, date, headnote). The brief comes out with real legal grounding, instead of the model "remembering" the article's content.

The field is optional: it stays empty when the context does not generate appeals (e.g. a social assistance or CNPJ context). In a court case context, empty does not mean "not applicable": that context's "Gerar recurso" starts querying the CPC (section below).

Where to edit

In SistemaContextos, when creating or editing a context, fill in the text field "Base do código processual (opcional)":

  • Examples (placeholder): cpc, cpp, cdc.
  • Help text: "Banco Neo4j com os artigos e a jurisprudência do código que rege os processos deste contexto (ex.: cpc, cpp, clt) — é o que o “Gerar recurso” consulta. Em branco, o recurso de um contexto de processo judicial usa o Código de Processo Civil (banco “cpc”); deixe em branco só quando o contexto não gera recurso." (the database holding the articles and case law of the code that governs this context's cases; blank means the CPC for a court case context).
  • Expected value: the name of the graph database containing the articles and the case law of the applicable code. It is a context database, not a system one — which is why it carries no datta- prefix.

Write behaviour is as follows:

  • The name is normalised by the same rules as the context's own database (lowercase, no accents, spaces or underscores): CLT is stored as clt. Whatever is not left as a valid database name is refused, and so are the system databases (datta-*, neo4j, system) — the message names the field and says what to enter. Before, the text was stored raw, with uppercase letters or underscores that Neo4j does not accept in a database name.
  • Normalising guarantees a valid name, not an existing database: whoever types the code's name instead of the database's name (Código de Processo Civil becomes codigodeprocessocivil) stores a database that does not exist, and that context's "Gerar recurso" comes out without articles and case law. That is why saving checks Neo4j's list of databases and, when the database is not there, returns a warning (not a refusal — the database may be loaded later), with the stored name and, when it differs, the text you typed. If the list of databases cannot be read at that moment, nothing is warned: saving does not wait for Neo4j nor state what it did not check.
  • On creation, the database is only set when the registration carries a non-empty value.
  • On update, an empty value clears the field; the field's absence from the submitted registration preserves the context's current value.
  • On read, the field appears only when filled in (it is omitted when empty).
  • Saving a court case context without the database is not refused, but returns a warning in Portuguese: on creation it shows in the "Contexto criado" card, under "Ficou pendente:"; on edit, in a notification. The nonexistent-database warning (above) shows in the same places.
  • In an edit that also changes the context's internal name, an invalid database is refused before the rename: the context is not renamed and no other change is applied.

The same registration is reachable by integrations — see the API reference.

What happens when the field is empty

The Appeal copilot resolves the database dynamically per context, reading the field from the context settings. If the context does not define the field (or the settings are momentarily unavailable, or the appeal arrives without a domain), the copilot falls back to a documented fallback and logs a warning:

text
Contexto '{dominio}' sem 'codigoProcessualDatabase' — usando fallback '{db}'

The fallback is the cpc database, as it is the most general procedural code. It is a last resort: the normal path is always dynamic resolution, never a database name pinned in code. The appeal is still generated — what changes is where the articles and case law come from. The warning exists so the operator can fix the context registration — and, for court case contexts, the registration itself already warns on screen when the field is left empty (above).

Relation to "Regras Não Aplicáveis (Sem Contexto)"

When screening (and the copilot) cannot ground a rule for lack of the legal database, the rules come back as "without context" — displayed in the audit detail as "Regras Não Aplicáveis (Sem Contexto)" / "Itens sem contexto suficiente para análise", and the screening verdict may end up Inconclusivo.

A missing procedural code database, or one pointing to a database without articles and case law, is one of the causes of this symptom: without the database, the copilot falls back to cpc and, if the context is not civil-law, the grounding may not match the case.

When investigating many "rules without context" in a specific context, check:

  1. Is the "Base do código processual (opcional)" field filled in under SistemaContextos?
  2. Does the referenced database exist and contain articles and case law?
  3. Do the log records show the fallback warning above?

See also the case audit detail for the semantics of "without context" / "Inconclusivo" in the report.

Propagation

Like the other context settings, the procedural code database is read through a cache (the same up-to-~60 s window from the How changes propagate section above). After editing it, wait for the cache to expire — or force the invalidation — for the copilot to see the new value.


See also

  • Case audit detail
  • Roles and permissions
  • API reference