Open steezeburger opened 4 years ago
I'm trying to implement a workaround, and custom resolvers for fields with @neo4j_ignore
directives are receiving arguments of object = {}, ...
so I'm not even able to build the datetime in javascript because I don't have the parent object data available in the custom resolver.
export const resolvers = {
Mutation: {
createSomeType,
updateSomeType,
},
SessionRule: {
startDateTime: async (object, params, context, info) => {
const startDateTime = await runCypherQuery(
context.driver,
'MATCH (sr:SomeType {id: $id})-[:IN]->(s:SomeOtherType) RETURN s.start',
{ id: object.id },
);
const momentDate = moment.utc(startDateTime);
const dateObj = momentDate.toObject();
return {
year: dateObj.years,
month: dateObj.months,
day: dateObj.date,
hour: dateObj.hours,
minute: dateObj.minutes,
second: dateObj.seconds,
millisecond: dateObj.milliseconds,
formatted: momentDate.toISOString(),
};
},
},
};
object.id
is always undefined for queries.
object
is actually the parent object when the field is resolved for a mutation.
FYI, the following works properly, so I know it's not an issue with my cypher.
startDateTime: Int @cypher(statement:"""
MATCH (this)-[:IN]->(s:SomeOtherType)
WITH s.start as startDateTime
RETURN startDateTime.year
""")
I am having the same issue. I could not use the cypher directive to achieve this; I am getting the same error. One could implement a utility function that takes a cypher fragment and resolves to a _Neo4jDateTime
object. An example would be:
const neo4jDateTimeResolver = ({
fragment, // A fragment of a cypher query such as MATCH (this)-[:IN]->(s:SomeOtherType)
field, // such as 's.start'
parameters = {},
order = 'DESC',
}) => async (node, _, { cypherParams, driver }) => {
try {
const session = driver.session()
const neo4jDateTime = await session.readTransaction(async txc => {
const { records } = await txc.run(`
MATCH (this:Node {id: $_nodeId})
${fragment}
WITH ${field} as dt
RETURN dt {.year, .month, .day, .hour, .minute, .second, .millisecond, .microsecond, .nanosecond, .timezone, formatted: apoc.date.toISO8601(dt.epochMillis)}
ORDER BY dt ${order}
LIMIT 1
`,
{ _nodeId: node.id, cypherParams, ...parameters },
)
return records.map(record => record.get('dt'))[0]
})
await session.close()
// neo4jDateTime will be of the form { year: Integer, month: Integer, ..., formatted: string} or undefined
// Integer coming from the neo4j-driver here:
// https://github.com/neo4j/neo4j-javascript-driver/blob/88e646d4664943e337aecb7d61c187407926cc46/src/integer.js#L39
if (neo4jDateTime) {
let object = {}
// map this to a javascript object with integers using Integer.toInt()
// https://github.com/neo4j/neo4j-javascript-driver/blob/88e646d4664943e337aecb7d61c187407926cc46/src/integer.js#L85
Object.entries(neo4jDateTime).forEach(([key, value]) => {
if (value.constructor.name === 'Integer') {
object[key] = value.toInt()
} else {
object[key] = value
}
})
return object
} else {
return null
}
} catch (error) {
// Do something with error
return null
}
}
First notice before the fragment I am matching a :Node
with indexed id
field. In the graph that I am using this is true for all types. The parent object node
is an object that consists of the fields that were queried for. The id
field must be queried along with the DateTime
fields in order for this to work, but the client is always querying the id
field for caching. Second, the matched node is called this
to help with the fragment. It is not constructed the same way this library constructs this
; it just reads the same. The cypherParams
are passed as well.
In your example
type SessionRule {
startDateTime: DateTime
}
then in resolvers use:
export const resolvers = {
Mutation: {
createSomeType,
updateSomeType,
},
SessionRule: {
startDateTime: neo4jDateTimeResolver({
fragment: `MATCH (this)-[:IN]->(s:SomeOtherType)`,
field: 's.start'
}),
},
};
I understand that the issue is asking a question about returning a DateTime
object using the cypher directive. This is one way that you can return the DateTime
object. If the schema is flexible maybe return a scalar like String in the form of an ISO8601 string using apoc
. Something like:
type SessionRule {
startDateTimeISO: String @cypher(statement:"""
MATCH (this)-[:IN]->(s:SomeOtherType)
WITH s.start as dt
RETURN apoc.date.toISO8601(dt.epochMillis)
""")
}
This is obviously only a workaround until the bug is worked out and my use cases may be simpler than yours, but I hope this helps.
@michaeldgraham @johnymontana Has this been identified as a bug?
I am still experiencing this issue on
neo4j-driver: 4.2.1
neo4j-graphql-js: 2.19.1
I may have found a bug.
imagine
SomeType
with property:for the following query:
I receive the following error:
Is the full error getting cut off? It doesn't even make sense to me haha.
I've also tried:
but I get
null
for every value:I'm at a loss for how to use a
@cypher
directive to return a DateTime type. Any ideas?