Commit 2650bc00 authored by aleclofabbro's avatar aleclofabbro
Browse files

almost there !

parent e3b40674
......@@ -18,7 +18,7 @@ export const buildDocumentSelection = (qObj: GraphQueryObj): DocumentSelection |
return null
}
const documentValueFieldsSelections = qObj.select.reduce((_documentSelection, selection) => {
const valueField: ValueField = { fieldName: selection.field }
const valueField: ValueField = selection
return {
..._documentSelection,
[selection.alias]: valueField,
......@@ -27,7 +27,7 @@ export const buildDocumentSelection = (qObj: GraphQueryObj): DocumentSelection |
const documentUnionLookupFieldSelections = qObj.traverse.reduce(
(_documentSelection, qObjSelection) => {
const _existingAlias = _documentSelection[qObjSelection.alias]
if (_existingAlias && 'fieldName' in _existingAlias) {
if (_existingAlias && !isUnionLookupField(_existingAlias)) {
throw aliasConflictError(qObjSelection, qObj)
}
const select = buildDocumentSelection(qObjSelection)
......@@ -37,7 +37,13 @@ export const buildDocumentSelection = (qObj: GraphQueryObj): DocumentSelection |
const unionLookupField: UnionLookupField =
_existingAlias ||
(_documentSelection[qObjSelection.alias] = { page: qObjSelection.page, lookups: [] })
(_documentSelection[qObjSelection.alias] = {
page: qObjSelection.page,
fieldName: qObjSelection.fieldName,
alias: qObjSelection.alias,
lookups: [],
traverseRelation: qObjSelection.traverseRelation,
})
unionLookupField.lookups.push({
__typename: qObjSelection.__typename,
......@@ -63,5 +69,4 @@ const aliasConflictError = (selection: GraphQueryObj, par: GraphQueryObj) => {
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 _)
......@@ -2,10 +2,11 @@ import { Context } from '../gql'
import { ResolverFn } from '../gql/types'
import { ShallowTypeMocks } from '../gql/shallowTypes'
import { typeInfo, getParent } from './graphQuery'
import { GraphQueryObj } from './types'
import { GraphQueryObj, TraverseRelation } from './types'
export function defaultGraphFieldResolver(): ResolverFn<any, any, Context, any> {
return (parent, args, context, info) => {
const { fieldName, parentType, returnType, schema } = info
if (!context.$graph) {
context.$graph = {
qObj: {
......@@ -13,11 +14,12 @@ export function defaultGraphFieldResolver(): ResolverFn<any, any, Context, any>
alias: 'Query',
directives: {},
traverse: [],
traverseRelation: null,
fieldName: fieldName,
select: [],
},
}
}
const { fieldName, parentType, returnType, schema } = info
console.log(`defaultFieldResolver `, {
parent,
parentTypeName: parentType.name,
......@@ -37,7 +39,7 @@ export function defaultGraphFieldResolver(): ResolverFn<any, any, Context, any>
if (!parentQ) {
throw new Error('defaultFieldResolver no parentQ')
}
if (isTop || ['_rel', '_subj', '_obj'].includes(fieldName)) {
if (isTop || isTraverseRelation(fieldName)) {
parentQ.traverse.push(
...__typenames.map<GraphQueryObj>((__typename) => ({
__typename,
......@@ -46,7 +48,9 @@ export function defaultGraphFieldResolver(): ResolverFn<any, any, Context, any>
page: args?.page,
select: [],
traverse: [],
traverseRelation: isTraverseRelation(fieldName) ? fieldName : null,
directives: {},
fieldName: fieldName,
}))
)
......@@ -62,9 +66,12 @@ export function defaultGraphFieldResolver(): ResolverFn<any, any, Context, any>
return fieldReturn
} else {
if ('string' == typeof info.path.key) {
parentQ.select.push({ field: fieldName, alias: info.path.key })
parentQ.select.push({ fieldName: fieldName, alias: info.path.key })
}
return parent[fieldName]
}
}
}
const isTraverseRelation = (_: string): _ is TraverseRelation =>
['_subj', '_obj', '_rel'].includes(_)
import { DocumentSelection } from '../types'
import { isValueField } from '../queryBuilder'
export const buildMongoPipeline = (docS: DocumentSelection) => {
import { isUnionLookupField } from '../buildDocumentSelection'
export const buildMongoPipeline = (docS: DocumentSelection, notTop?: boolean) => {
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[],
}
if (isUnionLookupField(field)) {
const $mainMatchUnionMatch = { $or: [] as any[] }
const $mainMatch = { $and: [$mainMatchUnionMatch] }
const pipeline = [{ $match: $mainMatch }] as any[]
const $lookup = {
as: alias,
as: field.alias,
let: {} as Record<string, string>,
from: 'Graph',
pipeline: [{ $unionWith }],
pipeline,
}
if (field.traverseRelation) {
if (field.traverseRelation === '_rel') {
$lookup.let.nodeId = '$_id'
notTop &&
$mainMatch.$and.push({
// $expr: { $eq: [`$${field.traverseRelation}`, '$$nodeId'] },
$expr: { $eq: [`$_subj`, '$$nodeId'] },
} as any)
} else {
$lookup.let.edgeSide = `$${field.traverseRelation}` //=== '_obj' ? '$_subj' : '$_obj'
notTop && $mainMatch.$and.push({ $expr: { $eq: [`$_id`, '$$edgeSide'] } } as any)
}
}
field.lookups.forEach((fieldLookup) => {
$match.$and.push({ __typename: fieldLookup.__typename })
const $match = { $and: [{ __typename: fieldLookup.__typename }] as any[] }
$mainMatchUnionMatch.$or.push($match)
fieldLookup.match && $match.$and.push(fieldLookup.match)
const pipeline = buildMongoPipeline(fieldLookup.select)
$unionWith.pipeline.push(...pipeline)
pipeline.push(...buildMongoPipeline(fieldLookup.select, true))
})
project[field.alias] = `$${field.alias}`
lookups.push({
$lookup,
})
} else {
project[alias] = `$${field.fieldName}`
}
})
const stages = [...lookups]
if (Object.keys(project).length) {
stages.push({ $project: project })
if (notTop) {
const stages = [...lookups]
if (Object.keys(project).length) {
stages.push({ $project: project })
}
return stages
} else {
const stages = lookups[0].$lookup.pipeline
return stages
}
return stages
}
......@@ -7,14 +7,17 @@ export type GqlType = { id: string; __typename: string }
export type GqlNode = GqlType & { _rel: any[] }
export type GqlRelation = GqlType & { _obj: any; _subj: any }
export type TraverseRelation = '_obj' | '_subj' | '_rel'
// GraphQuery
export type GraphQuery = {
qObj: GraphQueryObj
}
export type GraphQueryObj = {
export type GraphQueryObj = Selection & {
__typename: string
traverseRelation: TraverseRelation | null
match?: any
page?: {
limit?: number
......@@ -23,11 +26,10 @@ export type GraphQueryObj = {
directives: Record<string, any>
select: Selection[]
traverse: GraphQueryObj[]
alias: string
}
export type Selection = {
field: string
fieldName: string
alias: string
}
export type Match = any
......@@ -41,15 +43,14 @@ export type DocumentSelection = {
export type Field = UnionLookupField | ValueField
export type ValueField = {
fieldName: string
}
export type ValueField = Selection
export type UnionLookupField = {
export type UnionLookupField = ValueField & {
page?: {
limit?: number
after?: string | ObjectID
}
traverseRelation: TraverseRelation | null
lookups: FieldLookup[]
}
......
......@@ -3,8 +3,8 @@ 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 { buildMongoPipeline } from '../gql-graph/mongo/buildMongoPipeline'
import { buildQueryDocumentSelection } from '../gql-graph/buildDocumentSelection'
import { collection } from '../mongo/collection'
type Cfg = {
......@@ -28,7 +28,7 @@ const executor: GraphQLServerOptions['executor'] = async (requestContext) => {
console.dir({ 'executor documentSelection': documentSelection }, { depth: 15 })
if (documentSelection) {
const pipeline = buildMongoPipeline(documentSelection)
console.dir({ 'executor pipeline': pipeline }, { depth: 15 })
console.log('executor pipeline', JSON.stringify(pipeline, null, 4))
const c = await collection<any>()
return { data: await c.aggregate(pipeline).toArray() }
}
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment