Skip to content

Expand Record Object Mapping to allow Parameter Mapping#1362

Open
MaxAake wants to merge 43 commits into6.xfrom
parameter-mapping
Open

Expand Record Object Mapping to allow Parameter Mapping#1362
MaxAake wants to merge 43 commits into6.xfrom
parameter-mapping

Conversation

@MaxAake
Copy link
Copy Markdown
Contributor

@MaxAake MaxAake commented Nov 19, 2025

closes DRIVERS-107

This PR provides support for typechecking and automatic conversion of parameters, as well as using the object mapping registry to register mapping strategies for classes. This lets users directly pass objects even with problematic properties that can not be sent over bolt (like functions), by automatically converting them or by omitting them from the mapping strategy.

This is the 2nd half of the Record Object Mapping feature, which is also marked stabilized and taken out of preview in this PR.

Examples

class Obj {
    constructor (obj) {
        this.string = obj?.string ?? 'hi'
        this.number = obj?.number ?? 1
        this.bigint = obj?.bigint ?? BigInt(1)
        this.date = obj?.date ?? "2024-01-01"
        this.localDate = obj?.localDate ?? "2024-01-01"
        this.dateTime = obj?.dateTime ?? new neo4j.DateTime(1, 1, 1, 1, 1, 1, 1, 0)._toUTC()
        this.localDateTime = obj?.localDateTime ?? new neo4j.LocalDateTime(1, 1, 1, 1, 1, 1, 1).toString()
        this.duration = obj?.duration ?? "P1DT5.00007S"
        this.time = obj?.time ?? "10:11:12.13Z"
        this.localTime = obj?.localTime ?? "10:11:12.0001"
        this.list = obj?.list ?? ["hi"]
        this.function = () => "function string" // bolt cannot send functions
        this.node = new neo4j.Node("123", [], {}) // nor can it send nodes
    }
}

const session = driver.session()
const rules = {
    number: neo4j.rule.asNumber(),
    string: neo4j.rule.asString(),
    bigint: neo4j.rule.asBigInt({acceptNumber: true}),
    date: neo4j.rule.asDate({stringify: true}), // this will ensure date is stored as a Date in the DB and retrieved as a string 
    localDate: neo4j.rule.asDate({stringify: true}),
    dateTime: neo4j.rule.asDateTime({stringify: true}),
    localDateTime: neo4j.rule.asLocalDateTime({stringify: true}),
    duration: neo4j.rule.asDuration({stringify: true}),
    time: neo4j.rule.asTime({from: "dob", stringify: true}),
    localTime: neo4j.rule.asLocalTime({stringify: true}),
    list: neo4j.rule.asList({ apply: neo4j.rule.asString() }),
} // not including function and node here will make the driver skip sending them. They could alternatively be converted to a bolt-compatible type.

neo4j.RecordObjectMapping.register(Obj, rules)

// This allows us to use camelCase for our object properties and snake_case for the properties on our node in the DB
neo4j.RecordObjectMapping.translateIdentifiers(neo4j.RecordObjectMapping.getCaseTranslator("snake_case", "camelCase"))

session.run(
  'MERGE (n {string: $string, number: $number, bigint: $bigint, date: $date, local_date: $local_date, date_time: $date_time, local_date_time: $local_date_time, duration: $duration, dob: $dob, local_time: $local_time, list: $list}) RETURN n',
  new Obj(),
  {}
).as({n: {convert: (n) => new Obj(n.as(rules))}})
.then((res) => {
    console.log(res.records[0])
    session.close()
    driver.close()
})

@MaxAake MaxAake marked this pull request as ready for review January 29, 2026 14:33
@robsdedude robsdedude self-requested a review January 30, 2026 10:03
Copy link
Copy Markdown
Member

@robsdedude robsdedude left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review part 1. I'll continue next week with the review. I just want to submit my comments to make sure they don't get lost.

Comment thread packages/core/src/internal/util.ts Outdated
Comment thread packages/core/src/graph-types.ts Outdated
Comment thread packages/core/src/graph-types.ts
Comment thread packages/core/src/mapping.highlevel.ts Outdated
Comment thread packages/core/src/mapping.highlevel.ts Outdated
Comment thread packages/core/src/session.ts
Comment thread packages/core/src/mapping.highlevel.ts Outdated
Comment thread packages/core/src/mapping.highlevel.ts Outdated
Comment thread packages/core/src/mapping.highlevel.ts Outdated
Comment thread packages/core/src/mapping.highlevel.ts Outdated
Comment thread packages/core/src/mapping.highlevel.ts Outdated
Comment thread packages/core/src/mapping.highlevel.ts Outdated
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/test/temporal-types.test.ts Outdated
Comment thread packages/core/test/temporal-types.test.ts Outdated
Comment thread packages/core/test/temporal-types.test.ts
@MaxAake
Copy link
Copy Markdown
Contributor Author

MaxAake commented Feb 13, 2026

Consider making the optional parameter on the rules also allow for the record to totally lack that key, rather than only allow it to be undefined.

@MaxAake MaxAake requested a review from robsdedude March 4, 2026 08:23
Copy link
Copy Markdown
Member

@robsdedude robsdedude left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Here goes a first round of comments. Submitting them now so they don't get lost until tomorrow when I shall continue.

Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/src/graph-types.ts Outdated
Comment thread packages/core/src/mapping.highlevel.ts Outdated
Comment thread packages/core/src/internal/util.ts
Comment thread packages/core/src/internal/util.ts
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/src/temporal-types.ts
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/src/temporal-types.ts Outdated
@MaxAake MaxAake requested a review from robsdedude March 25, 2026 13:20
Comment thread packages/core/src/mapping.highlevel.ts Outdated
Comment thread packages/core/src/mapping.highlevel.ts
Comment thread packages/core/src/mapping.rulesfactories.ts Outdated
Comment thread packages/core/src/mapping.rulesfactories.ts
Comment thread packages/core/src/internal/util.ts
Comment thread packages/core/src/temporal-types.ts Outdated
Comment thread packages/core/test/mapping.highlevel.test.ts
Comment thread packages/core/test/mapping.rulesfactories.test.ts
Comment thread packages/neo4j-driver/test/bolt-v3.test.js
Comment thread packages/neo4j-driver/test/record-object-mapping.test.js Outdated
@robsdedude robsdedude self-requested a review April 15, 2026 15:20
Comment thread packages/core/src/temporal-types.ts
Comment thread packages/core/src/temporal-types.ts
Comment thread packages/core/src/mapping.highlevel.ts Outdated
Comment on lines 29 to 58
@@ -40,27 +42,25 @@ import Vector from './vector'
*
* @property {function(rule: ?Rule & { stringify?: boolean })} asLocalTime Create a {@link Rule} that validates the value is a {@link LocalTime}.
*
* @property {function(rule: ?Rule & { stringify?: boolean })} asLocalDateTime Create a {@link Rule} that validates the value is a {@link LocalDateTime}.
* @property {function(rule: ?Rule & { stringify?: boolean, JSNativeDate?: boolean })} asLocalDateTime Create a {@link Rule} that validates the value is a {@link LocalDateTime}.
*
* @property {function(rule: ?Rule & { stringify?: boolean })} asTime Create a {@link Rule} that validates the value is a {@link Time}.
*
* @property {function(rule: ?Rule & { stringify?: boolean })} asDateTime Create a {@link Rule} that validates the value is a {@link DateTime}.
* @property {function(rule: ?Rule & { stringify?: boolean, JSNativeDate?: boolean})} asDateTime Create a {@link Rule} that validates the value is a {@link DateTime}.
*
* @property {function(rule: ?Rule & { stringify?: boolean })} asDate Create a {@link Rule} that validates the value is a {@link Date}.
* @property {function(rule: ?Rule & { stringify?: boolean, JSNativeDate?: boolean})} asDate Create a {@link Rule} that validates the value is a {@link Date}.
*
* @property {function(rule: ?Rule)} asPoint Create a {@link Rule} that validates the value is a {@link Point}.
*
* @property {function(rule: ?Rule & { apply?: Rule })} asList Create a {@link Rule} that validates the value is a List.
*
* @property {function(rule: ?Rule & { asTypedList: boolean })} asVector Create a {@link Rule} that validates the value is a List.
*
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

asVector is missing dimension and type options.

I realized that these options were introduced in the last iteration of this PR... Goes to show how fragile it is to have (more or less) the same docs in two places...

Comment thread packages/core/src/mapping.rulesfactories.ts
Comment thread packages/core/src/mapping.rulesfactories.ts
Comment thread packages/core/src/mapping.rulesfactories.ts
Comment thread packages/core/src/mapping.rulesfactories.ts
Comment thread packages/core/src/mapping.rulesfactories.ts
Comment on lines 182 to +185
* @param {mixed} query - Cypher query to execute
* @param {Object} parameters - Map with parameters to use in query
* @param {TransactionConfig} [transactionConfig] - Configuration for the new auto-commit transaction.
* @param {Rules} parameterRules - Rules to typecheck and/or map the parameter object .
* @param {Rules} parameterRules - Rules to typecheck and/or map the parameter object. Must not be provided as a separate argument if an Object is passed as first argument
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

More of the same question as above. To | undefined or not to | undefined as JShakespeare wrote.

Transaction and managed transaction are other places like this that were touched by this PR. As written about, this is likely a separate piece of work. But at least the newly added parameters should probably

  • follow which ever way is right if the whole function is newly added
  • or follow whatever other parameters are doing if the function existed or other functions with optional parameters exist in the near vicinity.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed. Aligning this with the transactionConfig parameter.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants