cancel
Showing results for 
Search instead for 
Did you mean: 

Poor query performance with only about 30 nodes

DevDan
Node Link

I have a very small test set of user, collection and object nodes, only about 30. I use the neo4j-graphql-js package to setup a node graphql server to handle requests.

My graphql queries usually take between 300ms to 900ms. Now, they sometimes take 30 seconds for the initial query and back to 300ms on subsequent fetches.

I recently changed @auth for my object and collection nodes schemas, it now look ups user permissions via relationships. I don't see how this could cause some of my queries to go to 20-30 full seconds.

Here is my schema:

 

type User {
    id: ID! @id(autogenerate: true)
    authId: String! @unique
    email: String
    editCollections: [Collection!]!
      @relationship(type: "EDIT", direction: OUT)
    readCollections: [Collection!]!
      @relationship(type: "READ", direction: OUT)
    editObjects: [Object!]!
      @relationship(type: "EDIT", direction: OUT)
    readObjects: [Object!]!
      @relationship(type: "READ", direction: OUT)
    createdAt: DateTime! @timestamp(operations: [CREATE])
    updatedAt: DateTime! @timestamp(operations: [UPDATE])
  }

  extend type User @auth(
    rules: [
      {
        operations: [UPDATE],
        isAuthenticated: true,
        bind: { authId: "$jwt.sub" }
      },
      {
        isAuthenticated: true,
        allow: { authId: "$jwt.sub" }
      },
      {
        operations: [READ],
        isAuthenticated: true,
        allow: {
          OR: [
            { editCollections: { editUsers: { authId: "$jwt.sub" } } },
            { editCollections: { readUsers: { authId: "$jwt.sub" } } },
            { readCollections: { editUsers: { authId: "$jwt.sub" } } },
            { readCollections: { readUsers: { authId: "$jwt.sub" } } }
          ]
        }
      }
    ]
  )
 
 type Collection {
    id: ID! @id(autogenerate: true)
    name: String!
    selfCollection: Boolean!
    trashCollection: Boolean!
    editUsers: [User!]!
      @relationship(type: "EDIT", direction: IN)
    readUsers: [User!]!
      @relationship(type: "READ", direction: IN)
    containsObjects: [Object!]!
      @relationship(type: "CONTAINS", direction: OUT, properties: "Contains")
    rootsObjects: [Object!]!
      @relationship(type: "ROOTS", direction: OUT)
    createdAt: DateTime! @timestamp(operations: [CREATE])
    createdBy: String!
    updatedAt: DateTime! @timestamp(operations: [UPDATE])
    updatedBy: String!
  }

  extend type Collection @auth(rules: [
    {
      operations: [READ],
      isAuthenticated: true,
      allow: {
        OR: [
          { editUsers: { authId: "$jwt.sub" } },
          { readUsers: { authId: "$jwt.sub" } }
        ]
      }
    }
    {
      operations: [CREATE, UPDATE, CONNECT, DISCONNECT, DELETE]
      isAuthenticated: true,
      allow: {
        editUsers: { authId: "$jwt.sub" }
      }
    }
  ])
 
type Object {
    id: ID! @id(autogenerate: true)
    name: String!
    collections: [Collection!]!
      @relationship(type: "CONTAINS", direction: IN, properties: "Contains")
    rootCollections: [Collection!]!
      @relationship(type: "ROOTS", direction: IN)
    containsObjects: [Object!]!
      @relationship(type: "CONTAINS", direction: OUT, properties: "Contains")
    objectContains: [Object!]!
      @relationship(type: "CONTAINS", direction: IN, properties: "Contains")
    createdAt: DateTime! @timestamp(operations: [CREATE])
    createdBy: String!
    updatedAt: DateTime! @timestamp(operations: [UPDATE])
    updatedBy: String!
    removedAt: DateTime @timestamp(operations: [CREATE])
    removedBy: String
    images: [Image]
    parentRelation: String
      @cypher(
        statement: "MATCH (this)<-[c:CONTAINS]-(p:Object) RETURN c.relation"
      )
    childrenIds: [String!]!
      @cypher(
        statement: "MATCH (this)-[:CONTAINS*]->(c:Object) RETURN collect(c.id)"
      )
  }

  extend type Object @auth(rules: [
    {
      operations: [READ],
      isAuthenticated: true,
      allow: {
        OR: [
          { rootCollections: { editUsers: { authId: "$jwt.sub" } } },
          { rootCollections: { readUsers: { authId: "$jwt.sub" } } }
        ]
      }
    }
    {
      operations: [CREATE, UPDATE, CONNECT, DISCONNECT, DELETE]
      isAuthenticated: true,
      allow: {
        rootCollections: { editUsers: { authId: "$jwt.sub" } }
      }
    }
  ])
3 REPLIES 3

luiseduardo
Ninja
Ninja

Hi @DevDan !

Have you created constraints or indexes on the ids or most queried properties? Also, it would really help to see your schema graphically, by executing this on your neo4j browser:

CALL db.schema.visualization() 

 Although 30 nodes is really a small database to have that amount of time, perhaps we can check a profile of the queries that take a lot of time?

 

DevDan
Node Link

Here is my result from calling visualization:

DevDan_0-1666219236002.png

DevDan_1-1666219274877.pngDevDan_2-1666219289586.pngDevDan_3-1666219302385.png

 

If I use @id and @unique for the properties I query on, are they automatically indexed? In the Node properties screenshots above, I see some mentions of indexes in the constraints field, but the indexes field is empty.

 

Hi! If you create a uniqueness constraint you automatically create an index over the uniqueness properties, single or composite. For what I can see you have already did the creation of the constraints, so you should have the indexes also created, but they don't show on db.schema.visualization(), if you execute in browser SHOW CONSTRAINTS the constraints should be there.

Could you also share the creation query that you're using and an explain of it? Just put EXPLAIN before the query and execute it, then you can share the execution plan image here. Maybe the query isn't using the properties that you have indexed.

Cheers!