0

Consider the following database schema:

type Actor {
    actorId: ID!
    name: String
    movies: [Movie!]! @relationship(type: "ACTED_IN", direction: OUT)
}

type Movie {
    movieId: ID!
    title: String
    description: String
    year: Int
    actors(limit: Int = 10): [Actor!]! @relationship(type: "ACTED_IN", direction: IN)
}

Now, I want to know what the top movies with most number of actors, along with the counts. The following Cypher works perfectly in Neo4j:

type Query {
    getMoviesWithMostActors(limit: Int = 5): [Movie]
        (
            statement: """
                MATCH (movie:Movie)
                MATCH (movie) <-[act:ACTED_IN]- (:Actor)
                WITH movie, count(act) AS actorCount
                    ORDER BY actorCount DESCENDING
                    LIMIT $limit
                RETURN movie {.*, numActors: actorCount}
            """
        )
}

However, it fails in GraphQL playground. I tried the following:

query {
    this_works: getMoviesWithMostActorsbase(limit: 2) {
        movieId
    }

    this_does_not_work: getMoviesWithMostActorsbase(limit: 2) {
        movieId
        numActors
    }
}

This returned: GRAPHQL_VALIDATION_FAILED.

"GraphQLError: Cannot query field \"numActors\" on type \"Movie\"."

My question is how do I return temporary properties without modifying the type definitions itself. And since this is a dummy example and in fact I need to do it with multiple types of nodes and with different types of scoring (int/float/array), I want to know how to do it without frequently editing the schema whenever I want to add a new query.

Known Workarounds

  1. Extend schema with multiple nullable properties.
    • Schema needs to be changed with every new query
  2. Return a map and simulate a node object, as shown below.
    • Need to add 2 more types for every single type of nodes
    • Actual object types are lost
type MovieData {
    identity: Int!
    labels: [String]!
    properties: Movie!
}

type MovieWithScore {
    movieData: MovieData!
    movieScore: String!
}

type Query {
    getMoviesWithMostActors(limit: Int = 5): [MovieWithScore]
        (
            statement: """
                MATCH (movie:Movie)
                MATCH (movie) <-[act:ACTED_IN]- (:Actor)
                WITH movie, count(act) AS actorCount
                    ORDER BY actorCount DESCENDING
                    LIMIT $limit
                RETURN {
                    movieData: movie,
                    movieScore: 'number of actors: ' + toString(actorCount)
                }
            """
        )
}
Michel Floyd
  • 18,793
  • 4
  • 24
  • 39
yarnabrina
  • 1,561
  • 1
  • 10
  • 30
  • Previously asked at https://community.neo4j.com/t5/general-discussions/returning-temporary-properties-in-graphql/td-p/62800 – yarnabrina Dec 04 '22 at 11:09
  • You can't get `numActors` because it's not specified in your schema. – Michel Floyd Dec 04 '22 at 19:40
  • @MichelFloyd I understood that much, and even specified it in workaround section. My case is that I do not need numActors outside the scope of this query. And of course this is an example, actually I have plenty of other queries on plenty of different type of nodes. Each of these queries returns some scoring which is specific only to that query, and unused by everything else. I'm very new to graphql, but is it okay/standard in graphql to modify schema for each of the queries? That would mean very very frequent updates to the schema. – yarnabrina Dec 05 '22 at 03:23
  • 1
    It's common to have rarely used fields, yes. In the initial development of a schema and a UI there is a lot of back and forth to get the schema right. After awhile the rate of changes should settle down. You can also run multiple queries with a single request so that can help reduce the need for new queries. – Michel Floyd Dec 05 '22 at 17:00

0 Answers0