11import { Constructor } from '../utils/class' ;
22import { IObjectLiteral } from '../utils/type' ;
33import { ITransaction , Transaction } from './transaction' ;
4+ import { INode } from '../types/data' ;
5+ import { Iterators } from '../utils/iterator' ;
46
57/**
68 * Initializes a transaction.
@@ -23,22 +25,31 @@ export namespace TransactionBuilder {
2325 return new Transaction < any , null > ( ) ;
2426 }
2527
28+ /**
29+ * Tree mapper helps initializing the data. Meaning, it will
30+ * check all the node, make sure we don't have duplicate nodes in the tree,
31+ * if we have it will use references to refer to same node in different occurrences.
32+ *
33+ * DGraph clients return predicates as arrays, tree mapper will convert them to Sets instead.
34+ * since we don't want the duplicates, Sets are more efficient compare to arrays. Then plain
35+ * predicates data attached to nodes will circulate as sets.
36+ */
2637 class TreeMapperBuilder < T = any > {
2738 private readonly _entryType : Constructor < T > ;
28- private _root : IObjectLiteral < any > [ ] ;
29- private _resource = new Map < string , IObjectLiteral < any > > ( ) ;
39+ private _root : Set < INode > ;
40+ private _resource = new Map < string , INode > ( ) ;
3041
3142 constructor ( type : Constructor < T > ) {
3243 this . _entryType = type ;
3344 }
3445
3546 setRoot ( data : IObjectLiteral < any > | IObjectLiteral < any > [ ] ) : TreeMapperBuilder < T > {
3647 if ( ! Array . isArray ( data ) && Private . isNode ( data ) ) {
37- this . _root = [ data ] ;
38- } else {
39- if ( Private . isAllNodes ( data as IObjectLiteral < any > [ ] ) ) {
40- this . _root = data as IObjectLiteral < any > [ ] ;
41- }
48+ this . _root = new Set ( [ data ] ) ;
49+ }
50+
51+ if ( Array . isArray ( data ) && Private . isAllNodes ( data ) ) {
52+ this . _root = new Set ( data ) ;
4253 }
4354
4455 this . addJsonData ( data ) ;
@@ -49,39 +60,35 @@ export namespace TransactionBuilder {
4960 * Walk the resource graph and add all nodes into resource cache by its `uid`.
5061 */
5162 addJsonData ( data : IObjectLiteral < any > | IObjectLiteral < any > [ ] ) : TreeMapperBuilder < T > {
52- if ( data && ! ( data instanceof Array ) && data . uid ) {
63+ if ( data && ! ( data instanceof Array ) && Private . isNode ( data ) ) {
5364 const { obj, predicates } = Private . separatePredicates ( data ) ;
5465
5566 if ( this . _resource . has ( obj . uid ) ) {
56- const existingObj = this . _resource . get ( obj . uid ) as IObjectLiteral < any > ;
67+ const existingObj = this . _resource . get ( obj . uid ) ;
5768 // trying to not modify existing object ref
5869 Object . assign ( existingObj , obj ) ;
5970 } else {
6071 this . _resource . set ( obj . uid , obj ) ;
6172 }
6273
63- for ( const key of predicates . keys ( ) ) {
64- const predicate = predicates . get ( key ) as IObjectLiteral < any > [ ] ;
74+ Iterators . forEach ( predicates . keys ( ) , key => {
75+ const predicate = predicates . get ( key ) ! ;
76+
6577 // store nodes from the predicate
6678 this . addJsonData ( predicate ) ;
67- const existingObj = this . _resource . get ( obj . uid ) as IObjectLiteral < any > ;
68- const pred = existingObj [ key ] as IObjectLiteral < any > [ ] ;
79+ const existingObj = this . _resource . get ( obj . uid ) ! ;
80+ const pred = existingObj [ key ] as Set < INode > ;
6981
7082 predicate . forEach ( p => {
71- const pObj = this . _resource . get ( p . uid ) as IObjectLiteral < any > ;
72-
73- if ( pred . indexOf ( pObj ) > - 1 ) {
74- return ;
75- }
76-
77- pred . push ( pObj ) ;
83+ const pObj = this . _resource . get ( p . uid ) ! ;
84+ pred . add ( pObj ) ;
7885 } ) ;
79- }
86+ } ) ;
8087
8188 return this ;
8289 }
8390
84- data . forEach ( ( d : any ) => {
91+ ( data as IObjectLiteral [ ] ) . forEach ( d => {
8592 this . addJsonData ( d ) ;
8693 } ) ;
8794
@@ -92,6 +99,7 @@ export namespace TransactionBuilder {
9299 // eslint-disable-next-line @typescript-eslint/no-var-requires
93100 // const util = require('util');
94101 // console.log(
102+ // 'Final data',
95103 // util.inspect(
96104 // this._root.map(o => this._resource.get(o.uid)),
97105 // { colors: true, depth: 20 }
@@ -100,7 +108,7 @@ export namespace TransactionBuilder {
100108
101109 return new Transaction (
102110 this . _entryType ,
103- this . _root . map ( o => this . _resource . get ( o . uid ) )
111+ Iterators . map ( this . _root . values ( ) , o => this . _resource . get ( o . uid ) ! )
104112 ) ;
105113 }
106114 }
@@ -110,16 +118,17 @@ export namespace TransactionBuilder {
110118 * Module private statics
111119 */
112120namespace Private {
113- export function separatePredicates (
114- obj : IObjectLiteral < any >
115- ) : { obj : IObjectLiteral < any > ; predicates : Map < string , IObjectLiteral < any > [ ] > } {
116- const predicates : Map < string , IObjectLiteral < any > [ ] > = new Map ( ) ;
121+ /**
122+ * Separates predicates into a map and replaces the predicate array
123+ * with a Set to make sure we don't have duplicate nodes in predicates.
124+ */
125+ export function separatePredicates ( obj : INode ) : { obj : INode ; predicates : Map < string , INode [ ] > } {
126+ const predicates : Map < string , INode [ ] > = new Map ( ) ;
117127
118128 Object . keys ( obj ) . forEach ( ( key : string ) => {
119129 if ( isPredicate ( obj , key ) ) {
120130 predicates . set ( key , obj [ key ] ) ;
121-
122- obj [ key ] = [ ] ;
131+ obj [ key ] = new Set ( ) ;
123132 }
124133 } ) ;
125134
@@ -130,11 +139,11 @@ namespace Private {
130139 return isAllNodes ( obj [ key ] ) ;
131140 }
132141
133- export function isAllNodes ( obj : IObjectLiteral < any > [ ] ) : boolean {
142+ export function isAllNodes ( obj : IObjectLiteral < any > [ ] ) : obj is INode [ ] {
134143 return Array . isArray ( obj ) && ( obj as Array < any > ) . every ( isNode ) ;
135144 }
136145
137- export function isNode ( obj : IObjectLiteral < any > ) : boolean {
146+ export function isNode ( obj : IObjectLiteral < any > ) : obj is INode {
138147 return ! ! obj . uid ;
139148 }
140149}
0 commit comments