filter parameter can be added as part of augmentSchema, similar to functionality implemented in the neo4j-graphql database plugin. See: https://grandstack.io/docs/neo4j-graphql-plugin.html#auto-generated-query-types
Hello,
But, what if I don't want to use makeAugmentedSchema?
I don't want the auto-generated resolvers, querys and mutations, but I want to filter by some fields,
It's that possible? How and where do I implement the augmentSchema?
Thanks
[Edited: Solution in next comment.]
I tried this:
const {makeExecutableSchema} = require('graphql-tools');
const {transpileSchema} = require('graphql-s2s').graphqls2s;
const glue = require('schemaglue');
const { augmentSchema } = require('neo4j-graphql-js');
const {schema, resolver} = glue('graphql');
module.exports = makeExecutableSchema({
typeDefs: [transpileSchema(augmentSchema(schema))], // <--this
resolvers: resolver,
});
But got:
.../node_modules/neo4j-graphql-js/dist/augmentSchema.js:74
var typeMap = schema.getTypeMap();
^
TypeError: schema.getTypeMap is not a function
at extractTypeMapFromSchema (.../node_modules/neo4j-graphql-js/dist/augmentSchema.js:74:24)
at augmentSchema (.../node_modules/neo4j-graphql-js/dist/index.js:521:61)
at Object.<anonymous> (.../graphql/index.js:12:13)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Module.require (module.js:596:17)
at require (internal/module.js:11:18)
Hello,
But, what if I don't want to use
makeAugmentedSchema?
I don't want the auto-generated resolvers, querys and mutations, but I want to filter by some fields,It's that possible? How and where do I implement the
augmentSchema?Thanks
I tried this:
const {makeExecutableSchema} = require('graphql-tools'); const {transpileSchema} = require('graphql-s2s').graphqls2s; const glue = require('schemaglue'); const { augmentSchema } = require('neo4j-graphql-js'); const {schema, resolver} = glue('graphql'); module.exports = makeExecutableSchema({ typeDefs: [transpileSchema(augmentSchema(schema))], // <--this resolvers: resolver, });But got:
.../node_modules/neo4j-graphql-js/dist/augmentSchema.js:74 var typeMap = schema.getTypeMap(); ^ TypeError: schema.getTypeMap is not a function at extractTypeMapFromSchema (.../node_modules/neo4j-graphql-js/dist/augmentSchema.js:74:24) at augmentSchema (.../node_modules/neo4j-graphql-js/dist/index.js:521:61) at Object.<anonymous> (.../graphql/index.js:12:13) at Module._compile (module.js:652:30) at Object.Module._extensions..js (module.js:663:10) at Module.load (module.js:565:32) at tryModuleLoad (module.js:505:12) at Function.Module._load (module.js:497:3) at Module.require (module.js:596:17) at require (internal/module.js:11:18)
Now I'm a step forward, this solve my previus problem:
const {makeExecutableSchema} = require('graphql-tools');
const {transpileSchema} = require('graphql-s2s').graphqls2s;
const glue = require('schemaglue');
const { augmentSchema } = require('neo4j-graphql-js');
const {schema, resolver} = glue('graphql');
const executableSchema = makeExecutableSchema({
typeDefs: [transpileSchema(schema)],
resolvers: resolver,
});
module.exports = augmentSchema(executableSchema);
But i still can't use the filter parameter and can't avoid the auto generated create, update, delete querys.
Query:
{
service(first:3, filter:"a"){
name
}
}
Result:
{
"errors": [
{
"message": "Unknown argument \"filter\" on field \"service\" of type \"Query\".",
"locations": [
{
"line": 3,
"column": 18
}
]
}
]
}
Schema:
type Service inherits Node {
name: String
}
type Query {
service():[Service]
}
Resolver:
.
.
.
exports.resolver = {
Query: {
service: (object, params, context, resolveInfo) => {
return neo4jgraphql(object, params, context, resolveInfo, process.env.NODE_ENV === 'development')
},
},
};
PD: It's work with first and offset.
Any idea how to implements the filter?
Thanks.
Hi @rdimicheleb - in general, yes you can selectively make use of the features that augmentSchema adds to the schema by manually adding them to your schema (like first, offset, etc) and then in your resolver delegating out to theneo4jgraphql function.
We've also added the ability to configure which types are included in the schema augmentation process (or to turn off the auto-generated queries/mutations altogether): https://grandstack.io/docs/neo4j-graphql-js.html#configuring-schema-augmentation
Note however that we have not yet implemented the filter argument in neo4j-graphql.js, but hope to get to this soon!
As requested a few use cases:
To give a flavour of what we're trying to model, it's all about organisational structure and how this relates to physical assets. Hugely simplified it looks a bit like this:
(Department)-[:HAS_TEAM]->(:Team)<-[:SUPPORTED_BY]-(System)-[:DEPENDS_ON]->(:System)
Examples of what we might use filters like the above are as follows. I've provided, for clarity, roughly what the cypher queries would look like, assuming that the first node in each query is given by the entry point of the graphql query. May include mistakes 馃槤
MATCH (t:Team) WHERE d.name ENDS WITH " operations"MATCH (s:System) WHERE count((s)-[:SUPPORTED_BY]->(:Team)) = 0MATCH (s:System)-[rel:DEPENDS_ON]->(dep:System) WHERE rel.environment = "production"MATCH (t:Team)<-[:SUPPORTED_BY]-(s:System)-[:DEPENDS_ON]->(dep:System) WHERE dep.code = "neo4j"MATCH (d:Department)-[:HAS_TEAM]->(t:Team)<-[:SUPPORTED_BY]-(s:System)-[:DEPENDS_ON]->(dep:System) WHERE dep.code = "neo4j"Thanks a bunch - this really helps give us an idea of what to support in filter.
btw - I think you can currently do
and 6. Finding unsupported services MATCH (s:System) WHERE count((s)-[:SUPPORTED_BY]->(:Team)) = 0
with a computed field @cypher directive, since it's an equality.
type System {
name: String
supportingTeamsCount: Int @cypher("RETURN SIZE( (this)-[:SUPPORTED_BY]->(:Team)" )
}
then the query would be:
{
System(supportingTeamCout: 0) {
name
}
}
but would be good to support this in a more general way
Yeah I've written something similar. Worked as a property definition but not as a filter, but we're on version 0.x of the library. Will upgrade when I have time and let you know if it works
@johnymontana any rough timeline on this? I'd definitely jump in with both feet if filter was supported.
We've got this on the roadmap for the next epic after we finish up a few other things. So look for it to be added within the next month or so.
@johnymontana we're desperately looking forward to filter for relationships. Here is a use-case:
type Post {
disabled: Boolean! @cypher(statement: "OPTIONAL MATCH (this)<-[d:DISABLED]-() RETURN d IS NOT NULL")
disabledBy: User @relation(name: "DISABLED", direction: "IN")
}
Here's what I would like to do:
{
Post(disabled: true) {
id
title
disabled
disabledBy {
name
}
}
}
@johnymontana what you suggested here does not work:
Here is the generated cypher query:
MATCH (`post`:`Post` {disabled:$disabled}) RETURN `post` { .id , .title ,disabled: apoc.cypher.runFirstColumn("OPTIONAL MATCH (this)<-[d:DISABLED]-() RETURN d IS NOT NULL", {this: post}, false),disabledBy: head([(`post`)<-[:`DISABLED`]-(`post_disabledBy`:`User`) | post_disabledBy { .name }]) } AS `post` SKIP $offset
We've added a generated filter argument in v2.5.0. The docs here shows some examples.
Please test it out and let us know how it works for your use cases.
@johnymontana I tried what you pointed out to @roschaefer with v2.5.0.
I used a kind of trick to get what we want, but in a direct manner it did鈥檛 worked:
graphql.schema:
type User {
id: ID!
name: String
role: UserGroupEnum
}
enum UserGroupEnum {
admin
moderator
user
}
type Post {
id: ID!
title: String!
content: String!
disabled: Boolean! @cypher(statement: "OPTIONAL MATCH (this)<-[d:DISABLED]-() RETURN d IS NOT NULL")
disabledBy: User @relation(name: "DISABLED", direction: "IN")
userDisabledThis(filter: _UserFilter): User @relation(name: "DISABLED", direction: IN)
}
input _UserFilter {
id: ID
role_in: [UserGroupEnum!]
}
Query we would like, as showed here.
Doesn't work:
{
Post(disabled: true) {
id
disabled
}
Because disabled as computed field @cypher directive seems not to be supported as a real parameter with a specific value in the query.
The following works, but is kind of indirectly through a filter:
{
Post(filter: { userDisabledThis: { role_in: [admin,moderator,user] } }) {
id
disabled
}
We have to put all possible roles of a user into the filter to be sure we miss no User disable a Post.
The first and not working example would be not a kind of workaround, but straightforward.
Is there a possibility to get there?
@Tirokk We decided not to add @cypher directive fields to the auto-generated filter initially because of performance considerations, but I suppose we could reconsider this. Perhaps make this configurable to allow the user to include these?
That'd be great @johnymontana. I can think of lots of use cases where users might want to filter for things that do/don't have a given relationship, which we expose using a @cypher directive that converts the existence of relationships into a Boolean
It would be very helpful if you would consider the possibility to have @cypher directives that converts the existence of relationships into a Booleans, @johnymontana. Please make this configurable to include these. There are lots of use cases like @wheresrhys said.
The current filters can be used to filter for nodes that have/do not have relationship fields defined with @relation. For example, using this Codesandbox example:
Movies that have no (:Movie)<-[:ACTED_IN]-(:Actor) relationships
{
Movie(filter: { actors: null }) {
title
}
}
Movies that have at least one (:Movie)<-[:ACTED_IN]-(:Actor) relationship
{
Movie(filter: { actors_not: null }, first: 10) {
title
}
}
These filters can be nested as well:
What genres are connected to the movies with no actors?
{
Genre(filter: { movies_some: { actors: null } }) {
name
}
}
This currently does not work with relationship types. For example, (:User)-[:RATED]->(:Movie) in this example, but that will be added soon.
Does this address your use cases @Tirokk @wheresrhys @roschaefer ?
I'm hesitant to expose filtering on @cypher fields too widely, because I think this could quickly lead to sub-optimal performance in some cases.
Most helpful comment
We've added a generated
filterargument in v2.5.0. The docs here shows some examples.Please test it out and let us know how it works for your use cases.