Skip to main content

String Functions

Every function on this page returns null when any argument is null. Case operations are ASCII-only; normalize is a no-op placeholder.

Overview

GoalFunction
Case conversiontoLower, toUpper
Trim whitespacetrim, lTrim, rTrim
Substring replacereplace
Substring slicesubstring
Left / right sliceleft, right
Split by delimitersplit
Reversereverse
Lengthsize, length, charLength
Padlpad, rpad
Normalise (NFC)normalize (placeholder)
Convert typetoString, toInteger, toFloat, toBoolean
Predicate in WHERESTARTS WITH, ENDS WITH, CONTAINS, =~

toLower / toUpper

ASCII case conversion.

RETURN toLower('Ada LoveLace')    -- 'ada lovelace'
RETURN toUpper('ada') -- 'ADA'

Non-ASCII letters pass through unchanged:

RETURN toLower('Ångström')        -- 'Ångström'   (non-ASCII untouched)

Case-insensitive matching

MATCH (u:User)
WHERE toLower(u.email) = toLower($search)
RETURN u
MATCH (u:User)
WHERE toLower(u.name) STARTS WITH toLower($prefix)
RETURN u

trim / lTrim / rTrim

Strip whitespace from both ends / left / right.

RETURN trim('   hi   ')    -- 'hi'
RETURN lTrim(' hi ') -- 'hi '
RETURN rTrim(' hi ') -- ' hi'

Common for cleaning up user input before storage:

UNWIND $rows AS row
CREATE (:Contact {email: toLower(trim(row.email))})

replace

replace(str, find, replacement) — replaces every occurrence.

RETURN replace('banana', 'a', 'o')    -- 'bonono'
RETURN replace('hello', 'x', 'y') -- 'hello'
RETURN replace('abc def', ' ', '_') -- 'abc_def'

Multi-step replace

WITH 'Joe O\'Brien' AS raw
RETURN replace(replace(raw, ' ', '_'), '\'', '') AS slug
-- 'Joe_OBrien'

substring

substring(str, start[, length]) — 0-based indices.

RETURN substring('loradb', 0, 4)   -- 'lora'
RETURN substring('loradb', 4) -- 'db'
RETURN substring('hello', 1, 3) -- 'ell'

Out-of-range indices return an empty string rather than an error.

RETURN substring('hi', 99)         -- ''
RETURN substring('hi', 0, 99) -- 'hi'

left / right

RETURN left('graphdb', 5)     -- 'graph'
RETURN right('graphdb', 2) -- 'db'

Length exceeding the input returns the whole string:

RETURN left('ab', 99)         -- 'ab'

split

RETURN split('a,b,c,d', ',')         -- ['a', 'b', 'c', 'd']
RETURN split('one two three', ' ') -- ['one', 'two', 'three']
RETURN split('x', ',') -- ['x']

Empty input returns [''].

Split + UNWIND

Turn comma-separated values into rows:

UNWIND split('red,green,blue', ',') AS color
CREATE (:Swatch {color: color})

reverse

Works on both strings and lists.

RETURN reverse('hello')       -- 'olleh'
RETURN reverse([1, 2, 3]) -- [3, 2, 1]

size / length / charLength

FunctionMeasures
size(s)Length of the string (bytes for ASCII-only; code units otherwise)
length(s)Alias for size on strings; also accepts paths
charLength(s)Unicode code-point count
RETURN size('abc')           -- 3
RETURN length('abc') -- 3
RETURN charLength('café') -- 4

length also accepts paths — see Paths → path functions.

lpad / rpad

lpad(str, length, padding) / rpad(str, length, padding) — pads to the target length using the padding character repeated.

RETURN lpad('7',   3, '0')   -- '007'
RETURN rpad('7', 3, '0') -- '700'
RETURN lpad('abc', 5, '.') -- '..abc'
RETURN rpad('abc', 5, '.') -- 'abc..'

If the input is already longer than length, it's returned unchanged.

Fixed-width formatting

MATCH (r:Record)
RETURN lpad(toString(r.id), 6, '0') AS padded_id

normalize

Placeholder for Unicode NFC normalisation. Today it returns the input unchanged.

RETURN normalize('café')   -- 'café'   (no NFC applied)

If you need real NFC normalisation, apply it host-side before writing.

Type conversion

FunctionAcceptsReturns
toString(x)anyString; nullnull
toInteger(x) / toInt(x)Int, Float (truncates), String, BoolInt or null on parse failure
toFloat(x)Int, Float, StringFloat or null on parse failure
toBoolean(x) / toBooleanOrNull(x)Bool, String ("true"/"false"), Int (0 / non-0)Bool or null on parse failure
RETURN toString(42)              -- '42'
RETURN toString(true) -- 'true'
RETURN toString(date('2024-01-15')) -- '2024-01-15'

RETURN toInteger('007') -- 7
RETURN toInteger(3.9) -- 3 (truncates)
RETURN toInteger(true) -- 1
RETURN toInteger('not a number') -- null (parse fails)

RETURN toFloat('3.14') -- 3.14
RETURN toFloat(42) -- 42.0

RETURN toBoolean('TRUE') -- true
RETURN toBoolean(0) -- false
RETURN toBoolean('maybe') -- null

Safe conversion pattern

Combine with coalesce for a default on parse failure:

MATCH (p:Product) RETURN coalesce(toInteger(p.stock), 0) AS stock

String operators (in WHERE)

Covered in the WHERE page — included here for completeness:

OperatorCase-sensitiveDescription
STARTS WITHyesPrefix match
ENDS WITHyesSuffix match
CONTAINSyesSubstring match
=~yesRegex match (Rust regex, RE2-style — no backreferences)
MATCH (u:User) WHERE u.email ENDS WITH '@loradb.com' RETURN u
MATCH (u:User) WHERE toLower(u.email) =~ '.*@loradb\\.com$' RETURN u
MATCH (u:User) WHERE u.name CONTAINS 'Admin' RETURN u

Regex vs CONTAINS

Regex is more expressive but slower and strict-anchored (=~ 'foo' matches only the full string foo). Prefer CONTAINS for simple substring matches.

Common patterns

Slugify

WITH 'Hello, World! 2024' AS raw
RETURN toLower(replace(replace(raw, ',', ''), ' ', '-')) AS slug
-- 'hello--world!-2024'

Not a full slugifier — punctuation survives. For real slugs, normalise host-side.

Initials

MATCH (p:Person) WHERE p.name IS NOT NULL
RETURN p.name,
reduce(acc = '', part IN split(p.name, ' ') |
acc + left(part, 1)) AS initials

Domain from email

MATCH (u:User) WHERE u.email CONTAINS '@'
RETURN u.email,
substring(u.email, size(split(u.email, '@')[0]) + 1) AS domain

Normalise for comparison

MATCH (u:User)
WHERE toLower(trim(u.email)) = toLower(trim($candidate))
RETURN u

Join a list into a string

There's no join function. Use reduce:

MATCH (u:User)
RETURN u.name,
reduce(out = '', t IN u.tags |
CASE WHEN out = '' THEN t ELSE out + ', ' + t END
) AS tags_csv

Parse key=value pairs

WITH 'a=1;b=2;c=3' AS s
RETURN reduce(
m = {},
pair IN split(s, ';') |
m + {[split(pair, '=')[0]]: split(pair, '=')[1]}
) AS parsed
-- {a: '1', b: '2', c: '3'}

Values are strings — wrap each with toInteger if you need numeric types.

Truncate for preview

MATCH (p:Post)
RETURN p.id,
CASE WHEN size(p.body) > 100
THEN left(p.body, 97) + '...'
ELSE p.body
END AS preview

The conditional here is a CASE expression — LoraDB's ternary. See that page for the full reference.

Limitations

  • toLower / toUpper are ASCII-only. For Unicode case folding, normalise host-side before passing the string to LoraDB.
  • normalize is not yet implemented — it's a placeholder that returns its input unchanged.
  • String indexing is byte-based inside size and length. Use charLength when you need Unicode code-point counts.

See also