Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion dev/run_cargo_test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -69,4 +69,8 @@ if [[ -n "${PYTHONPATH_DETECTED}" ]]; then
fi
fi

exec uv run cargo test "$@"
if [[ "${COCOINDEX_SKIP_UV:-}" == "1" ]]; then
exec cargo test "$@"
else
exec uv run cargo test "$@"
fi
2 changes: 1 addition & 1 deletion docs/docs/core/flow_def.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -455,7 +455,7 @@ def demo_flow(flow_builder: cocoindex.FlowBuilder, data_scope: cocoindex.DataSco
Most time a target is created by calling `export()` method on a collector, and this `export()` call comes with configurations needed for the target, e.g. options for storage indexes.
Occasionally, you may need to specify some configurations for the target out of the context of any specific data collector.

For example, for graph database targets like `Neo4j` and `Kuzu`, you may have a data collector to export data to relationships, which will create nodes referenced by various relationships in turn.
For example, for graph database targets like `Neo4j` and `Ladybug`, you may have a data collector to export data to relationships, which will create nodes referenced by various relationships in turn.
These nodes don't directly come from any specific data collector (consider relationships from different data collectors may share the same nodes).
To specify configurations for these nodes, you can *declare* spec for related node labels.

Expand Down
1 change: 1 addition & 0 deletions docs/docs/targets/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ The way to map data from a data collector to a target depends on data model of t
| [Qdrant](/docs/targets/qdrant) | Vector Database, Keyword Search |
| [LanceDB](/docs/targets/lancedb) | Vector Database, Keyword Search |
| [Neo4j](/docs/targets/neo4j) | [Property graph](#property-graph-targets) |
| [Ladybug](/docs/targets/ladybug) | [Property graph](#property-graph-targets) |

If you are looking for targets beyond here, you can always use [custom targets](/docs/custom_ops/custom_targets) as building blocks.

Expand Down
3 changes: 2 additions & 1 deletion docs/docs/targets/kuzu.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import { ExampleButton } from '../../src/components/GitHubButton';

# Kuzu (Archived)

Note:[Kuzu](https://github.com/kuzudb/kuzu) - embedded graph database is no longer maintained.
Note: [Kuzu](https://github.com/kuzudb/kuzu) - embedded graph database is no longer maintained.
For a maintained fork with an identical API, see [Ladybug](/docs/targets/ladybug).

Exports data to a [Kuzu](https://github.com/kuzudb/kuzu) graph database.

Expand Down
51 changes: 51 additions & 0 deletions docs/docs/targets/ladybug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
---
title: Ladybug
description: CocoIndex Ladybug Target
toc_max_heading_level: 4
---
import { ExampleButton } from '../../src/components/GitHubButton';

# Ladybug

Exports data to a [Ladybug](https://github.com/LadybugDB/ladybug) graph database. Ladybug is a maintained fork of Kuzu that carries forth the original vision of Kuzu, with added functionality for the lakehouse ecosystem. Just like Kuzu, Ladybug follows the structurd property graph model and functions as a fast, embedded database with a permissive (MIT) license.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

structured


## Get Started

Read [Property Graph Targets](./index.md#property-graph-targets) for more information to get started on how it works in CocoIndex.

## Spec

CocoIndex supports talking to Ladybug through its API server. You can run the server from [LadybugDB/api-server](https://github.com/LadybugDB/api-server).

The `Ladybug` target spec takes the following fields:

* `connection` ([auth reference](/docs/core/flow_def#auth-registry) to `LadybugConnectionSpec`): The connection to the Ladybug database. `LadybugConnectionSpec` has the following fields:
* `api_server_url` (`str`): The URL of the Ladybug API server, e.g. `http://localhost:8123`.
* `mapping` (`Nodes | Relationships`): The mapping from collected row to nodes or relationships of the graph. For either [nodes to export](./index.md#nodes-to-export) or [relationships to export](./index.md#relationships-to-export).

Ladybug also provides a declaration spec `LadybugDeclaration`, to configure indexing options for nodes only referenced by relationships. It has the following fields:

* `connection` (auth reference to `LadybugConnectionSpec`)
* Fields for [nodes to declare](./index.md#declare-extra-node-labels), including
* `nodes_label` (required)
* `primary_key_fields` (required)

## Ladybug API server

For running the API server locally or in Docker, follow the instructions in the Ladybug documentation.

## Python client

If you want the Ladybug Python client, install it with:

```sh
pip install real_ladybug
```

## Example

<ExampleButton
href="https://github.com/cocoindex-io/cocoindex/tree/main/examples/docs_to_knowledge_graph"
text="Docs to Knowledge Graph"
margin="16px 0 24px 0"
/>
1 change: 1 addition & 0 deletions docs/sidebars.ts
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ const sidebars: SidebarsConfig = {
'targets/qdrant',
'targets/lancedb',
'targets/neo4j',
'targets/ladybug',
'targets/kuzu',
],
},
Expand Down
23 changes: 23 additions & 0 deletions python/cocoindex/targets/_engine_builtin_specs.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,26 @@ class KuzuDeclaration(op.DeclarationSpec):
connection: AuthEntryReference[KuzuConnection]
nodes_label: str
primary_key_fields: Sequence[str]


@dataclass
class LadybugConnection:
"""Connection spec for Ladybug."""

api_server_url: str


class Ladybug(op.TargetSpec):
"""Graph storage powered by Ladybug."""

connection: AuthEntryReference[LadybugConnection]
mapping: Nodes | Relationships


class LadybugDeclaration(op.DeclarationSpec):
"""Declarations for Ladybug."""

kind = "Ladybug"
connection: AuthEntryReference[LadybugConnection]
nodes_label: str
primary_key_fields: Sequence[str]
27 changes: 27 additions & 0 deletions python/cocoindex/tests/test_targets_specs.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from cocoindex.auth_registry import AuthEntryReference, ref_auth_entry
from cocoindex.engine_object import dump_engine_object
from cocoindex.targets import Ladybug, LadybugConnection, LadybugDeclaration, Nodes


def test_ladybug_target_dump() -> None:
conn_ref: AuthEntryReference[LadybugConnection] = ref_auth_entry("ladybug")
spec = Ladybug(connection=conn_ref, mapping=Nodes(label="Document"))
dumped = dump_engine_object(spec)

assert dumped["connection"]["key"] == "ladybug"
assert dumped["mapping"]["kind"] == "Node"
assert dumped["mapping"]["label"] == "Document"


def test_ladybug_declaration_dump() -> None:
conn_ref: AuthEntryReference[LadybugConnection] = ref_auth_entry("ladybug")
decl = LadybugDeclaration(
connection=conn_ref,
nodes_label="Place",
primary_key_fields=["name"],
)
dumped = dump_engine_object(decl)

assert dumped["kind"] == "Ladybug"
assert dumped["nodes_label"] == "Place"
assert dumped["primary_key_fields"] == ["name"]
3 changes: 2 additions & 1 deletion rust/cocoindex/src/ops/registration.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ fn register_executor_factories(registry: &mut ExecutorFactoryRegistry) -> Result

targets::postgres::register(registry)?;
targets::qdrant::register(registry)?;
targets::kuzu::register(registry, reqwest_client)?;
targets::kuzu::register(registry, reqwest_client.clone())?;
targets::ladybug::register(registry, reqwest_client)?;

targets::neo4j::Factory::new().register(registry)?;

Expand Down
Loading
Loading