Built-in functions extend GQL with powerful data manipulation, aggregation, and computation capabilities. Geode implements the complete ISO/IEC 39075:2024 function specification plus performance-optimized extensions.
Function Categories
Aggregation Functions
Compute summary values across result sets:
// COUNT - count rows or non-null values
MATCH (u:User)
RETURN COUNT(*) AS total_users,
COUNT(u.email) AS users_with_email
// SUM - sum numeric values
MATCH (o:Order)
RETURN SUM(o.total) AS revenue
// AVG - average of numeric values
MATCH (p:Product)-[:HAS_REVIEW]->(r:Review)
RETURN p.name, AVG(r.rating) AS avg_rating
// MIN/MAX - minimum and maximum values
MATCH (u:User)
RETURN MIN(u.age) AS youngest,
MAX(u.age) AS oldest
// COLLECT - collect values into list
MATCH (u:User)-[:LIKES]->(p:Product)
RETURN u.name, COLLECT(p.name) AS liked_products
// String aggregation: COLLECT returns a LIST; join it in the client.
// There is no STRING_AGG function.
MATCH (t:Tag)
RETURN COLLECT(t.name) AS tag_list
String Functions
Text manipulation and analysis:
// UPPER/LOWER - case conversion
MATCH (u:User)
RETURN UPPER(u.name) AS name_upper,
LOWER(u.email) AS email_lower
// LENGTH - string length
MATCH (p:Post)
WHERE LENGTH(p.content) > 280
RETURN p
// SUBSTRING - extract substring (there is no POSITION/STRPOS function;
// substring takes a start offset and a length)
MATCH (u:User)
RETURN SUBSTRING(u.email, 0, 5) AS email_prefix
// TRIM/LTRIM/RTRIM - remove whitespace
MATCH (t:Text)
RETURN TRIM(t.value) AS trimmed
// Concatenation is an operator (`+` or `||`); there is no CONCAT function
MATCH (u:User)
RETURN u.first_name || ' ' || u.last_name AS full_name
// REPLACE - literal substring replacement
MATCH (t:Text)
RETURN REPLACE(t.content, 'old', 'new') AS updated
// Regular expressions: `=~` is a predicate over a lightweight matcher
// (^, $, ., *, +, ?, character classes, escapes — no alternation or captures)
MATCH (p:Product)
WHERE p.sku =~ '^PROD-[0-9]{4}$'
RETURN p
Mathematical Functions
Numeric computations:
// Basic math
RETURN ABS(-42) AS absolute,
CEIL(3.14) AS ceiling,
FLOOR(3.14) AS floor,
ROUND(3.14159, 2) AS rounded,
SIGN(-5) AS sign_value
// Power and roots
RETURN POWER(2, 10) AS power,
SQRT(16) AS square_root,
EXP(1) AS e,
LOG(100) AS natural_log,
LOG10(1000) AS log_base_10
// Trigonometry
RETURN SIN(3.14159) AS sine,
COS(0) AS cosine,
TAN(0.785) AS tangent,
ASIN(0.5) AS arcsine
// Random numbers
RETURN RAND() AS random_0_to_1,
FLOOR(RAND() * 100) AS random_0_to_99
Temporal Functions
Date and time operations:
// Current time functions
RETURN CURRENT_DATE AS today,
CURRENT_TIME AS now_time,
CURRENT_TIMESTAMP AS now_datetime,
datetime() AS timestamp
// Date construction
RETURN DATE('2024-06-15') AS specific_date,
DATETIME('2024-06-15T14:30:00') AS specific_datetime
// Date components
MATCH (e:Event)
RETURN e.date.year AS year,
e.date.month AS month,
e.date.day AS day,
e.date.dayOfWeek AS weekday,
e.date.weekOfYear AS week
// Duration arithmetic
MATCH (e:Event)
WHERE e.timestamp > datetime() - duration('P7D') // Last 7 days
RETURN e
// Date formatting
MATCH (e:Event)
RETURN FORMAT_DATETIME(e.timestamp, 'YYYY-MM-DD HH:mm:ss') AS formatted
List Functions
List manipulation and analysis:
// SIZE - list length
MATCH (u:User)
RETURN u.tags, SIZE(u.tags) AS tag_count
// HEAD/LAST - first and last elements
MATCH (u:User)
RETURN HEAD(u.tags) AS first_tag,
LAST(u.tags) AS last_tag
// TAIL - all but first element
MATCH (u:User)
RETURN TAIL(u.tags) AS remaining_tags
// REVERSE - reverse list order
MATCH (u:User)
RETURN REVERSE(u.scores) AS reversed
// RANGE - generate numeric range
RETURN RANGE(1, 10) AS one_to_ten,
RANGE(0, 100, 10) AS tens
// List comprehension
MATCH (u:User)
RETURN [tag IN u.tags WHERE LENGTH(tag) > 5] AS long_tags,
[x IN RANGE(1, 10) | x * x] AS squares
// REDUCE - fold/accumulate
MATCH (o:Order)-[:CONTAINS]->(i:Item)
WITH o, COLLECT(i.price * i.quantity) AS subtotals
RETURN o.id, null /* reduce() is advertised by SHOW FUNCTIONS but has no evaluator dispatch; collect() the values and fold client-side (for path weights use algo.dijkstra.stream, which yields cost) */ AS order_total
Map Functions
Work with map/object data:
// KEYS - get map keys
MATCH (u:User)
RETURN KEYS(u.settings) AS setting_keys
// VALUES - get map values
MATCH (u:User)
RETURN VALUES(u.settings) AS setting_values
// SIZE - count map entries
MATCH (u:User)
RETURN SIZE(u.profile) AS profile_field_count
// Map access
MATCH (u:User)
RETURN u.settings['theme'] AS theme,
u.settings.notifications AS notifications
Path Functions
Graph path manipulation:
// LENGTH - path length (number of relationships)
MATCH path = (a:User)-[:KNOWS*]->(b:User)
WHERE LENGTH(path) <= 3
RETURN path
// NODES - extract nodes from path
MATCH path = (a:User)-[:KNOWS*]->(b:User)
RETURN NODES(path) AS users_in_path
// RELATIONSHIPS - extract relationships
MATCH path = (a)-[r:KNOWS*]->(b)
RETURN RELATIONSHIPS(path) AS connections
// Path predicates
MATCH path = (a:User)-[:KNOWS*]->(b:User)
WHERE ALL(node IN NODES(path) WHERE node.active = true)
AND NONE(rel IN RELATIONSHIPS(path) WHERE rel.blocked = true)
RETURN path
Type Conversion Functions
Convert between data types:
// To string
RETURN TOSTRING(42) AS str,
TOSTRING(3.14) AS float_str,
TOSTRING(true) AS bool_str
// To integer
RETURN TOINTEGER('42') AS int,
TOINTEGER(3.14) AS truncated
// To float
RETURN TOFLOAT('3.14') AS float,
TOFLOAT(42) AS int_as_float
// To boolean
RETURN TOBOOLEAN('true') AS bool_true,
TOBOOLEAN(1) AS bool_from_int
Conditional Functions
Conditional logic and null handling:
// COALESCE - first non-null value
MATCH (u:User)
RETURN COALESCE(u.nickname, u.username, 'Anonymous') AS display_name
// CASE - conditional expressions
MATCH (u:User)
RETURN u.name,
CASE
WHEN u.age < 18 THEN 'minor'
WHEN u.age < 65 THEN 'adult'
ELSE 'senior'
END AS age_group
// NULLIF - return null if values equal
MATCH (u:User)
RETURN NULLIF(u.display_name, '') AS name // NULL if empty string
Scalar Functions
Single-value utilities:
// ID - get node/relationship ID
MATCH (n:User)
RETURN ID(n) AS node_id
// LABELS - get node labels
MATCH (n)
RETURN LABELS(n) AS node_labels
// TYPE - get relationship type
MATCH ()-[r]->()
RETURN TYPE(r) AS rel_type
// PROPERTIES - get all properties
MATCH (n:User {id: 123})
RETURN PROPERTIES(n) AS user_props
// EXISTS - check existence
MATCH (u:User)
WHERE EXISTS { (u)-[:PURCHASED]->(:Product) }
RETURN u
Hash and Encoding Functions
Geode does not compute digests and has no Base64 or URL encoders: there is no
SHA256, MD5, BASE64_ENCODE, BASE64_DECODE or URL_ENCODE function.
What it provides is a HASH type built from an already-computed hex digest, a
constant-time comparison, hex encode/decode, and URL parsing:
// Store a client-computed digest as a typed HASH value
RETURN hash('sha3-256', $hex_digest) AS digest;
// Constant-time comparison
RETURN hash_equals(hash('sha3-256', $a), hash('sha3-256', $b)) AS same;
// Hex encoding (bytea/from_hex require the \x prefix)
RETURN to_hex($bytes) AS hex, from_hex('\x48656c6c6f') AS bytes;
// URL parsing
RETURN url_scheme($href) AS scheme,
url_host($href) AS host,
url_path($href) AS path,
url_query($href) AS query;
Advanced Function Usage
Window Functions
Compute over partitions:
// Row number within partition
MATCH (u:User)-[:PURCHASED]->(p:Product)
RETURN u.name,
p.name,
null /* Geode has no window functions and no OVER clause */ AS rank
// Running totals
MATCH (t:Transaction)
RETURN t.date,
t.amount,
SUM(t.amount) AS running_total
// Moving averages
MATCH (m:Measurement)
RETURN m.timestamp,
m.value,
AVG(m.value) AS moving_avg
User-Defined Functions
Extend with custom functions (Python example):
from geode_client import Client
client = Client(host="localhost", port=3141)
async with client.connection() as conn:
# Register custom function
await conn.execute("""
CREATE FUNCTION haversine_distance(
lat1 FLOAT, lon1 FLOAT,
lat2 FLOAT, lon2 FLOAT
) RETURNS FLOAT
LANGUAGE PYTHON
AS $$
import math
R = 6371 # Earth radius in km
dlat = math.radians(lat2 - lat1)
dlon = math.radians(lon2 - lon1)
a = (math.sin(dlat/2) ** 2 +
math.cos(math.radians(lat1)) *
math.cos(math.radians(lat2)) *
math.sin(dlon/2) ** 2)
c = 2 * math.asin(math.sqrt(a))
return R * c
$$
""")
# Use custom function
result, _ = await conn.query("""
MATCH (loc1:Location {name: 'NYC'}),
(loc2:Location {name: 'SF'})
RETURN haversine_distance(
loc1.lat, loc1.lon,
loc2.lat, loc2.lon
) AS distance_km
""")
Performance Considerations
Function Execution Cost
- Aggregations: Process all matching rows (O(n))
- String operations: Linear in string length
- Math functions: Constant time (O(1))
- Path functions: Linear in path length
Optimization Tips
// Expensive: function in WHERE with no index
MATCH (u:User)
WHERE UPPER(u.email) = 'ADMIN@EXAMPLE.COM'
RETURN u
// Better: index on computed column or filter differently
CREATE INDEX FOR (u:User) ON (u.email);
MATCH (u:User)
WHERE u.email = 'admin@example.com' // Case-insensitive index
RETURN u
// Avoid repeated function calls
MATCH (p:Product)
WITH p, LENGTH(p.description) AS desc_len // Compute once
WHERE desc_len > 100 AND desc_len < 500
RETURN p
Best Practices
- Use Indexes: Filter on indexed properties before applying functions
- Compute Once: Store function results in variables with WITH
- Null Safety: Always handle potential nulls with COALESCE or IS NULL
- Type Compatibility: Ensure function arguments match expected types
- Aggregation Scope: Use GROUP BY explicitly for clarity
- Function Composition: Chain functions for complex transformations
- Performance Testing: Profile expensive function calls on realistic data
Common Patterns
Calculate Derived Properties
MATCH (u:User)
SET u.full_name = u.first_name || ' ' || u.last_name,
u.name_length = LENGTH(u.first_name || ' ' || u.last_name)
Conditional Aggregation
MATCH (u:User)-[:PURCHASED]->(p:Product)
RETURN u.name,
COUNT(p) AS total_purchases,
SUM(CASE WHEN p.price > 100 THEN 1 ELSE 0 END) AS expensive_purchases,
AVG(CASE WHEN p.category = 'Electronics' THEN p.price ELSE NULL END) AS avg_electronics_price
Time-Based Analysis
MATCH (e:Event)
WHERE e.timestamp >= DATE_TRUNC('month', datetime())
WITH $bucket_start /* no date_trunc(): bucket at write time or compare an explicit range */ AS day, COUNT(*) AS count
RETURN day, count
ORDER BY day
Graph-Specific Functions
Shortest Path Functions
-- Find shortest path between nodes
MATCH path = SHORTEST_PATH((a:User {id: 1})-[:KNOWS*]-(b:User {id: 100}))
RETURN path, length(path) AS hops;
-- All shortest paths (multiple paths with same length)
MATCH paths = ALL_SHORTEST_PATHS((a)-[:RELATED*]-(b))
RETURN paths;
-- Weighted shortest path
MATCH path = SHORTEST_PATH((a)-[r:ROAD*]-(b))
RETURN path, null /* reduce() is advertised by SHOW FUNCTIONS but has no evaluator dispatch; collect() the values and fold client-side (for path weights use algo.dijkstra.stream, which yields cost) */ AS total_distance;
Centrality Functions
-- PageRank centrality (runs over the whole graph; takes no arguments)
CALL algo.pagerank.stream()
YIELD nodeId, score
MATCH (n:User) WHERE id(n) = nodeId
RETURN n.name, score
ORDER BY score DESC
LIMIT 10;
-- Betweenness centrality
CALL algo.betweenness.stream()
YIELD nodeId, betweenness
MATCH (n:User) WHERE id(n) = nodeId
RETURN n.name, betweenness;
-- Degree centrality
MATCH (n:User)
RETURN n.name,
SIZE([(n)-[:FOLLOWS]->() | 1]) AS out_degree,
SIZE([()<-[:FOLLOWS]-(n) | 1]) AS in_degree,
SIZE([(n)-[:FOLLOWS]-() | 1]) AS total_degree;
Community Detection
-- Louvain community detection
CALL algo.louvain.stream()
YIELD nodeId, community
MATCH (n:User) WHERE id(n) = nodeId
SET n.community = community;
-- Label propagation
CALL algo.labelPropagation.stream()
YIELD nodeId, label
MATCH (n:User) WHERE id(n) = nodeId
RETURN label AS community, COUNT(n) AS size
ORDER BY size DESC;
None of the algo.* procedures accept label, relationship-type or iteration
arguments; they run over every node and relationship in the current graph.
Geospatial Functions
Distance Calculations
-- Haversine distance (km)
MATCH (a:Location), (b:Location)
RETURN a.name, b.name,
geospatial.distance(
point({latitude: a.lat, longitude: a.lon}),
point({latitude: b.lat, longitude: b.lon})
) AS distance_km;
-- Find locations within radius
MATCH (center:Location {name: 'New York'})
MATCH (nearby:Location)
WHERE geospatial.distance(
point({latitude: center.lat, longitude: center.lon}),
point({latitude: nearby.lat, longitude: nearby.lon})
) < 100 -- 100 km radius
RETURN nearby.name, nearby.lat, nearby.lon;
Bounding Box Queries
-- Find all locations in bounding box
MATCH (loc:Location)
WHERE loc.lat >= $min_lat AND loc.lat <= $max_lat
AND loc.lon >= $min_lon AND loc.lon <= $max_lon
RETURN loc.name, loc.lat, loc.lon;
JSON Functions
JSON Processing
-- Parse JSON string
MATCH (d:Document)
WITH d, JSON_PARSE(d.content) AS json_obj
RETURN json_obj.title, json_obj.author;
-- Extract JSON field
MATCH (d:Document)
RETURN json_get(d.metadata, '$.tags[0]') AS first_tag;
-- Build JSON object
MATCH (u:User)
RETURN JSON_OBJECT(
'name', u.name,
'email', u.email,
'age', u.age
) AS user_json;
-- Convert to JSON string
MATCH (u:User)
RETURN JSON_STRINGIFY(u) AS user_string;
Array Functions
-- Array contains
MATCH (u:User)
WHERE ARRAY_CONTAINS(u.tags, 'developer')
RETURN u;
-- Array length
MATCH (u:User)
RETURN u.name, ARRAY_LENGTH(u.interests) AS interest_count;
-- Array slice
MATCH (u:User)
RETURN u.name, ARRAY_SLICE(u.purchases, 0, 5) AS recent_purchases;
-- Array distinct
MATCH (u:User)
SET u.unique_tags = ARRAY_DISTINCT(u.tags);
Statistical Functions
Descriptive Statistics
-- Standard deviation
MATCH (u:User)
RETURN collect(u.age) /* stddev/variance parse but are never computed; reduce client-side */ AS age_stddev,
collect(u.age) /* stddev/variance parse but are never computed; reduce client-side */ AS population_stddev;
-- Variance
MATCH (p:Product)
RETURN collect(p.price) /* stddev/variance parse but are never computed; reduce client-side */ AS price_variance;
-- Percentiles
MATCH (u:User)
RETURN collect(u.age) /* percentile_cont/_disc parse but are never computed; sort client-side */ AS q1,
collect(u.age) /* percentile_cont/_disc parse but are never computed; sort client-side */ AS median,
collect(u.age) /* percentile_cont/_disc parse but are never computed; sort client-side */ AS q3;
-- Mode (most common value)
MATCH (u:User)
RETURN u.country, COUNT(*) AS frequency
ORDER BY frequency DESC
LIMIT 1;
Correlation
-- Correlation between two properties
MATCH (u:User)
RETURN CORR(u.age, u.purchase_count) AS age_purchase_correlation;
Text Search Procedures
Full-text search is not a scalar function in Geode — it is a set of CALL
procedures in the geode.fts.* namespace, backed by a durable on-disk index you
create with CREATE INDEX ... USING fulltext.
Full-Text Search
-- Create the index once
CREATE INDEX post_content_fts FOR (p:Post) ON (p.content) USING fulltext;
-- BM25-ranked search: yields the matching node's id and its relevance score
CALL geode.fts.search('post_content_fts', 'graph database', 10) YIELD node, score;
-- Exact phrase match (position-based)
CALL geode.fts.phrase('post_content_fts', 'graph database') YIELD node, score;
-- Proximity: terms within N positions of each other
CALL geode.fts.proximity('post_content_fts', 'graph database', 5) YIELD node, score;
See Full-Text Search for the full reference.
Text Analysis
Tokenization, case-folding and stopword removal happen inside the full-text
index at write time; they are not exposed as callable GQL functions. There is no
TOKENIZE(), STEM(), WORD_COUNT(), FUZZY_MATCH() or SOUNDEX() in the
function registry.
For the substring and case-insensitive matching the evaluator does implement:
-- Case-insensitive containment
MATCH (p:Post)
WHERE icontains(p.content, 'graph database')
RETURN p.title;
-- Prefix matching (STARTS WITH / ENDS WITH are infix operators, not functions)
MATCH (u:User)
WHERE u.name STARTS WITH 'Ali'
RETURN u.name;
Limitation: edit-distance (“fuzzy”) matching, phonetic matching, per-document word counts and standalone stemming are not implemented in the engine. Compute them client-side, or store the derived value as an ordinary indexed property.
Vector Functions
Vector Operations
-- Cosine similarity
MATCH (a:Product {id: 1}), (b:Product)
WHERE b <> a
RETURN b.name,
similarity(a.embedding, b.embedding) AS similarity
ORDER BY similarity DESC
LIMIT 10;
-- Euclidean distance (euclidean() is an alias of vector_l2())
MATCH (a:Item), (b:Item)
RETURN a.id, b.id,
euclidean(a.features, b.features) AS distance;
-- Dot product
MATCH (v:Vector)
RETURN vector_dot(v.vec1, v.vec2) AS dot_prod;
-- Vector magnitude: there is no built-in magnitude/norm function. The L2 norm
-- is the distance to the zero vector of the same dimensionality.
MATCH (v:Vector)
RETURN vector_l2(v.embedding, vectorf32([0.0, 0.0, 0.0])) AS mag;
Custom Function Development
Python UDFs
# Register custom function
await client.execute("""
CREATE FUNCTION sentiment_score(text STRING)
RETURNS FLOAT
LANGUAGE PYTHON
AS $$
from textblob import TextBlob
def sentiment_score(text):
blob = TextBlob(text)
return blob.sentiment.polarity
$$
""")
# Use custom function
result, _ = await client.query("""
MATCH (p:Post)
RETURN p.content,
sentiment_score(p.content) AS sentiment
ORDER BY sentiment DESC
""")
JavaScript UDFs
-- Create JavaScript function
-- Geode has no user-defined functions, aggregates or procedures: the
-- grammar has no CREATE FUNCTION / AGGREGATE / PROCEDURE. Inline the
-- logic into the query, or compute it in the client.
return price * (1 - (discounts[tier] || 0));
}
$$;
-- Use function
MATCH (p:Product)
RETURN p.name,
p.price,
calculate_discount(p.price, 'gold') AS discounted_price;
Performance Tips for Functions
Indexed Function Results
-- Create index on function result
CREATE INDEX user_name_upper ON User(UPPER(name));
-- Efficient case-insensitive search
MATCH (u:User)
WHERE UPPER(u.name) = UPPER($search_name)
RETURN u;
Materialized Function Results
-- Pre-compute expensive functions
MATCH (u:User)
SET u.full_name_normalized = UPPER(TRIM(u.first_name || ' ' || u.last_name));
-- Later queries are fast
MATCH (u:User {full_name_normalized: 'ALICE SMITH'})
RETURN u;
Avoid Repeated Function Calls
-- Inefficient: LENGTH called twice
MATCH (p:Product)
WHERE LENGTH(p.description) > 100 AND LENGTH(p.description) < 500
RETURN p;
-- Efficient: Compute once with WITH
MATCH (p:Product)
WITH p, LENGTH(p.description) AS desc_len
WHERE desc_len > 100 AND desc_len < 500
RETURN p;
Function Reference Summary
Aggregation
- COUNT, SUM, AVG, MIN, MAX, COLLECT, STRING_AGG
- STDDEV, VARIANCE, PERCENTILE_CONT
String
- UPPER, LOWER, LENGTH, SUBSTRING, TRIM, CONCAT, REPLACE, SPLIT
- FULLTEXT_SEARCH, FUZZY_MATCH, SOUNDEX
Math
- ABS, CEIL, FLOOR, ROUND, SIGN, POWER, SQRT, EXP, LOG
- SIN, COS, TAN, RAND
Temporal
- CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, NOW
- DATE, DATETIME, DURATION, DATE_TRUNC
List
- SIZE, HEAD, LAST, TAIL, REVERSE, RANGE, REDUCE
Type Conversion
- TOSTRING, TOINTEGER, TOFLOAT, TOBOOLEAN
Conditional
- COALESCE, CASE, NULLIF
Graph
- SHORTEST_PATH, ALL_SHORTEST_PATHS, NODES, RELATIONSHIPS
- PageRank, Betweenness, Louvain
Geospatial
- geospatial.distance, point
Vector
- COSINE_SIMILARITY, EUCLIDEAN_DISTANCE, DOT_PRODUCT
Related Topics
- GQL Operators : Operators work alongside functions in expressions
- Data Types : Understand type compatibility for function arguments
- Query Optimization : Optimize function usage for performance
- Aggregation : Deep dive into aggregation patterns
- GQL Reference : Complete function documentation
- Stored Procedures : Creating custom functions
- Graph Algorithms : Algorithm implementations
Further Reading
- GQL Operators - Operators and expressions
- Data Types - Type system and conversions
- Query Performance - Optimize function calls
- Aggregation Patterns - Advanced aggregation techniques
- GQL Reference - Complete language documentation
- Function Performance Guide - Optimization strategies
- Custom Function Development - UDF best practices
Master Geode’s built-in functions to write expressive, powerful queries that transform and analyze your graph data efficiently.