Skip to main content

Properties on Nodes and Relationships

Properties are typed key/value pairs attached to nodes and relationships. Keys are case-sensitive strings; values are any of the supported data types.

At a glance

OperationClause
Set a single keySET n.prop = value
Merge keysSET n += {k: v}
Replace the whole mapSET n = {}
Remove a keyREMOVE n.prop / SET n.prop = null
Bulk patch from paramSET n += $patch
Read a keyn.prop
Read the whole mapproperties(n)
List the keyskeys(n)
Project a subsetn {.name, .born}

Write properties

On create

CREATE (:City {
name: 'Amsterdam',
population: 918000,
location: point({latitude: 52.37, longitude: 4.89}),
founded: date('1275-10-27'),
tags: ['capital', 'port']
})

On an existing entity

MATCH (c:City {name: 'Amsterdam'})
SET c.population = 920000

Patch with a parameter map

MATCH (c:City {name: 'Amsterdam'})
SET c += $patch
RETURN c

See SET for the full rules on =, +=, and null assignment.

Read properties

Dot access

MATCH (c:City {name: 'Amsterdam'})
RETURN c.name, c.population, c.location.latitude

Bracket access (computed key)

MATCH (c:City {name: 'Amsterdam'})
WITH c, 'population' AS k
RETURN c[k]

Full map

MATCH (c:City) RETURN properties(c)

Key list

MATCH (c:City) RETURN keys(c)   -- e.g. ['name', 'population',]

Map projection

Shape an entity into a map with only the keys you want — see also Lists & Maps → Map projection:

MATCH (c:City)
RETURN c {.name, .population}
RETURN c {.*}
RETURN c {.name, density: c.population / c.area}

Update

OperationClause
Set a single propertySET n.prop = value
Merge keys into the mapSET n += {k1: v1, k2: v2}
Replace the whole mapSET n = {…}
Remove a propertyREMOVE n.prop or SET n.prop = null
MATCH (c:City {name: 'Amsterdam'})
SET c += {updated_at: datetime(), active: true}
RETURN c

Replace vs merge

SET n = {…} replaces the entire property map — every key not in the new map is dropped. SET n += {…} only adds/overwrites the listed keys. Pick += for partial updates (almost always what you want). See Troubleshooting → SET wiped my properties.

Properties on relationships

Identical shape — relationships have their own property map.

MATCH (a)-[r:KNOWS]->(b)
SET r.since = 2020, r.visibility = 'public'
RETURN r.since

Value types

Properties accept every LoraDB data type:

CategoryPages
Scalars (Null, Boolean, Integer, Float, String)Scalars
Collections (List, Map)Lists & Maps
Temporals (Date, Time, DateTime, Duration, …)Temporal
Spatial (Point)Spatial
Vector (typed fixed-dimension coordinates)Vectors

Graph types (Node, Relationship, Path) are not storable as properties — they only appear in query results.

A VECTOR can be a property value and can appear as a value inside a Map property, but a list that contains a VECTOR is rejected at write time — store many embeddings as separate nodes, not as a list. See Vectors → Storage for the exact rule.

Common patterns

Default via coalesce

MATCH (u:User)
RETURN u.name, coalesce(u.nickname, u.name) AS display

coalesce returns the first non-null argument, so users with a nickname get it under display and everyone else falls back to name. No extra row work — this is a per-row projection.

Touch a timestamp on write

MATCH (u:User {id: $id})
SET u.last_seen = timestamp()

Migrate a property

MATCH (u:User)
WHERE u.full_name IS NOT NULL AND u.name IS NULL
SET u.name = u.full_name
REMOVE u.full_name

Moves values from full_name to name on every matched row. Because the predicate filters out rows that already have a name, this is safe to re-run — users who are already migrated are skipped.

Conditional add

MERGE (u:User {id: $id})
ON CREATE SET u.created = timestamp()
SET u.updated = timestamp()

Copy properties from one entity to another

MATCH (src:Template {id: $src}), (dst:Record {id: $dst})
SET dst += properties(src)

properties(src) returns every key on src as a map; += merges that map into dst, overwriting matching keys and leaving anything unique to dst untouched. Useful for applying a template over an existing record without nuking custom fields.

Bulk patch via UNWIND

UNWIND $patches AS patch
MATCH (u:User {id: patch.id})
SET u += patch.fields

Derived property with CASE

MATCH (u:User)
SET u.tier = CASE
WHEN u.score >= 1000 THEN 'platinum'
WHEN u.score >= 100 THEN 'gold'
ELSE 'bronze'
END

See CASE.

Compact property dump for debugging

MATCH (n)
WHERE id(n) = $raw_id
RETURN labels(n) AS labels,
keys(n) AS keys,
properties(n) AS props

Keys as a set

keys(n) is a list. Use list predicates to ask set-style questions:

MATCH (u:User)
WHERE all(k IN ['email', 'name', 'created_at'] WHERE k IN keys(u))
RETURN u

One row per user who carries all three required keys — the list predicate all holds only when every element of the input list passes the inner WHERE. Swap all for none to find users missing a required key.

Nullable property check

MATCH (n)
WHERE n.optional IS NULL RETURN n -- missing or explicitly null
MATCH (n)
WHERE n.optional IS NOT NULL RETURN n -- present and non-null

Edge cases

Missing vs null

A missing key and a key set to null are indistinguishable — both return null on access. SET n.prop = null is the idiomatic way to remove a key (see SET).

Keys as strings

Keys are always case-sensitive strings. user.Nameuser.name.

Type drift

There's no type constraint — the same key can hold different types on different entities:

CREATE (:Item {stock: 5})
CREATE (:Item {stock: '5'}) -- legal but will surprise you

Use valueType to detect at query time; or always normalise on write.

No indexes

Property filters without a label are O(n) full scans. Always scope to a label when you can:

-- Good
MATCH (u:User {email: $email}) RETURN u

-- Scans every node
MATCH ({email: $email}) RETURN

See Limitations → Storage.

Deep property paths

Accessing nested map keys works:

MATCH (c:City)
RETURN c.location.latitude, c.tags[0]

But nothing about the inner map is indexed. For frequent inner-key filters, promote to a dedicated property at write time.

Notes

  • Property keys are case-sensitive strings.
  • A single entity can hold any number of properties.
  • There are no required properties and no type constraints — different nodes with the same label can have different property sets.
  • For the full type catalogue see Data Types.

See also