Skip to content

Commit acd7622

Browse files
committed
Add mappedResultTransformer
1 parent efee40a commit acd7622

File tree

2 files changed

+206
-2
lines changed

2 files changed

+206
-2
lines changed

packages/core/src/result-transformers.ts

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
* limitations under the License.
1818
*/
1919

20-
import { Dict } from './record'
20+
import Record, { Dict } from './record'
2121
import Result from './result'
2222
import EagerResult from './result-eager'
23+
import ResultSummary from './result-summary'
24+
import { newError } from './error'
2325

2426
async function createEagerResultFromResult<Entries extends Dict> (result: Result): Promise<EagerResult<Entries>> {
2527
const { summary, records } = await result
@@ -66,6 +68,106 @@ class ResultTransformers {
6668
eagerResultTransformer<Entries extends Dict = Dict>(): ResultTransformer<EagerResult<Entries>> {
6769
return createEagerResultFromResult
6870
}
71+
72+
/**
73+
* Creates a {@link ResultTransformer} which maps the {@link Record} in the result and collects it
74+
* along with the {@link ResultSummary} and {@link Result#keys}.
75+
*
76+
* NOTE: The config object requires map or/and collect to be valid.
77+
*
78+
* @example
79+
* // Mapping the records
80+
* const { keys, records, summary } = await driver.executeQuery('MATCH (p:Person{ age: $age }) RETURN p.name as name', { age: 25 }, {
81+
* resultTransformer: neo4j.resultTransformers.mappedResultTransformer({
82+
* map(record) {
83+
* return record.get('name')
84+
* }
85+
* })
86+
* })
87+
*
88+
* records.forEach(name => console.log(`${name} has 25`))
89+
*
90+
* @example
91+
* // Mapping records and collect result
92+
* const names = await driver.executeQuery('MATCH (p:Person{ age: $age }) RETURN p.name as name', { age: 25 }, {
93+
* resultTransformer: neo4j.resultTransformers.mappedResultTransformer({
94+
* map(record) {
95+
* return record.get('name')
96+
* },
97+
* collect(records, summary, keys) {
98+
* return records
99+
* }
100+
* })
101+
* })
102+
*
103+
* names.forEach(name => console.log(`${name} has 25`))
104+
*
105+
* @example
106+
* // The transformer can be defined one and used everywhere
107+
* const getRecordsAsObjects = neo4j.resultTransformers.mappedResultTransformer({
108+
* map(record) {
109+
* return record.toObject()
110+
* },
111+
* collect(objects) {
112+
* return objects
113+
* }
114+
* })
115+
*
116+
* // The usage in a driver.executeQuery
117+
* const objects = await driver.executeQuery('MATCH (p:Person{ age: $age }) RETURN p.name as name', { age: 25 }, {
118+
* resultTransformer: getRecordsAsObjects
119+
* })
120+
* objects.forEach(object => console.log(`${object.name} has 25`))
121+
*
122+
*
123+
* // The usage in session.executeRead
124+
* const objects = await session.executeRead(tx => getRecordsAsObjects(tx.run('MATCH (p:Person{ age: $age }) RETURN p.name as name')))
125+
* objects.forEach(object => console.log(`${object.name} has 25`))
126+
*
127+
* @experimental
128+
* @param {object} config The result transformer configuration
129+
* @param {function(record:Record):R} [config.map=function(record) { return record }] Method called for mapping each record
130+
* @param {function(records:R[], summary:ResultSummary, keys:string[]):T} [config.collect=function(records, summary, keys) { return { records, summary, keys }}] Method called for mapping
131+
* the result data to the transformer output.
132+
* @returns {ResultTransformer<T>} The result transformer
133+
* @see {@link Driver#executeQuery}
134+
*/
135+
mappedResultTransformer <
136+
R = Record, T = { records: R[], keys: string[], summary: ResultSummary }
137+
>(config: { map?: (rec: Record) => R, collect?: (records: R[], summary: ResultSummary, keys: string[]) => T }): ResultTransformer<T> {
138+
if (config == null || (config.collect == null && config.map == null)) {
139+
throw newError('Requires a map or/and a collect functions.')
140+
}
141+
return async (result: Result) => {
142+
return await new Promise((resolve, reject) => {
143+
const state: { keys: string[], records: R[] } = { records: [], keys: [] }
144+
145+
result.subscribe({
146+
onKeys (keys: string[]) {
147+
state.keys = keys
148+
},
149+
onNext (record: Record) {
150+
if (config.map != null) {
151+
state.records.push(config.map(record))
152+
} else {
153+
state.records.push(record as unknown as R)
154+
}
155+
},
156+
onCompleted (summary: ResultSummary) {
157+
if (config.collect != null) {
158+
resolve(config.collect(state.records, summary, state.keys))
159+
} else {
160+
const obj = { records: state.records, summary, keys: state.keys }
161+
resolve(obj as unknown as T)
162+
}
163+
},
164+
onError (error: Error) {
165+
reject(error)
166+
}
167+
})
168+
})
169+
}
170+
}
69171
}
70172

71173
/**

packages/neo4j-driver-deno/lib/core/result-transformers.ts

Lines changed: 103 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@
1717
* limitations under the License.
1818
*/
1919

20-
import { Dict } from './record.ts'
20+
import Record, { Dict } from './record.ts'
2121
import Result from './result.ts'
2222
import EagerResult from './result-eager.ts'
23+
import ResultSummary from './result-summary.ts'
24+
import { newError } from './error.ts'
2325

2426
async function createEagerResultFromResult<Entries extends Dict> (result: Result): Promise<EagerResult<Entries>> {
2527
const { summary, records } = await result
@@ -66,6 +68,106 @@ class ResultTransformers {
6668
eagerResultTransformer<Entries extends Dict = Dict>(): ResultTransformer<EagerResult<Entries>> {
6769
return createEagerResultFromResult
6870
}
71+
72+
/**
73+
* Creates a {@link ResultTransformer} which maps the {@link Record} in the result and collects it
74+
* along with the {@link ResultSummary} and {@link Result#keys}.
75+
*
76+
* NOTE: The config object requires map or/and collect to be valid.
77+
*
78+
* @example
79+
* // Mapping the records
80+
* const { keys, records, summary } = await driver.executeQuery('MATCH (p:Person{ age: $age }) RETURN p.name as name', { age: 25 }, {
81+
* resultTransformer: neo4j.resultTransformers.mappedResultTransformer({
82+
* map(record) {
83+
* return record.get('name')
84+
* }
85+
* })
86+
* })
87+
*
88+
* records.forEach(name => console.log(`${name} has 25`))
89+
*
90+
* @example
91+
* // Mapping records and collect result
92+
* const names = await driver.executeQuery('MATCH (p:Person{ age: $age }) RETURN p.name as name', { age: 25 }, {
93+
* resultTransformer: neo4j.resultTransformers.mappedResultTransformer({
94+
* map(record) {
95+
* return record.get('name')
96+
* },
97+
* collect(records, summary, keys) {
98+
* return records
99+
* }
100+
* })
101+
* })
102+
*
103+
* names.forEach(name => console.log(`${name} has 25`))
104+
*
105+
* @example
106+
* // The transformer can be defined one and used everywhere
107+
* const getRecordsAsObjects = neo4j.resultTransformers.mappedResultTransformer({
108+
* map(record) {
109+
* return record.toObject()
110+
* },
111+
* collect(objects) {
112+
* return objects
113+
* }
114+
* })
115+
*
116+
* // The usage in a driver.executeQuery
117+
* const objects = await driver.executeQuery('MATCH (p:Person{ age: $age }) RETURN p.name as name', { age: 25 }, {
118+
* resultTransformer: getRecordsAsObjects
119+
* })
120+
* objects.forEach(object => console.log(`${object.name} has 25`))
121+
*
122+
*
123+
* // The usage in session.executeRead
124+
* const objects = await session.executeRead(tx => getRecordsAsObjects(tx.run('MATCH (p:Person{ age: $age }) RETURN p.name as name')))
125+
* objects.forEach(object => console.log(`${object.name} has 25`))
126+
*
127+
* @experimental
128+
* @param {object} config The result transformer configuration
129+
* @param {function(record:Record):R} [config.map=function(record) { return record }] Method called for mapping each record
130+
* @param {function(records:R[], summary:ResultSummary, keys:string[]):T} [config.collect=function(records, summary, keys) { return { records, summary, keys }}] Method called for mapping
131+
* the result data to the transformer output.
132+
* @returns {ResultTransformer<T>} The result transformer
133+
* @see {@link Driver#executeQuery}
134+
*/
135+
mappedResultTransformer <
136+
R = Record, T = { records: R[], keys: string[], summary: ResultSummary }
137+
>(config: { map?: (rec: Record) => R, collect?: (records: R[], summary: ResultSummary, keys: string[]) => T }): ResultTransformer<T> {
138+
if (config == null || (config.collect == null && config.map == null)) {
139+
throw newError('Requires a map or/and a collect functions.')
140+
}
141+
return async (result: Result) => {
142+
return await new Promise((resolve, reject) => {
143+
const state: { keys: string[], records: R[] } = { records: [], keys: [] }
144+
145+
result.subscribe({
146+
onKeys (keys: string[]) {
147+
state.keys = keys
148+
},
149+
onNext (record: Record) {
150+
if (config.map != null) {
151+
state.records.push(config.map(record))
152+
} else {
153+
state.records.push(record as unknown as R)
154+
}
155+
},
156+
onCompleted (summary: ResultSummary) {
157+
if (config.collect != null) {
158+
resolve(config.collect(state.records, summary, state.keys))
159+
} else {
160+
const obj = { records: state.records, summary, keys: state.keys }
161+
resolve(obj as unknown as T)
162+
}
163+
},
164+
onError (error: Error) {
165+
reject(error)
166+
}
167+
})
168+
})
169+
}
170+
}
69171
}
70172

71173
/**

0 commit comments

Comments
 (0)