Skip to content
GitLab
Menu
Projects
Groups
Snippets
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
moodlenet
nodejs-services
Commits
1499b85d
Commit
1499b85d
authored
Oct 08, 2020
by
aleclofabbro
Browse files
unionType $lookup with $unionWith
parent
2650bc00
Changes
14
Hide whitespace changes
Inline
Side-by-side
graphql/schema.graphql
View file @
1499b85d
interface
Glyph
{
id
:
ID
!
_
id
:
ID
!
}
input
StringMatchInput
{
_eq
:
String
...
...
@@ -23,10 +23,10 @@ input KnowsObjectQueryInput {
input
KnowsQueryInput
{
_and
:
[
KnowsQueryInput
!]
_or
:
[
KnowsQueryInput
!]
id
:
[
ID
!]
_
id
:
[
ID
!]
}
type
Knows
implements
Glyph
{
id
:
ID
!
_
id
:
ID
!
_subj
(
query
:
KnowsSubjectQueryInput
,
page
:
PageInput
):
[
Knower
!]!
_obj
(
query
:
KnowsObjectQueryInput
,
page
:
PageInput
):
[
Knowable
!]!
}
...
...
@@ -44,10 +44,10 @@ input FollowSubjectQueryInput {
input
FollowsQueryInput
{
_and
:
[
FollowsQueryInput
!]
_or
:
[
FollowsQueryInput
!]
id
:
[
ID
!]
_
id
:
[
ID
!]
}
type
Follows
implements
Glyph
{
id
:
ID
!
_
id
:
ID
!
_subj
(
query
:
FollowSubjectQueryInput
,
page
:
PageInput
):
[
Follower
!]!
_obj
(
query
:
FollowObjectQueryInput
,
page
:
PageInput
):
[
Followable
!]!
}
...
...
@@ -56,7 +56,7 @@ type Follows implements Glyph {
input
UserQueryInput
{
_and
:
[
UserQueryInput
!]
_or
:
[
UserQueryInput
!]
id
:
[
ID
!]
_
id
:
[
ID
!]
username
:
StringMatchInput
}
...
...
@@ -68,7 +68,7 @@ input UserRelationQueryInput {
union
UserRelation
=
Knows
|
Follows
type
User
implements
Glyph
{
id
:
ID
!
_
id
:
ID
!
_rel
(
query
:
UserRelationQueryInput
,
page
:
PageInput
):
[
UserRelation
!]!
username
:
String
!
}
...
...
src/gql-graph/mongo/buildMongoPipeline.ts
View file @
1499b85d
...
...
@@ -2,47 +2,62 @@ import { DocumentSelection } from '../types'
import
{
isUnionLookupField
}
from
'
../buildDocumentSelection
'
export
const
buildMongoPipeline
=
(
docS
:
DocumentSelection
,
notTop
?:
boolean
)
=>
{
const
lookups
=
[]
as
any
[]
const
project
=
{}
as
Record
<
string
,
string
>
const
project
=
{
__typename
:
true
}
as
Record
<
string
,
any
>
Object
.
entries
(
docS
).
forEach
(([
alias
,
field
])
=>
{
Object
.
entries
(
docS
).
forEach
(([
_
alias
,
field
])
=>
{
if
(
isUnionLookupField
(
field
))
{
const
$mainMatchUnionMatch
=
{
$or
:
[]
as
any
[]
}
const
$mainMatch
=
{
$and
:
[
$mainMatchUnionMatch
]
}
const
pipeline
=
[{
$match
:
$mainMatch
}]
as
any
[]
const
$lookup
=
{
const
unionLookupPipeline
=
[]
as
any
[]
let
_let
:
any
=
undefined
const
mainLookup
=
{
as
:
field
.
alias
,
let
:
{}
as
Record
<
string
,
string
>
,
from
:
'
Graph
'
,
pipeline
,
}
pipeline
:
[],
}
as
any
if
(
field
.
traverseRelation
)
{
if
(
field
.
traverseRelation
===
'
_rel
'
)
{
$lookup
.
let
.
nodeId
=
'
$_id
'
_let
=
{
nodeId
:
'
$_id
'
}
notTop
&&
$mainMatch
.
$and
.
push
({
// $expr: { $eq: [`$${field.traverseRelation}`, '$$nodeId'] },
$expr
:
{
$eq
:
[
`$_subj`
,
'
$$nodeId
'
]
},
}
as
any
)
unionLookupPipeline
.
push
({
$match
:
{
// $expr: { $eq: [`$${field.traverseRelation}`, '$$nodeId'] },
$expr
:
{
$eq
:
[
`$_subj`
,
'
$$nodeId
'
]
},
},
})
}
else
{
$lookup
.
let
.
edgeSide
=
`$
${
field
.
traverseRelation
}
`
//=== '_obj' ? '$_subj' : '$_obj'
notTop
&&
$mainMatch
.
$and
.
push
(
{
$expr
:
{
$eq
:
[
`$_id`
,
'
$$edgeSide
'
]
}
}
as
any
)
_let
=
{
edgeSide
:
`$
${
field
.
traverseRelation
}
`
}
//=== '_obj' ? '$_subj' : '$_obj'
notTop
&&
unionLookupPipeline
.
push
({
$match
:
{
$expr
:
{
$eq
:
[
`$_id`
,
'
$$edgeSide
'
]
}
}
}
)
}
}
field
.
lookups
.
forEach
((
fieldLookup
)
=>
{
field
.
lookups
.
forEach
((
fieldLookup
,
index
)
=>
{
const
$match
=
{
$and
:
[{
__typename
:
fieldLookup
.
__typename
}]
as
any
[]
}
$mainMatchUnionMatch
.
$or
.
push
(
$match
)
fieldLookup
.
match
&&
$match
.
$and
.
push
(
fieldLookup
.
match
)
pipeline
.
push
(...
buildMongoPipeline
(
fieldLookup
.
select
,
true
))
})
project
[
field
.
alias
]
=
`$
${
field
.
alias
}
`
lookups
.
push
({
$lookup
,
if
(
!
index
)
{
mainLookup
.
pipeline
=
[
...
unionLookupPipeline
,
{
$match
},
...
buildMongoPipeline
(
fieldLookup
.
select
,
true
),
]
_let
&&
(
mainLookup
.
let
=
_let
)
lookups
.
push
({
$lookup
:
mainLookup
})
}
else
{
mainLookup
.
pipeline
.
push
({
$unionWith
:
{
coll
:
'
Graph
'
,
pipeline
:
[
...
unionLookupPipeline
,
{
$match
},
...
buildMongoPipeline
(
fieldLookup
.
select
,
true
)
/* [0].$lookup.pipeline */
,
],
},
})
}
})
project
[
field
.
alias
]
=
true
}
else
{
project
[
alias
]
=
`$
${
field
.
fieldName
}
`
project
[
field
.
alias
]
=
field
.
alias
===
field
.
fieldName
?
true
:
`$
${
field
.
fieldName
}
`
}
})
if
(
notTop
)
{
...
...
src/gql-graph/mongo/mappers.ts
View file @
1499b85d
import
{
ObjectID
}
from
'
mongodb
'
import
{
GqlNode
,
GqlRelation
,
ShallowEntity
,
ShallowRelation
}
from
'
../types
'
export
type
MongoNode
<
T
extends
GqlNode
>
=
Omit
<
T
,
'
id
'
|
'
_rel
'
>
&
{
_id
:
ObjectID
}
export
type
MongoRelation
<
T
extends
GqlRelation
>
=
Omit
<
T
,
'
id
'
|
'
_subj
'
|
'
_obj
'
>
&
{
export
type
MongoNode
<
T
extends
GqlNode
>
=
Omit
<
T
,
'
_
id
'
|
'
_rel
'
>
&
{
_id
:
ObjectID
}
export
type
MongoRelation
<
T
extends
GqlRelation
>
=
Omit
<
T
,
'
_
id
'
|
'
_subj
'
|
'
_obj
'
>
&
{
_id
:
ObjectID
_obj
:
ObjectID
_subj
:
ObjectID
}
export
const
toMongoNode
=
<
Node
extends
GqlNode
>
(
gql_node
:
ShallowEntity
<
Node
>
|
Omit
<
ShallowEntity
<
Node
>
,
'
id
'
>
gql_node
:
ShallowEntity
<
Node
>
|
Omit
<
ShallowEntity
<
Node
>
,
'
_
id
'
>
):
MongoNode
<
Node
>
=>
{
const
_id
=
'
id
'
in
gql_node
?
new
ObjectID
(
gql_node
.
id
)
:
new
ObjectID
()
const
_id
=
'
_
id
'
in
gql_node
?
new
ObjectID
(
gql_node
.
_
id
)
:
new
ObjectID
()
const
mongo_node
=
Object
.
entries
(
gql_node
).
reduce
(
(
constructing_mongo_node
,
[
key
,
val
])
=>
{
if
([
'
id
'
,
'
_rel
'
].
includes
(
key
))
{
if
([
'
_
id
'
,
'
_rel
'
].
includes
(
key
))
{
return
constructing_mongo_node
}
return
{
...
...
@@ -31,15 +31,15 @@ export const toMongoNode = <Node extends GqlNode>(
}
export
const
toMongoRelation
=
<
Rel
extends
GqlRelation
>
(
gql_node
:
ShallowRelation
<
Rel
>
|
Omit
<
ShallowRelation
<
Rel
>
,
'
id
'
>
,
gql_node
:
ShallowRelation
<
Rel
>
|
Omit
<
ShallowRelation
<
Rel
>
,
'
_
id
'
>
,
obj
:
ObjectID
|
string
,
subj
:
ObjectID
|
string
):
MongoRelation
<
Rel
>
=>
{
const
_id
=
'
id
'
in
gql_node
?
new
ObjectID
(
gql_node
.
id
)
:
new
ObjectID
()
const
_id
=
'
_
id
'
in
gql_node
?
new
ObjectID
(
gql_node
.
_
id
)
:
new
ObjectID
()
const
mongo_rel
=
Object
.
entries
(
gql_node
).
reduce
(
(
constructing_mongo_rel
,
[
key
,
val
])
=>
{
if
([
'
id
'
].
includes
(
key
))
{
if
([
'
_
id
'
].
includes
(
key
))
{
return
constructing_mongo_rel
}
return
{
...
...
src/gql-graph/types.d.ts
View file @
1499b85d
...
...
@@ -3,7 +3,7 @@ import { ObjectID } from 'mongodb'
export
type
ShallowEntity
<
T
>
=
Omit
<
T
,
'
_rel
'
>
export
type
ShallowRelation
<
T
>
=
Omit
<
T
,
'
_obj
'
|
'
_subj
'
>
export
type
GqlType
=
{
id
:
string
;
__typename
:
string
}
export
type
GqlType
=
{
_
id
:
string
;
__typename
:
string
}
export
type
GqlNode
=
GqlType
&
{
_rel
:
any
[]
}
export
type
GqlRelation
=
GqlType
&
{
_obj
:
any
;
_subj
:
any
}
...
...
src/gql/graphql.schema.json
View file @
1499b85d
...
...
@@ -14,7 +14,7 @@
"description"
:
null
,
"fields"
:
[
{
"name"
:
"id"
,
"name"
:
"
_
id"
,
"description"
:
null
,
"args"
:
[],
"type"
:
{
...
...
@@ -270,7 +270,7 @@
"defaultValue"
:
null
},
{
"name"
:
"id"
,
"name"
:
"
_
id"
,
"description"
:
null
,
"type"
:
{
"kind"
:
"LIST"
,
...
...
@@ -298,7 +298,7 @@
"description"
:
null
,
"fields"
:
[
{
"name"
:
"id"
,
"name"
:
"
_
id"
,
"description"
:
null
,
"args"
:
[],
"type"
:
{
...
...
@@ -532,7 +532,7 @@
"defaultValue"
:
null
},
{
"name"
:
"id"
,
"name"
:
"
_
id"
,
"description"
:
null
,
"type"
:
{
"kind"
:
"LIST"
,
...
...
@@ -560,7 +560,7 @@
"description"
:
null
,
"fields"
:
[
{
"name"
:
"id"
,
"name"
:
"
_
id"
,
"description"
:
null
,
"args"
:
[],
"type"
:
{
...
...
@@ -720,7 +720,7 @@
"defaultValue"
:
null
},
{
"name"
:
"id"
,
"name"
:
"
_
id"
,
"description"
:
null
,
"type"
:
{
"kind"
:
"LIST"
,
...
...
@@ -810,7 +810,7 @@
"description"
:
null
,
"fields"
:
[
{
"name"
:
"id"
,
"name"
:
"
_
id"
,
"description"
:
null
,
"args"
:
[],
"type"
:
{
...
...
src/gql/resolvers/mutation/createFollows.ts
View file @
1499b85d
...
...
@@ -13,5 +13,5 @@ export const createFollows: MutationResolvers['createFollows'] = async (_parent,
)
const
c
=
await
collection
<
Follows
>
()
await
c
.
insertOne
(
newFollows
)
return
null
//{ ...newFollows, id: newFollows._id.toHexString(), _obj: [], _subj: [] }
return
null
//{ ...newFollows,
_
id: newFollows._id.toHexString(), _obj: [], _subj: [] }
}
src/gql/resolvers/mutation/createKnows.ts
View file @
1499b85d
...
...
@@ -13,5 +13,5 @@ export const createKnows: MutationResolvers['createKnows'] = async (_parent, arg
)
const
c
=
await
collection
<
Knows
>
()
await
c
.
insertOne
(
newKnows
)
return
null
//{ ...newKnows, id: newKnows._id.toHexString(), _obj: [], _subj: [] }
return
null
//{ ...newKnows,
_
id: newKnows._id.toHexString(), _obj: [], _subj: [] }
}
src/gql/resolvers/mutation/createUser.ts
View file @
1499b85d
...
...
@@ -12,5 +12,5 @@ export const createUser: MutationResolvers['createUser'] = async (
})
const
c
=
await
collection
<
User
>
()
await
c
.
insertOne
(
newUser
)
return
null
//{ ...newUser, id: newUser._id.toHexString(), _rel: [] }
return
null
//{ ...newUser,
_
id: newUser._id.toHexString(), _rel: [] }
}
src/gql/resolvers/types/index.ts
View file @
1499b85d
import
{
defaultGraphFieldResolver
}
from
'
../../../gql-graph/defaultGraphFieldResolver
'
export
const
Types
=
{
User
:
{
id
:
defaultGraphFieldResolver
(),
_
id
:
defaultGraphFieldResolver
(),
_rel
:
defaultGraphFieldResolver
(),
username
:
defaultGraphFieldResolver
(),
},
Knows
:
{
id
:
defaultGraphFieldResolver
(),
_
id
:
defaultGraphFieldResolver
(),
_subj
:
defaultGraphFieldResolver
(),
_obj
:
defaultGraphFieldResolver
(),
},
Follows
:
{
id
:
defaultGraphFieldResolver
(),
_
id
:
defaultGraphFieldResolver
(),
_subj
:
defaultGraphFieldResolver
(),
_obj
:
defaultGraphFieldResolver
(),
},
...
...
src/gql/shallowTypes/Follows.ts
View file @
1499b85d
...
...
@@ -3,5 +3,5 @@ import * as Types from '../types'
export
const
Follows
=
(
tag
:
string
):
ShallowRelation
<
Types
.
Follows
>
=>
({
__typename
:
'
Follows
'
,
id
:
`id[
${
tag
}
]`
,
_
id
:
`
_
id[
${
tag
}
]`
,
})
src/gql/shallowTypes/Knows.ts
View file @
1499b85d
...
...
@@ -3,5 +3,5 @@ import * as Types from '../types'
export
const
Knows
=
(
tag
:
string
):
ShallowRelation
<
Types
.
Knows
>
=>
({
__typename
:
'
Knows
'
,
id
:
`id[
${
tag
}
]`
,
_
id
:
`
_
id[
${
tag
}
]`
,
})
src/gql/shallowTypes/User.ts
View file @
1499b85d
...
...
@@ -2,6 +2,6 @@ import { ShallowEntity } from '../../gql-graph/types'
import
*
as
Types
from
'
../types
'
export
const
User
=
(
tag
:
string
):
ShallowEntity
<
Types
.
User
>
=>
({
__typename
:
'
User
'
,
id
:
`id[
${
tag
}
]`
,
_
id
:
`
_
id[
${
tag
}
]`
,
username
:
`username[
${
tag
}
]`
,
})
src/gql/types.ts
View file @
1499b85d
...
...
@@ -14,7 +14,7 @@ export type Scalars = {
};
export
type
Glyph
=
{
id
:
Scalars
[
'
ID
'
];
_
id
:
Scalars
[
'
ID
'
];
};
export
type
StringMatchInput
=
{
...
...
@@ -43,12 +43,12 @@ export type KnowsObjectQueryInput = {
export
type
KnowsQueryInput
=
{
_and
:
Maybe
<
Array
<
KnowsQueryInput
>>
;
_or
:
Maybe
<
Array
<
KnowsQueryInput
>>
;
id
:
Maybe
<
Array
<
Scalars
[
'
ID
'
]
>>
;
_
id
:
Maybe
<
Array
<
Scalars
[
'
ID
'
]
>>
;
};
export
type
Knows
=
Glyph
&
{
__typename
:
'
Knows
'
;
id
:
Scalars
[
'
ID
'
];
_
id
:
Scalars
[
'
ID
'
];
_subj
:
Array
<
Knower
>
;
_obj
:
Array
<
Knowable
>
;
};
...
...
@@ -80,12 +80,12 @@ export type FollowSubjectQueryInput = {
export
type
FollowsQueryInput
=
{
_and
:
Maybe
<
Array
<
FollowsQueryInput
>>
;
_or
:
Maybe
<
Array
<
FollowsQueryInput
>>
;
id
:
Maybe
<
Array
<
Scalars
[
'
ID
'
]
>>
;
_
id
:
Maybe
<
Array
<
Scalars
[
'
ID
'
]
>>
;
};
export
type
Follows
=
Glyph
&
{
__typename
:
'
Follows
'
;
id
:
Scalars
[
'
ID
'
];
_
id
:
Scalars
[
'
ID
'
];
_subj
:
Array
<
Follower
>
;
_obj
:
Array
<
Followable
>
;
};
...
...
@@ -105,7 +105,7 @@ export type Follows_ObjArgs = {
export
type
UserQueryInput
=
{
_and
:
Maybe
<
Array
<
UserQueryInput
>>
;
_or
:
Maybe
<
Array
<
UserQueryInput
>>
;
id
:
Maybe
<
Array
<
Scalars
[
'
ID
'
]
>>
;
_
id
:
Maybe
<
Array
<
Scalars
[
'
ID
'
]
>>
;
username
:
Maybe
<
StringMatchInput
>
;
};
...
...
@@ -118,7 +118,7 @@ export type UserRelation = Knows | Follows;
export
type
User
=
Glyph
&
{
__typename
:
'
User
'
;
id
:
Scalars
[
'
ID
'
];
_
id
:
Scalars
[
'
ID
'
];
_rel
:
Array
<
UserRelation
>
;
username
:
Scalars
[
'
String
'
];
};
...
...
@@ -331,7 +331,7 @@ export type ResolversParentTypes = {
export
type
GlyphResolvers
<
ContextType
=
Context
,
ParentType
extends
ResolversParentTypes
[
'
Glyph
'
]
=
ResolversParentTypes
[
'
Glyph
'
]
>
=
{
__resolveType
:
TypeResolveFn
<
'
Knows
'
|
'
Follows
'
|
'
User
'
,
ParentType
,
ContextType
>
;
id
:
Resolver
<
ResolversTypes
[
'
ID
'
],
ParentType
,
ContextType
>
;
_
id
:
Resolver
<
ResolversTypes
[
'
ID
'
],
ParentType
,
ContextType
>
;
};
export
type
KnowerResolvers
<
ContextType
=
Context
,
ParentType
extends
ResolversParentTypes
[
'
Knower
'
]
=
ResolversParentTypes
[
'
Knower
'
]
>
=
{
...
...
@@ -343,7 +343,7 @@ export type KnowableResolvers<ContextType = Context, ParentType extends Resolver
};
export
type
KnowsResolvers
<
ContextType
=
Context
,
ParentType
extends
ResolversParentTypes
[
'
Knows
'
]
=
ResolversParentTypes
[
'
Knows
'
]
>
=
{
id
:
Resolver
<
ResolversTypes
[
'
ID
'
],
ParentType
,
ContextType
>
;
_
id
:
Resolver
<
ResolversTypes
[
'
ID
'
],
ParentType
,
ContextType
>
;
_subj
:
Resolver
<
Array
<
ResolversTypes
[
'
Knower
'
]
>
,
ParentType
,
ContextType
,
RequireFields
<
Knows_SubjArgs
,
never
>>
;
_obj
:
Resolver
<
Array
<
ResolversTypes
[
'
Knowable
'
]
>
,
ParentType
,
ContextType
,
RequireFields
<
Knows_ObjArgs
,
never
>>
;
__isTypeOf
?:
IsTypeOfResolverFn
<
ParentType
,
ContextType
>
;
...
...
@@ -358,7 +358,7 @@ export type FollowableResolvers<ContextType = Context, ParentType extends Resolv
};
export
type
FollowsResolvers
<
ContextType
=
Context
,
ParentType
extends
ResolversParentTypes
[
'
Follows
'
]
=
ResolversParentTypes
[
'
Follows
'
]
>
=
{
id
:
Resolver
<
ResolversTypes
[
'
ID
'
],
ParentType
,
ContextType
>
;
_
id
:
Resolver
<
ResolversTypes
[
'
ID
'
],
ParentType
,
ContextType
>
;
_subj
:
Resolver
<
Array
<
ResolversTypes
[
'
Follower
'
]
>
,
ParentType
,
ContextType
,
RequireFields
<
Follows_SubjArgs
,
never
>>
;
_obj
:
Resolver
<
Array
<
ResolversTypes
[
'
Followable
'
]
>
,
ParentType
,
ContextType
,
RequireFields
<
Follows_ObjArgs
,
never
>>
;
__isTypeOf
?:
IsTypeOfResolverFn
<
ParentType
,
ContextType
>
;
...
...
@@ -369,7 +369,7 @@ export type UserRelationResolvers<ContextType = Context, ParentType extends Reso
};
export
type
UserResolvers
<
ContextType
=
Context
,
ParentType
extends
ResolversParentTypes
[
'
User
'
]
=
ResolversParentTypes
[
'
User
'
]
>
=
{
id
:
Resolver
<
ResolversTypes
[
'
ID
'
],
ParentType
,
ContextType
>
;
_
id
:
Resolver
<
ResolversTypes
[
'
ID
'
],
ParentType
,
ContextType
>
;
_rel
:
Resolver
<
Array
<
ResolversTypes
[
'
UserRelation
'
]
>
,
ParentType
,
ContextType
,
RequireFields
<
User_RelArgs
,
never
>>
;
username
:
Resolver
<
ResolversTypes
[
'
String
'
],
ParentType
,
ContextType
>
;
__isTypeOf
?:
IsTypeOfResolverFn
<
ParentType
,
ContextType
>
;
...
...
src/http/index.ts
View file @
1499b85d
...
...
@@ -28,9 +28,10 @@ const executor: GraphQLServerOptions['executor'] = async (requestContext) => {
console
.
dir
({
'
executor documentSelection
'
:
documentSelection
},
{
depth
:
15
})
if
(
documentSelection
)
{
const
pipeline
=
buildMongoPipeline
(
documentSelection
)
console
.
log
(
'
executor pipeline
'
,
JSON
.
stringify
(
pipeline
,
null
,
4
))
const
c
=
await
collection
<
any
>
()
return
{
data
:
await
c
.
aggregate
(
pipeline
).
toArray
()
}
console
.
log
(
'
executor pipeline
'
,
JSON
.
stringify
(
pipeline
,
null
,
2
))
const
coll
=
await
collection
<
any
>
()
const
data
=
await
coll
.
aggregate
(
pipeline
).
toArray
()
return
{
data
}
}
return
Promise
.
resolve
(
res
)
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment