Connect Records
Relationships are the connections that link records together, creating a graph structure. They have a type (a string label you define), a direction, and optional user-defined edge properties.
RushDB supports three categories of relationships:
| Type | Created by | Description |
|---|---|---|
__RUSHDB__RELATION__DEFAULT__ | Auto, on import | Links parent → child records in nested JSON |
__RUSHDB__RELATION__VALUE__ | Auto, internally | Connects property nodes to record nodes (read-only) |
Custom ("STARS_IN", "DIRECTED_BY", etc.) | You | Domain-specific, manually created |
Attach Records
- Python
- TypeScript
- shell
db.records.attach()
# Single target
db.records.attach(
source=movie,
target=actor,
options={"type": "STARS_IN", "direction": "out", "properties": {"role": "lead"}}
)
# One-to-many (target list)
db.records.attach(
source=movie,
target=[actor1, actor2, actor3],
options={"type": "STARS_IN"}
)
db.records.attach()
// Single target
await db.records.attach({
source: 'movie-id-123',
target: 'actor-id-456',
options: { type: 'STARS_IN', direction: 'out', properties: { role: 'lead' } }
// (MOVIE) -[:STARS_IN]-> (ACTOR)
})
// Multiple targets
await db.records.attach({
source: 'movie-id-123',
target: ['actor-id-1', 'actor-id-2'],
options: { type: 'STARS_IN' }
})
target can be a single ID, an array of IDs, or a record instance.
POST /api/v1/records/:entityId/relationships
# Single target
curl -X POST https://api.rushdb.com/api/v1/records/$MOVIE_ID/relationships \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"targetIds": "$ACTOR_ID", "type": "STARS_IN", "direction": "out", "properties": {"role": "lead"}}'
# Multiple targets
curl -X POST https://api.rushdb.com/api/v1/records/$MOVIE_ID/relationships \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"targetIds": ["$ACTOR1_ID", "$ACTOR2_ID"], "type": "STARS_IN"}'
| Field | Type | Description |
|---|---|---|
targetIds | string | string[] | Target record ID(s) |
type | string | Relationship type |
direction | "in" | "out" | Direction from source to target (out = default) |
properties | object | Optional user-defined edge properties |
Detach Records
- Python
- TypeScript
- shell
db.records.detach()
db.records.detach(
source=movie,
target=actor,
options={"type": "STARS_IN"}
# Omit "type" to detach all relationship types
)
db.records.detach()
await db.records.detach({
source: 'movie-id-123',
target: 'actor-id-456',
options: { typeOrTypes: 'STARS_IN' } // omit to detach all types
})
PUT /api/v1/records/:entityId/relationships
curl -X PUT https://api.rushdb.com/api/v1/records/$MOVIE_ID/relationships \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{"targetIds": "$ACTOR_ID", "typeOrTypes": ["STARS_IN"]}'
List Relationships
- Python
- TypeScript
- shell
db.relationships.find()
# Relationship search: where filters edge type/properties
relationships = db.relationships.find(
search_query={
"source": {"labels": ["MOVIE"], "where": {"title": "Inception"}},
"target": {"labels": ["ACTOR"], "where": {"country": "USA"}},
"where": {"type": "STARS_IN", "role": "lead"}
}
)
# With pagination
page = db.relationships.find(
search_query={"source": {"labels": ["MOVIE"]}},
pagination={"limit": 50, "skip": 0}
)
db.relationships.find()
const { data, total } = await db.relationships.find({
source: { labels: ['MOVIE'], where: { title: 'Inception' } },
target: { labels: ['ACTOR'], where: { country: 'USA' } },
where: { type: 'STARS_IN', role: 'lead' }
})
GET /api/v1/records/:entityId/relationships
curl "https://api.rushdb.com/api/v1/records/$MOVIE_ID/relationships?limit=50" \
-H "Authorization: Bearer $RUSHDB_API_KEY"
Search relationships across all records:
POST /api/v1/relationships/search
curl -X POST https://api.rushdb.com/api/v1/relationships/search \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"source": { "labels": ["MOVIE"], "where": { "title": "Inception" } },
"target": { "labels": ["ACTOR"], "where": { "country": "USA" } },
"where": { "type": "STARS_IN", "role": "lead" }
}'
db.relationships.find().where is edge-scoped. Use source and target for record predicates. To filter records by traversing relationships, use db.records.find() with $relation as shown below.
Edge properties are stored directly on relationship edges. They are filterable through relationships.find() and summarized in ontology relationship sections, but they are not promoted to Record Property nodes and are not semantically indexed. See Relationship Properties for storage semantics, the full operator set, and modeling guidance.
Direction Reference
direction | Graph pattern |
|---|---|
out | (source) -[:TYPE]-> (target) |
in | (source) <-[:TYPE]- (target) |
Search by Relationship
Use $relation inside a where clause to filter records through their graph connections. The nested key is the label of the related record; $relation constrains which relationship edge to follow.
Any relationship type
Omit $relation to traverse any edge connecting the two labels:
- Python
- TypeScript
- shell
# Find MOVIEs connected to an ACTOR named "Timothée Chalamet" — any relationship type
result = db.records.find({
"labels": ["MOVIE"],
"where": {
"ACTOR": {
"name": "Timothée Chalamet"
}
}
})
// Find MOVIEs connected to an ACTOR named "Timothée Chalamet" — any relationship type
const { data } = await db.records.find({
labels: ['MOVIE'],
where: {
ACTOR: {
name: 'Timothée Chalamet'
}
}
})
curl -X POST https://api.rushdb.com/api/v1/records/search \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"labels": ["MOVIE"],
"where": {
"ACTOR": { "name": "Timothée Chalamet" }
}
}'
Filter by relationship type
Pass $relation as a string to restrict traversal to a specific relationship type. This produces a direction-agnostic Cypher pattern (-[:TYPE]-) that matches the edge regardless of its stored direction — use the full object form (below) to also constrain direction:
- Python
- TypeScript
- shell
result = db.records.find({
"labels": ["MOVIE"],
"where": {
"ACTOR": {
"$relation": "STARS_IN",
"country": "USA"
}
}
})
const { data } = await db.records.find({
labels: ['MOVIE'],
where: {
ACTOR: {
$relation: 'STARS_IN',
country: 'USA'
}
}
})
curl -X POST https://api.rushdb.com/api/v1/records/search \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"labels": ["MOVIE"],
"where": {
"ACTOR": { "$relation": "STARS_IN", "country": "USA" }
}
}'
Filter by relationship type and direction
Use the full object form when direction matters:
| Field | Type | Description |
|---|---|---|
type | string | Relationship type label |
direction | "in" | "out" | Edge direction relative to the root record |
- Python
- TypeScript
- shell
# Find MOVIEs where an ACTOR is connected via an incoming STARS_IN edge
# i.e. (ACTOR) -[:STARS_IN]-> (MOVIE)
result = db.records.find({
"labels": ["MOVIE"],
"where": {
"ACTOR": {
"$relation": {"type": "STARS_IN", "direction": "in"},
"country": "USA"
}
}
})
// (ACTOR) -[:STARS_IN]-> (MOVIE) — direction "in" from MOVIE's perspective
const { data } = await db.records.find({
labels: ['MOVIE'],
where: {
ACTOR: {
$relation: { type: 'STARS_IN', direction: 'in' },
country: 'USA'
}
}
})
curl -X POST https://api.rushdb.com/api/v1/records/search \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"labels": ["MOVIE"],
"where": {
"ACTOR": {
"$relation": {"type": "STARS_IN", "direction": "in"},
"country": "USA"
}
}
}'
Multi-hop traversal
Nest $relation filters to traverse multiple relationship levels in a single query:
- Python
- TypeScript
- shell
# Companies → Engineering departments → projects with budget over 10k
result = db.records.find({
"labels": ["COMPANY"],
"where": {
"name": "Acme Corp",
"DEPARTMENT": {
"$relation": "HAS_DEPARTMENT",
"name": "Engineering",
"PROJECT": {
"$relation": "HAS_PROJECT",
"budget": {"$gte": 10000}
}
}
}
})
const { data } = await db.records.find({
labels: ['COMPANY'],
where: {
name: 'Acme Corp',
DEPARTMENT: {
$relation: 'HAS_DEPARTMENT',
name: 'Engineering',
PROJECT: {
$relation: 'HAS_PROJECT',
budget: { $gte: 10000 }
}
}
}
})
curl -X POST https://api.rushdb.com/api/v1/records/search \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"labels": ["COMPANY"],
"where": {
"name": "Acme Corp",
"DEPARTMENT": {
"$relation": "HAS_DEPARTMENT",
"name": "Engineering",
"PROJECT": {
"$relation": "HAS_PROJECT",
"budget": {"$gte": 10000}
}
}
}
}'
→ For the full operator reference including $alias, $id, and logical grouping, see Where Operators — Relationship Queries.
In a Transaction
- Python
- TypeScript
- shell
tx = db.tx.begin()
try:
movie = db.records.create(
label="MOVIE", data={"title": "Dune"}, transaction=tx
)
actor = db.records.create(
label="ACTOR", data={"name": "Timothée Chalamet"}, transaction=tx
)
db.records.attach(
source=movie,
target=actor,
options={"type": "STARS_IN"},
transaction=tx
)
tx.commit()
except Exception:
tx.rollback()
raise
const tx = await db.tx.begin()
try {
const movie = await db.records.create({ label: 'MOVIE', data: { title: 'Dune' } }, tx)
const actor = await db.records.create({ label: 'ACTOR', data: { name: 'Timothée Chalamet' } }, tx)
await db.records.attach({ source: movie, target: actor, options: { type: 'STARS_IN' } }, tx)
await tx.commit()
} catch (e) {
await tx.rollback()
throw e
}
curl -X POST https://api.rushdb.com/api/v1/records/$MOVIE_ID/relationships \
-H "Authorization: Bearer $RUSHDB_API_KEY" \
-H "Content-Type: application/json" \
-H "X-Transaction-Id: $TX_ID" \
-d '{"targetIds": "$ACTOR_ID", "type": "STARS_IN"}'
Via Record Instance
- Python
- TypeScript
# Record instance methods — equivalent to db.records.attach/detach
movie.attach(
target=actor,
options={"type": "STARS_IN", "direction": "out"}
)
movie.detach(
target=actor,
options={"type": "STARS_IN"}
)
// DBRecordInstance methods — equivalent to db.records.attach/detach
await movie.attach(actor, { type: 'STARS_IN', direction: 'out' })
await movie.detach(actor, { typeOrTypes: 'STARS_IN' })
For TypeScript Models:
await MovieModel.attach({
source: 'movie-id-123',
target: 'actor-id-456',
options: { type: 'STARS_IN', direction: 'out' }
})
await MovieModel.detach({
source: 'movie-id-123',
target: 'actor-id-456',
options: { typeOrTypes: 'STARS_IN' }
})
See also
- Bulk Relationships — connect/disconnect many records at once by key match or many-to-many
- Import Data — nested JSON auto-creates relationships
- Transactions — ACID guarantees for multi-step operations