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
e3b40674
Commit
e3b40674
authored
Oct 07, 2020
by
aleclofabbro
Browse files
build mongo pipeline with $unionWith
parent
204ae430
Changes
13
Hide whitespace changes
Inline
Side-by-side
graphql/schema.graphql
View file @
e3b40674
...
...
@@ -89,7 +89,9 @@ input CreateUserInput {
username
:
String
!
}
type
Mutation
{
createUser
(
user
:
CreateUserInput
!):
User
!
createUser
(
user
:
CreateUserInput
!):
User
createKnows
(
from
:
ID
!,
to
:
ID
!):
Knows
createFollows
(
from
:
ID
!,
to
:
ID
!):
Follows
}
enum
Role
{
...
...
src/gql-graph/
queryBuilder
.ts
→
src/gql-graph/
buildDocumentSelection
.ts
View file @
e3b40674
import
{
DocumentSelection
,
GraphQuery
,
GraphQueryObj
,
UnionLookupField
,
ValueField
}
from
'
./types
'
import
{
DocumentSelection
,
Field
,
GraphQuery
,
GraphQueryObj
,
UnionLookupField
,
ValueField
,
}
from
'
./types
'
export
const
buildQueryDocumentSelection
=
(
root
?
:
GraphQuery
root
:
GraphQuery
|
null
|
undefined
):
DocumentSelection
|
null
|
undefined
=>
{
return
root
&&
buildDocumentSelection
(
root
.
qObj
)
}
...
...
@@ -55,3 +62,6 @@ const aliasConflictError = (selection: GraphQueryObj, par: GraphQueryObj) => {
console
.
error
(
`A ValueField can't have same alias[
${
selection
.
alias
}
] as a UnionLookupField`
,
par
)
return
new
Error
(
`A ValueField can't have same alias[
${
selection
.
alias
}
] as a UnionLookupField`
)
}
export
const
isValueField
=
(
_
:
Field
):
_
is
ValueField
=>
!!
(
_
&&
'
fieldName
'
in
_
)
export
const
isUnionLookupField
=
(
_
:
Field
):
_
is
UnionLookupField
=>
!!
(
_
&&
'
lookups
'
in
_
)
src/gql-graph/mongo/buildMongoPipeline.ts
0 → 100644
View file @
e3b40674
import
{
DocumentSelection
}
from
'
../types
'
import
{
isValueField
}
from
'
../queryBuilder
'
export
const
buildMongoPipeline
=
(
docS
:
DocumentSelection
)
=>
{
const
lookups
=
[]
as
any
[]
const
project
=
{}
as
Record
<
string
,
string
>
Object
.
entries
(
docS
).
forEach
(([
alias
,
field
])
=>
{
if
(
isValueField
(
field
))
{
project
[
field
.
fieldName
]
=
alias
}
else
{
const
$match
=
{
$and
:
[]
as
any
[]
}
const
$unionWith
=
{
coll
:
'
Graph
'
,
pipeline
:
[{
$match
}]
as
any
[],
}
const
$lookup
=
{
as
:
alias
,
from
:
'
Graph
'
,
pipeline
:
[{
$unionWith
}],
}
field
.
lookups
.
forEach
((
fieldLookup
)
=>
{
$match
.
$and
.
push
({
__typename
:
fieldLookup
.
__typename
})
fieldLookup
.
match
&&
$match
.
$and
.
push
(
fieldLookup
.
match
)
const
pipeline
=
buildMongoPipeline
(
fieldLookup
.
select
)
$unionWith
.
pipeline
.
push
(...
pipeline
)
})
lookups
.
push
({
$lookup
,
})
}
})
const
stages
=
[...
lookups
]
if
(
Object
.
keys
(
project
).
length
)
{
stages
.
push
({
$project
:
project
})
}
return
stages
}
src/gql-graph/mongo/mappers.ts
View file @
e3b40674
import
{
ObjectID
}
from
'
mongodb
'
import
{
GqlNode
,
GqlRelation
,
ShallowEntity
}
from
'
../types
'
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
'
>
&
{
_id
:
ObjectID
...
...
@@ -7,9 +7,9 @@ export type MongoRelation<T extends GqlRelation> = Omit<T, 'id' | '_subj' | '_ob
_subj
:
ObjectID
}
export
const
toMongoNode
=
<
N
extends
GqlNode
>
(
gql_node
:
ShallowEntity
<
N
>
|
Omit
<
ShallowEntity
<
N
>
,
'
id
'
>
):
MongoNode
<
N
>
=>
{
export
const
toMongoNode
=
<
N
ode
extends
GqlNode
>
(
gql_node
:
ShallowEntity
<
N
ode
>
|
Omit
<
ShallowEntity
<
N
ode
>
,
'
id
'
>
):
MongoNode
<
N
ode
>
=>
{
const
_id
=
'
id
'
in
gql_node
?
new
ObjectID
(
gql_node
.
id
)
:
new
ObjectID
()
const
mongo_node
=
Object
.
entries
(
gql_node
).
reduce
(
...
...
@@ -24,8 +24,35 @@ export const toMongoNode = <N extends GqlNode>(
},
{
_id
,
}
as
MongoNode
<
N
>
}
as
MongoNode
<
N
ode
>
)
return
mongo_node
}
export
const
toMongoRelation
=
<
Rel
extends
GqlRelation
>
(
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
mongo_rel
=
Object
.
entries
(
gql_node
).
reduce
(
(
constructing_mongo_rel
,
[
key
,
val
])
=>
{
if
([
'
id
'
].
includes
(
key
))
{
return
constructing_mongo_rel
}
return
{
...
constructing_mongo_rel
,
[
key
]:
val
,
}
},
{
_id
,
_obj
:
new
ObjectID
(
obj
),
_subj
:
new
ObjectID
(
subj
),
}
as
MongoRelation
<
Rel
>
)
return
mongo_rel
}
src/gql-graph/types.d.ts
View file @
e3b40674
...
...
@@ -35,6 +35,10 @@ export type Limit = any
// Graph DocumentSelection
export
type
DocumentSelection
=
{
[
alias
:
string
]:
Field
}
export
type
Field
=
UnionLookupField
|
ValueField
export
type
ValueField
=
{
...
...
@@ -52,9 +56,5 @@ export type UnionLookupField = {
export
type
FieldLookup
=
{
__typename
:
string
match
?:
any
select
:
null
|
DocumentSelection
}
export
type
DocumentSelection
=
{
[
alias
:
string
]:
Field
select
:
DocumentSelection
}
src/gql/graphql.schema.json
View file @
e3b40674
...
...
@@ -1045,13 +1045,91 @@
}
],
"type"
:
{
"kind"
:
"NON_NULL"
,
"name"
:
null
,
"ofType"
:
{
"kind"
:
"OBJECT"
,
"name"
:
"User"
,
"ofType"
:
null
"kind"
:
"OBJECT"
,
"name"
:
"User"
,
"ofType"
:
null
},
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"createKnows"
,
"description"
:
null
,
"args"
:
[
{
"name"
:
"from"
,
"description"
:
null
,
"type"
:
{
"kind"
:
"NON_NULL"
,
"name"
:
null
,
"ofType"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"ID"
,
"ofType"
:
null
}
},
"defaultValue"
:
null
},
{
"name"
:
"to"
,
"description"
:
null
,
"type"
:
{
"kind"
:
"NON_NULL"
,
"name"
:
null
,
"ofType"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"ID"
,
"ofType"
:
null
}
},
"defaultValue"
:
null
}
],
"type"
:
{
"kind"
:
"OBJECT"
,
"name"
:
"Knows"
,
"ofType"
:
null
},
"isDeprecated"
:
false
,
"deprecationReason"
:
null
},
{
"name"
:
"createFollows"
,
"description"
:
null
,
"args"
:
[
{
"name"
:
"from"
,
"description"
:
null
,
"type"
:
{
"kind"
:
"NON_NULL"
,
"name"
:
null
,
"ofType"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"ID"
,
"ofType"
:
null
}
},
"defaultValue"
:
null
},
{
"name"
:
"to"
,
"description"
:
null
,
"type"
:
{
"kind"
:
"NON_NULL"
,
"name"
:
null
,
"ofType"
:
{
"kind"
:
"SCALAR"
,
"name"
:
"ID"
,
"ofType"
:
null
}
},
"defaultValue"
:
null
}
],
"type"
:
{
"kind"
:
"OBJECT"
,
"name"
:
"Follows"
,
"ofType"
:
null
},
"isDeprecated"
:
false
,
"deprecationReason"
:
null
...
...
src/gql/resolvers/mutation/createFollows.ts
0 → 100644
View file @
e3b40674
import
{
ObjectID
}
from
'
mongodb
'
import
{
toMongoRelation
}
from
'
../../../gql-graph/mongo/mappers
'
import
{
collection
}
from
'
../../../mongo/collection
'
import
{
Follows
,
MutationResolvers
}
from
'
../../types
'
export
const
createFollows
:
MutationResolvers
[
'
createFollows
'
]
=
async
(
_parent
,
args
)
=>
{
const
newFollows
=
toMongoRelation
<
Follows
>
(
{
__typename
:
'
Follows
'
,
},
new
ObjectID
(
args
.
to
),
new
ObjectID
(
args
.
from
)
)
const
c
=
await
collection
<
Follows
>
()
await
c
.
insertOne
(
newFollows
)
return
null
//{ ...newFollows, id: newFollows._id.toHexString(), _obj: [], _subj: [] }
}
src/gql/resolvers/mutation/createKnows.ts
0 → 100644
View file @
e3b40674
import
{
ObjectID
}
from
'
mongodb
'
import
{
toMongoRelation
}
from
'
../../../gql-graph/mongo/mappers
'
import
{
collection
}
from
'
../../../mongo/collection
'
import
{
Knows
,
MutationResolvers
}
from
'
../../types
'
export
const
createKnows
:
MutationResolvers
[
'
createKnows
'
]
=
async
(
_parent
,
args
)
=>
{
const
newKnows
=
toMongoRelation
<
Knows
>
(
{
__typename
:
'
Knows
'
,
},
new
ObjectID
(
args
.
to
),
new
ObjectID
(
args
.
from
)
)
const
c
=
await
collection
<
Knows
>
()
await
c
.
insertOne
(
newKnows
)
return
null
//{ ...newKnows, id: newKnows._id.toHexString(), _obj: [], _subj: [] }
}
src/gql/resolvers/mutation/createUser.ts
View file @
e3b40674
...
...
@@ -10,7 +10,7 @@ export const createUser: MutationResolvers['createUser'] = async (
__typename
:
'
User
'
,
username
:
args
.
user
.
username
,
})
const
c
=
await
collection
<
User
>
(
'
User
'
)
const
c
=
await
collection
<
User
>
()
await
c
.
insertOne
(
newUser
)
return
{
...
newUser
,
id
:
newUser
.
_id
.
toHexString
(),
_rel
:
[]
}
return
null
//
{ ...newUser, id: newUser._id.toHexString(), _rel: [] }
}
src/gql/resolvers/mutation/index.ts
View file @
e3b40674
import
{
MutationResolvers
}
from
'
../../types
'
import
{
createUser
}
from
'
./createUser
'
import
{
createFollows
}
from
'
./createFollows
'
import
{
createKnows
}
from
'
./createKnows
'
export
const
Mutation
:
MutationResolvers
=
{
createUser
,
createFollows
,
createKnows
,
}
src/gql/types.ts
View file @
e3b40674
...
...
@@ -152,7 +152,9 @@ export type CreateUserInput = {
export
type
Mutation
=
{
__typename
:
'
Mutation
'
;
createUser
:
User
;
createUser
:
Maybe
<
User
>
;
createKnows
:
Maybe
<
Knows
>
;
createFollows
:
Maybe
<
Follows
>
;
};
...
...
@@ -160,6 +162,18 @@ export type MutationCreateUserArgs = {
user
:
CreateUserInput
;
};
export
type
MutationCreateKnowsArgs
=
{
from
:
Scalars
[
'
ID
'
];
to
:
Scalars
[
'
ID
'
];
};
export
type
MutationCreateFollowsArgs
=
{
from
:
Scalars
[
'
ID
'
];
to
:
Scalars
[
'
ID
'
];
};
export
enum
Role
{
Anonymous
=
'
Anonymous
'
,
Admin
=
'
Admin
'
,
...
...
@@ -366,7 +380,9 @@ export type QueryResolvers<ContextType = Context, ParentType extends ResolversPa
};
export
type
MutationResolvers
<
ContextType
=
Context
,
ParentType
extends
ResolversParentTypes
[
'
Mutation
'
]
=
ResolversParentTypes
[
'
Mutation
'
]
>
=
{
createUser
:
Resolver
<
ResolversTypes
[
'
User
'
],
ParentType
,
ContextType
,
RequireFields
<
MutationCreateUserArgs
,
'
user
'
>>
;
createUser
:
Resolver
<
Maybe
<
ResolversTypes
[
'
User
'
]
>
,
ParentType
,
ContextType
,
RequireFields
<
MutationCreateUserArgs
,
'
user
'
>>
;
createKnows
:
Resolver
<
Maybe
<
ResolversTypes
[
'
Knows
'
]
>
,
ParentType
,
ContextType
,
RequireFields
<
MutationCreateKnowsArgs
,
'
from
'
|
'
to
'
>>
;
createFollows
:
Resolver
<
Maybe
<
ResolversTypes
[
'
Follows
'
]
>
,
ParentType
,
ContextType
,
RequireFields
<
MutationCreateFollowsArgs
,
'
from
'
|
'
to
'
>>
;
};
export
type
AuthResolvers
<
ContextType
=
Context
,
ParentType
extends
ResolversParentTypes
[
'
Auth
'
]
=
ResolversParentTypes
[
'
Auth
'
]
>
=
{
...
...
src/http/index.ts
View file @
e3b40674
...
...
@@ -3,7 +3,9 @@ import { ApolloServer } from 'apollo-server-express'
import
express
from
'
express
'
import
{
graphql
,
GraphQLSchema
}
from
'
graphql
'
import
{
Context
}
from
'
../gql
'
import
{
buildMongoPipeline
}
from
'
../gql-graph/mongo/buildMongoQuery
'
import
{
buildQueryDocumentSelection
}
from
'
../gql-graph/queryBuilder
'
import
{
collection
}
from
'
../mongo/collection
'
type
Cfg
=
{
httpPort
:
number
...
...
@@ -24,5 +26,11 @@ const executor: GraphQLServerOptions['executor'] = async (requestContext) => {
console
.
dir
({
'
executor $graph
'
:
ctx
},
{
depth
:
15
})
const
documentSelection
=
buildQueryDocumentSelection
(
ctx
.
$graph
)
console
.
dir
({
'
executor documentSelection
'
:
documentSelection
},
{
depth
:
15
})
if
(
documentSelection
)
{
const
pipeline
=
buildMongoPipeline
(
documentSelection
)
console
.
dir
({
'
executor pipeline
'
:
pipeline
},
{
depth
:
15
})
const
c
=
await
collection
<
any
>
()
return
{
data
:
await
c
.
aggregate
(
pipeline
).
toArray
()
}
}
return
Promise
.
resolve
(
res
)
}
src/mongo/collection.ts
View file @
e3b40674
...
...
@@ -2,7 +2,12 @@ import { MongoNode, MongoRelation } from '../gql-graph/mongo/mappers'
import
{
GqlType
,
GqlNode
,
GqlRelation
}
from
'
../gql-graph/types
'
import
{
DB
}
from
'
./
'
export
const
collection
=
async
<
E
extends
GqlType
>
(
name
:
E
[
'
__typename
'
])
=>
// export const collection = async <E extends GqlType>(name: E['__typename']) =>
// (await DB).collection<
// E extends GqlNode ? MongoNode<E> : E extends GqlRelation ? MongoRelation<E> : never
// >(name)
export
const
collection
=
async
<
E
extends
GqlType
>
()
=>
(
await
DB
).
collection
<
E
extends
GqlNode
?
MongoNode
<
E
>
:
E
extends
GqlRelation
?
MongoRelation
<
E
>
:
never
>
(
name
)
>
(
'
Graph
'
)
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