Skip to content

PersistenceManager

The low-level API: you write the statement, Drivine runs it, maps the rows and manages the transaction.

const people = await pm.query(new QuerySpecification<Person>('MATCH (p:Person) RETURN properties(p)').transform(Person));
const person = await pm.getOne(spec); // exactly one, or it throws
const maybe = await pm.maybeGetOne(spec); // one or undefined
await pm.execute(spec); // no results
new QuerySpecification<Person>()
.withStatement('MATCH (p:Person {city: $city}) RETURN properties(p)')
.bind({ city: 'Sydney' }) // named parameters, or a list for $1, $2, …
.transform(Person) // map each row to a class
.filter((it) => it.age >= 18)
.map((it) => it.firstName)
.skip(10)
.limit(10);
@Injectable()
export class UserService {
@Transactional() // join the current transaction, or start one; commit on success, roll back on a throw
async register(person: Person): Promise<void> {
await this.people.create(person);
await this.audit.record(person);
}
}

A transaction spans every database the method touches. Outside a request, run code in Drivine’s context with inDrivineContext().withTransaction({ rollback: true }).run(async () => …), or call runInTransaction(fn) inside one.

Stream a large result without holding it in memory:

const cursor = await pm.openCursor(new CursorSpecification<Route>().withStatement(statement).batchSize(100).transform(Route));
for await (const route of cursor) {
// …
}
cursor.asStream().pipe(destination); // or as a Node stream

Keep statements in files beside your code, and inject them:

constructor(
@InjectCypher(__dirname, 'routesBetween') readonly routesBetween: CypherStatement,
@InjectSql(__dirname, 'report') readonly report: SqlStatement
) {}