> ## Documentation Index
> Fetch the complete documentation index at: https://docs.experio.cloud/llms.txt
> Use this file to discover all available pages before exploring further.

# Relationship attributes

> How ontology relationship attributes flow through content types, extraction, and Neo4j

## Overview

**Relationship attributes** extend edges with structured properties (similar to entity attributes on nodes).
They are defined in the **ontology** per relationship triple (source entity type, relationship type,
target entity type), configured per **content type**, extracted by the LLM as JSON on each edge, filtered
to allowed keys, and written to Neo4j as relationship properties where applicable.

## Data model

* **Ontology** defines relationships between **entity types** and optional **relationship attributes**
  (name, type, extraction defaults, enum options).
* **Content types** persist selections and overrides under `DocumentType.metadata["relations"]`.
* **Extraction JSON** uses **instance names** on edges:
  `{ "source": "<name>", "type": "<REL_TYPE>", "target": "<name>", "attributes": { ... } }`.
  That differs from schema lines in prompts, which show **entity type** labels for source/target.

## Available entities API

`GET /api/ingestion/content_types/available_entities/` returns the default ontology via
`extract_schema_structure()`, which **normalizes relationship attributes** the same way as node attributes
(stable `extraction_instructions`, enum `options`, optional `semantic_intent`).

## Prompt construction

`DocumentTypeConfig.get_document_type_config()` builds `prompt_config["relationships"]` with
`attributes` as **attribute name → extraction instruction text**.

`build_prompt()` / `build_completion_prompt()` (and paginated prompts) append those instructions per
triple so the model knows which keys may appear under `relationships[].attributes`.

## Filtering extracted attributes

`filter_relationship_attributes_by_config()` restricts keys to those allowed on the document type.

Schema triples use **entity type names** for source/target; extracted edges use **instance names**.
`relationship_key_for_schema_lookup()` resolves instance names to entity types using the extracted
`entities` list before lookup. If a triple cannot be aligned to the schema map, extracted attributes are
not blindly cleared (avoids dropping valid LLM output when matching is imperfect).

## Neo4j graph and LLM schema context

Relationship properties are reflected in Neo4j introspection and LLM-facing schema strings. Helpers live in
`experio.neo4j_graph.schema_helpers` (shared formatting and ontology filtering). Fan-out batch Cypher uses the
**variable scope clause** `CALL (triple) { ... }`, which requires **Neo4j 5.23 or later** (see Neo4j Cypher
manual: additions in 5.23). Older Neo4j versions must be upgraded for schema fan-out statistics to run.

## Related code

| Area                      | Package / module                                                             |
| ------------------------- | ---------------------------------------------------------------------------- |
| Content-type metadata     | `experio.ingestion.views.content_types`                                      |
| Prompt config and prompts | `experio.ingestion.services.document_type_config`, `experio.ingestion.utils` |
| Extraction pipeline       | `experio.ingestion.services.extraction_processor`                            |
| Neo4j schema              | `experio.neo4j_graph.schema_helpers`, `experio.neo4j_graph.api`              |
| Artifact-types UI         | `client-cn` content-types editor                                             |

## See also

* [Content Types](/admin-guide/content-types) (admin workflow)
* `server/experio/ingestion/README.md` (developer notes)
