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

# Custom Data Models

> Step-by-step guide to creating custom data models and using add_data_points

A minimal guide to creating custom data models and inserting them directly into the knowledge graph using `add_data_points`.

**Before you start:**

* Complete [Quickstart](getting-started/quickstart) to understand basic operations
* Ensure you have [LLM Providers](setup-configuration/llm-providers) configured
* Have some structured data you want to model

## What Custom Data Models Do

* Define your own Pydantic models that inherit from `DataPoint`
* Insert structured data directly into the knowledge graph without `cognify`
* Create relationships between data points programmatically
* Control exactly what gets indexed and how

## Code in Action

### Step 1: Define Your Data Model

```python theme={null}
class Person(DataPoint):
    name: str
    knows: SkipValidation[Any] = None
    # Recommended: specify which fields to index for search
    metadata: dict = {"index_fields": ["name"]}
```

Create a Pydantic model that inherits from `DataPoint`. Use `SkipValidation[Any]` for fields that will hold other DataPoints to avoid forward reference issues. **Metadata is recommended** - it tells Cognee which fields to embed and store in the vector database for search.

### Step 2: Create Data Instances

```python theme={null}
alice = Person(name="Alice")
bob = Person(name="Bob")
charlie = Person(name="Charlie")
```

Instantiate your models with the data you want to store. Each instance becomes a node in the knowledge graph.

### Step 3: Create Relationships

```python theme={null}
alice.knows = bob
# Optional: add weights and custom relationship types
bob.knows = (Edge(weight=0.9, relationship_type="friend_of"), charlie)
```

Assign DataPoint instances to fields to create edges. The field name becomes the relationship label by default. **Weights are optional** - you can use `Edge` to add weights, custom relationship types, or other metadata to your relationships.

### Step 4: Insert into Graph

```python theme={null}
await add_data_points([alice, bob, charlie])
```

This converts your DataPoint instances into nodes and edges in the knowledge graph, automatically handling the graph structure and indexing. The `name` field gets embedded and stored in the vector database for search.

## Custom Data Model Fields

When `add_data_points` walks your model, it decides field-by-field whether a value is a relationship or a plain property:

* **Edges** — a field whose value is another `DataPoint`, a `list[DataPoint]`, or an `(Edge(...), DataPoint)` / `(Edge(...), list[DataPoint])` tuple. Each referenced DataPoint becomes its own node, and the field name (or the `Edge.relationship_type`) becomes the edge label.
* **Properties** — every other value type (`str`, `int`, `float`, `bool`, **`dict`**, or a list of scalar values) is stored on the node. It is not expanded into separate nodes or edges.

Only fields listed in `metadata.index_fields` are embedded for vector search — pick a text field (like `name`) for that, since a `dict` is stored but not meaningfully searchable. See [DataPoints](/core-concepts/building-blocks/datapoints) for more on indexing.

For complex nested values, such as a list of dictionaries, prefer serializing them yourself or modeling each nested object as its own `DataPoint` when you need portable graph behavior across database backends.

### Edge Metadata Fields

`Edge` accepts the following fields, all optional:

| Field               | Type               | Description                                                                                                                                                                                                                  |
| ------------------- | ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `weight`            | `float`            | A single numeric weight for the relationship.                                                                                                                                                                                |
| `weights`           | `dict[str, float]` | Multiple named weights (e.g. `{"strength": 0.8, "confidence": 0.9}`). Each one is also stored as a separate, queryable `weight_<name>` property on the edge.                                                                 |
| `relationship_type` | `str`              | Custom relationship label. When set, it overrides the field name as the edge's relationship name.                                                                                                                            |
| `properties`        | `dict[str, Any]`   | Arbitrary custom metadata to attach to the edge. Use this for any fields not covered above.                                                                                                                                  |
| `edge_text`         | `str`              | A rich, natural-language description of the relationship that is embedded for semantic edge/triplet retrieval. When omitted, Cognee builds fallback retrieval text from the source node, relationship name, and target node. |

Use `properties` for custom edge metadata, such as `properties={"since": 2015, "context": "college"}`. Advanced users can also subclass `Edge`; subclass fields are included in the stored edge properties.

### Custom Fields and Read-Back

Use plain scalar fields when you need to keep external identifiers, labels, statuses, or other simple properties on a node. Do not add those fields to `metadata.index_fields` unless you actually want Cognee to embed them as searchable text.

```python theme={null}
from cognee.infrastructure.engine import DataPoint
from cognee.infrastructure.engine.models.DataPoint import MetaData

class Note(DataPoint):
    text: str
    external_id: str
    category: str
    metadata: MetaData = {"index_fields": ["text"]}
```

Here, `text` is embedded for semantic search, while `external_id` and `category` are stored as normal node properties.

When you have the `DataPoint` object itself, read custom fields directly:

```python theme={null}
note.external_id
note.model_dump()["category"]
```

When a search result returns the stored payload directly, custom fields on that payload are plain keys:

```python theme={null}
import cognee
from cognee import SearchType

results = await cognee.search(
    query_text="my query",
    query_type=SearchType.CHUNKS,
)

results[0]["external_id"]
```

When using `recall()`, normalized graph entries reserve `metadata` for stable provenance keys such as `data_id`, `chunk_id`, `chunk_index`, and `document_name`. Custom payload fields from the result are available on `raw`:

```python theme={null}
import cognee
from cognee import SearchType

entries = await cognee.recall(
    query_text="my query",
    query_type=SearchType.CHUNKS,
)

entries[0].raw["external_id"]
```

## Use in Custom Tasks and Pipelines

This approach is particularly useful when creating custom tasks and pipelines where you need to:

* Insert structured data programmatically
* Define specific relationships between known entities
* Control exactly what gets indexed and how
* Integrate with external data sources or APIs

You can combine this with `cognify` to extract knowledge from unstructured text, then add your own structured data on top.

## Linking DataPoints to a Dataset

When you call `add_data_points` standalone, nodes are inserted globally with no dataset association. Dataset-level [`forget()`](/core-concepts/main-operations/forget) calls will **not** remove them. To delete those unassociated DataPoints, call `prune_system()` instead of `forget(dataset=...)`.

To associate DataPoints with a dataset so that `forget(dataset=...)` can clean them up, pass a `PipelineContext` as the `ctx` argument:

```python theme={null}
from cognee.modules.pipelines.models import PipelineContext

await add_data_points(
    [alice, bob, charlie],
    ctx=PipelineContext(
        user=user,           # authenticated user object
        dataset=dataset,     # dataset object
        data_item=data_item, # source data item for provenance
    ),
)
```

When `ctx` carries all three values, each node and edge is tagged with `dataset_id` and `data_id` in the relational database. `forget(dataset=...)` then finds and removes exactly those records — nodes shared across other datasets are preserved.

When using `Task(add_data_points)` inside `cognee.run_custom_pipeline()`, the pipeline machinery builds and injects `ctx` automatically. If you write a custom task that calls `add_data_points` internally, declare `ctx` in your task signature so the pipeline forwards it:

```python theme={null}
from cognee.modules.pipelines.models import PipelineContext

async def my_custom_task(data, ctx: PipelineContext = None) -> list:
    points = build_data_points(data)
    return await add_data_points(points, ctx=ctx)  # forward ctx for dataset linking
```

## Additional examples

Additional examples about Custom data models are available on our [github](https://github.com/topoteretes/cognee/tree/main/examples/guides).

## Full Example

<Accordion title="Latest guide">
  ```python theme={null}
  import asyncio
  from typing import Any
  from pydantic import SkipValidation

  import cognee
  from cognee.infrastructure.engine import DataPoint
  from cognee.infrastructure.engine.models.Edge import Edge
  from cognee.tasks.storage import add_data_points

  class Person(DataPoint):
      name: str
      # Keep it simple for forward refs / mixed values
      knows: SkipValidation[Any] = None  # single Person or list[Person]
      # Recommended: specify which fields to index for search
      metadata: dict = {"index_fields": ["name"]}

  async def main():
      # Start clean (optional in your app)
      await cognee.forget(everything=True)

      alice = Person(name="Alice")
      bob = Person(name="Bob")
      charlie = Person(name="Charlie")

      # Create relationships - field name becomes edge label
      alice.knows = bob
      # You can also do lists: alice.knows = [bob, charlie]
      
      # Optional: add weights and custom relationship types
      bob.knows = (Edge(weight=0.9, relationship_type="friend_of"), charlie)

      await add_data_points([alice, bob, charlie])

  if __name__ == "__main__":
      asyncio.run(main())
  ```
</Accordion>

<Accordion title="Legacy guide">
  ```python theme={null}
  import asyncio
  from typing import Any
  from pydantic import SkipValidation

  import cognee
  from cognee.infrastructure.engine import DataPoint
  from cognee.infrastructure.engine.models.Edge import Edge
  from cognee.tasks.storage import add_data_points

  class Person(DataPoint):
      name: str
      # Keep it simple for forward refs / mixed values
      knows: SkipValidation[Any] = None  # single Person or list[Person]
      # Recommended: specify which fields to index for search
      metadata: dict = {"index_fields": ["name"]}

  async def main():
      # Start clean (optional in your app)
      await cognee.prune.prune_data()
      await cognee.prune.prune_system(metadata=True)

      alice = Person(name="Alice")
      bob = Person(name="Bob")
      charlie = Person(name="Charlie")

      # Create relationships - field name becomes edge label
      alice.knows = bob
      # You can also do lists: alice.knows = [bob, charlie]
      
      # Optional: add weights and custom relationship types
      bob.knows = (Edge(weight=0.9, relationship_type="friend_of"), charlie)

      await add_data_points([alice, bob, charlie])

  if __name__ == "__main__":
      asyncio.run(main())
  ```
</Accordion>

<Note>
  This example shows the complete workflow with metadata for indexing and optional edge weights. In practice, you can create complex nested models with multiple relationships and sophisticated data structures.
</Note>

<Columns cols={3}>
  <Card title="Low-Level LLM" icon="cpu" href="/guides/low-level-llm">
    Learn about direct LLM interaction
  </Card>

  <Card title="Core Concepts" icon="brain" href="/core-concepts/overview">
    Understand knowledge graph fundamentals
  </Card>

  <Card title="API Reference" icon="code" href="/api-reference/introduction">
    Explore API endpoints
  </Card>
</Columns>
