Skip to content

Bolt: Parameterized queries fail to match in MATCH property maps #3560

@bthj

Description

@bthj

Description

Bolt parameterized queries do not substitute $param values when used in MATCH node property maps ({id: $id}). The parameters are correctly received and work in other contexts — simple RETURN $x, WHERE clause comparisons, and arithmetic expressions all handle parameters correctly. Only the inline property map syntax fails to substitute.

Reproduction

Using the official Neo4j JavaScript driver v6.0.0 connecting to ArcadeDB 26.2.1 via Bolt.

Setup

# Start ArcadeDB with Bolt enabled
./bin/server.sh -Darcadedb.server.plugins="Bolt:com.arcadedb.bolt.BoltProtocolPlugin"

Minimal test script (Node.js)

const neo4j = require('neo4j-driver'); // v6.0.0

const driver = neo4j.driver(
  'bolt://127.0.0.1:7687',
  neo4j.auth.basic('root', 'playwithdata'),
  { disableLosslessIntegers: true }
);

const session = driver.session({ database: 'mydb' });

// Step 1: Create a test vertex
await session.run("CREATE (t:TestNode {id: 'ABC123', name: 'test'}) RETURN t");

// Step 2: Simple parameter — WORKS ✅
const r1 = await session.run('RETURN $x AS val', { x: 'hello' });
console.log('RETURN $x:', r1.records[0].get('val')); // "hello"

// Step 3: MATCH with property map parameter — FAILS ❌
const r2 = await session.run(
  'MATCH (t:TestNode {id: $id}) RETURN t.name AS name',
  { id: 'ABC123' }
);
console.log('MATCH {id: $id}:', r2.records.length, 'records'); // 0 records

// Step 4: WHERE clause parameter — WORKS ✅
const r3 = await session.run(
  'MATCH (t:TestNode) WHERE t.id = $id RETURN t.name AS name',
  { id: 'ABC123' }
);
console.log('WHERE t.id = $id:', r3.records.length, 'records'); // 1 record

// Step 5: Literal in query — WORKS ✅
const r4 = await session.run(
  "MATCH (t:TestNode {id: 'ABC123'}) RETURN t.name AS name"
);
console.log('Literal:', r4.records[0].get('name')); // "test"

// Step 6: Integer parameter — WORKS ✅
const r5 = await session.run('RETURN $n + 1 AS val', { n: neo4j.int(41) });
console.log('RETURN $n + 1:', r5.records[0].get('val')); // 42

await session.close();
await driver.close();

Expected output

RETURN $x: hello
MATCH {id: $id}: 1 records
WHERE t.id = $id: 1 records
Literal: test
RETURN $n + 1: 42

Actual output

RETURN $x: hello
MATCH {id: $id}: 0 records        ← BUG
WHERE t.id = $id: 1 records
Literal: test
RETURN $n + 1: 42

Analysis

  • Parameter passing over Bolt works correctlyRETURN $x, RETURN $n + 1, and WHERE t.id = $id all receive and use parameters properly
  • Only MATCH property map syntax failsMATCH (n {id: $id}) does not substitute the parameter value, while the equivalent WHERE n.id = $id works fine
  • The same queries work with literal values, confirming the data is correct and indexed
  • Negotiated protocol: Bolt v4.4 (the driver offers v5.x, server negotiates down to 4.4)

Environment

  • ArcadeDB: 26.2.1
  • Neo4j JavaScript Driver: 6.0.0 (negotiates Bolt v4.4)
  • OS: macOS (Apple Silicon)
  • Java: openjdk version "25.0.2" 2026-01-20

Impact

This is a minor compatibility gap with Neo4j's Cypher dialect. In Neo4j, MATCH (n {id: $id}) and MATCH (n) WHERE n.id = $id are equivalent, and existing code or tutorials commonly use the property map form. Applications migrating from Neo4j may need to rewrite these patterns to use WHERE clauses instead, which is a straightforward change but easy to miss.

Workaround

Use explicit WHERE clauses instead of inline property maps:

-- Instead of this (broken):
MATCH (n:Label {id: $id}) RETURN n

-- Use this (works):
MATCH (n:Label) WHERE n.id = $id RETURN n

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions