roschaefer e4d57f80aa Fix #1333
Ok, so here are multiple issues:
1. In cypher, `NOT NULL` will return `NULL` not `FALSE`. If we want
`FALSE` to be set in the database import, we should use `COAELESCE`
to find the first not-null value.
See:
https://neo4j.com/docs/cypher-manual/current/syntax/working-with-null/
https://markhneedham.com/blog/2017/02/22/neo4j-null-values-even-work/

2. I removed the `disabled` and `deleted` checks on the commented
counter. With `neo4j-graphql-js` it is not possible to filter on the
join models (at least not without a lot of complexity) for disabled or
deleted items. Let's live with the fact that the list of commented posts
will include those posts, where the user has deleted his comment or where
the user's comment was disabled. It's being displayed as "not available"
so I think this is OK for now.

3. De-couple the pagination counters from the "commented", "shouted"
etc. counters. It might be that the list of posts is different for
different users. E.g. if the user has blocked you, the "posts" list
will be empty. The "shouted" or "commented" list will not have the
posts of the author. If you are a moderator, the list will include
disabled posts. So the counters are not in sync with the actual list
coming from the backend. Therefore I implemented "fetch and check if
resultSet < pageSize" instead of a global counter.
2019-08-28 12:57:32 +02:00

98 lines
3.0 KiB
JavaScript

import { neode } from '../../../bootstrap/neo4j'
export const undefinedToNullResolver = list => {
const resolvers = {}
list.forEach(key => {
resolvers[key] = async (parent, params, context, resolveInfo) => {
return typeof parent[key] === 'undefined' ? null : parent[key]
}
})
return resolvers
}
export default function Resolver(type, options = {}) {
const instance = neode()
const {
idAttribute = 'id',
undefinedToNull = [],
boolean = {},
count = {},
hasOne = {},
hasMany = {},
} = options
const _hasResolver = (resolvers, { key, connection }, { returnType }) => {
return async (parent, params, context, resolveInfo) => {
if (typeof parent[key] !== 'undefined') return parent[key]
const id = parent[idAttribute]
const statement = `MATCH(:${type} {${idAttribute}: {id}})${connection} RETURN related`
const result = await instance.cypher(statement, { id })
let response = result.records.map(r => r.get('related').properties)
if (returnType === 'object') response = response[0] || null
return response
}
}
const booleanResolver = obj => {
const resolvers = {}
for (const [key, condition] of Object.entries(obj)) {
resolvers[key] = async (parent, params, { cypherParams }, resolveInfo) => {
if (typeof parent[key] !== 'undefined') return parent[key]
const result = await instance.cypher(
`
${condition.replace('this', 'this {id: $parent.id}')} as ${key}`,
{
parent,
cypherParams,
},
)
const [record] = result.records
return record.get(key)
}
}
return resolvers
}
const countResolver = obj => {
const resolvers = {}
for (const [key, connection] of Object.entries(obj)) {
resolvers[key] = async (parent, params, context, resolveInfo) => {
if (typeof parent[key] !== 'undefined') return parent[key]
const id = parent[idAttribute]
const statement = `
MATCH(u:${type} {${idAttribute}: {id}})${connection}
RETURN COUNT(DISTINCT(related)) as count
`
const result = await instance.cypher(statement, { id })
const [response] = result.records.map(r => r.get('count').toNumber())
return response
}
}
return resolvers
}
const hasManyResolver = obj => {
const resolvers = {}
for (const [key, connection] of Object.entries(obj)) {
resolvers[key] = _hasResolver(resolvers, { key, connection }, { returnType: 'iterable' })
}
return resolvers
}
const hasOneResolver = obj => {
const resolvers = {}
for (const [key, connection] of Object.entries(obj)) {
resolvers[key] = _hasResolver(resolvers, { key, connection }, { returnType: 'object' })
}
return resolvers
}
const result = {
...undefinedToNullResolver(undefinedToNull),
...booleanResolver(boolean),
...countResolver(count),
...hasOneResolver(hasOne),
...hasManyResolver(hasMany),
}
return result
}