Connect Records
Relationships are the connections that link records together, creating a graph structure. They have a type (a string label you define) and a direction.
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"}
)
# 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' }
// (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"}'
# 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) |
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()
# All relationships for records matching a search
relationships = db.relationships.find(
search_query={
"labels": ["MOVIE"],
"where": {
"ACTOR": {"$relation": "STARS_IN", "country": "USA"}
}
}
)
# With pagination
page = db.relationships.find(
search_query={"labels": ["MOVIE"]},
pagination={"limit": 50, "skip": 0}
)
db.relationships.find()
const { data, total } = await db.relationships.find({
labels: ['MOVIE'],
where: {
ACTOR: { $relation: 'STARS_IN', country: 'USA' }
}
})
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 '{"where": {"sourceRecord": {"title": "Inception"}}}'
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