Skip to content
This repository was archived by the owner on Sep 3, 2021. It is now read-only.

[BUG] Nested ordering errors #272

Closed
Crodaycat opened this issue Jul 7, 2019 · 12 comments
Closed

[BUG] Nested ordering errors #272

Crodaycat opened this issue Jul 7, 2019 · 12 comments
Labels
Priority Bug Fix Issue is a bug impacting users that should be prioritized

Comments

@Crodaycat
Copy link

Crodaycat commented Jul 7, 2019

When i try to order the values of a related entity it throws an error:

query {
  Chat {
    id
    messages(orderBy: [sentAt_desc]) {
      text
      sentAt {
        year
        month
        day
      }
      sender {
        id
        name
      }
    }
  }
}

Get this error:

Failed to invoke function `apoc.coll.sortMulti`: Caused by: java.lang.ClassCastException: java.util.HashMap cannot be cast to java.lang.Comparable

We are using:

  • apollo-server: 2.6.4
  • neo4j-driver: 1.7.4
  • neo4j-graphql-js: 2.6.3
@johnymontana
Copy link
Contributor

Could you share your GraphQL type definitions so I can try to reproduce? (or at least the Chat and message types).

I'm assuming sentAt is a Date type?

@Crodaycat
Copy link
Author

sentAt is a DateTime type, here are the types definitios for Chat and Message:

type Chat {
  id: String!
  checkIn: DateTime
  checkOut: DateTime
  createdAt: DateTime!
  updatedAt: DateTime!
  roomNumber: Int!
  owner: User! @relation(name: "OWNS", direction: "IN")
  messages: [Message] @relation(name: "BELONGS_TO", direction: "IN")
  hotel: Hotel @relation(name: "BELONGS_TO", direction: "OUT")
}

type Message {
  id: String!
  text: String!
  sentAt: DateTime!
  sender: User! @relation(name: "SENT", direction: "IN")
  chat: Chat! @relation(name: "BELONGS_TO", direction: "OUT")
}

@andrashee
Copy link

I'm facing the same problem with a DateTime field. Any news on this?

@mattwr18
Copy link

Hey, we are also dealing with this issue. We would like to refactor our code base to use Temporal values like DateTime, but ran into an issue when we try to use orderBy: createdAt_asc for nested attributes like comments on a Post

Post(id: "p15") {
  comments(orderBy: createdAt_asc) {
    content
    createdAt {
      formatted
    }
  }
}

In our case, the neo4j-graphql-js generated query look like this

MATCH (`post`:`Post` {id:$id, deleted:$deleted, disabled:$disabled}) WITH `post` ORDER BY post.createdAt DESC RETURN `post` { .deleted , .disabled , .id , .id , .title , .content , .contentExcerpt ,createdAt: { formatted: toString(`post`.createdAt) }, .disabled , .deleted , .slug , .image ,author: head([(`post`)<-[:`WROTE`]-(`post_author`:`User`) | post_author { .id , .slug , .name , .avatar , .disabled , .deleted ,shoutedCount: apoc.cypher.runFirstColumn("MATCH (this)-[: SHOUTED]->(r: Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)", {this: post_author, cypherParams: $cypherParams}, false),contributionsCount: apoc.cypher.runFirstColumn("MATCH (this)-[: WROTE]->(r: Post)
WHERE NOT r.deleted = true AND NOT r.disabled = true
RETURN COUNT(r)", {this: post_author, cypherParams: $cypherParams}, false),commentedCount: apoc.cypher.runFirstColumn("MATCH (this)-[: WROTE]->(: Comment)-[: COMMENTS]->(p: Post) WHERE NOT p.deleted = true AND NOT p.disabled = true RETURN COUNT(DISTINCT(p))", {this: post_author, cypherParams: $cypherParams}, false),followedByCount: apoc.cypher.runFirstColumn("MATCH (this)<-[: FOLLOWS]-(r: User) RETURN COUNT(DISTINCT r)", {this: post_author, cypherParams: $cypherParams}, false),followedByCurrentUser: apoc.cypher.runFirstColumn("MATCH (this)<-[: FOLLOWS]-(u: User { id: $cypherParams.currentUserId})
RETURN COUNT(u) >= 1", {this: post_author, cypherParams: $cypherParams}, false),location: head([ post_author_location IN apoc.cypher.runFirstColumn("MATCH (this)-[: IS_IN]->(l: Location) RETURN l", {this: post_author, cypherParams: $cypherParams}, true) | post_author_location { .nameEN }]) ,badges: [(`post_author`)<-[:`REWARDED`]-(`post_author_badges`:`Badge`) | post_author_badges { .id , .icon }] }]) ,tags: [(`post`)-[:`TAGGED`]->(`post_tags`:`Tag`) | post_tags { .id }] ,categories: [(`post`)-[:`CATEGORIZED`]->(`post_categories`:`Category`) | post_categories { .id , .name , .icon }] ,commentsCount: apoc.cypher.runFirstColumn("MATCH (this)<-[: COMMENTS]-(r: Comment) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)", {this: post, cypherParams: $cypherParams}, false),shoutedCount: apoc.cypher.runFirstColumn("MATCH (this)<-[: SHOUTED]-(r: User) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)", {this: post, cypherParams: $cypherParams}, false),shoutedByCurrentUser: apoc.cypher.runFirstColumn("MATCH (this)<-[: SHOUTED]-(u: User { id: $cypherParams.currentUserId}) RETURN COUNT(u) >= 1", {this: post, cypherParams: $cypherParams}, false),emotionsCount: apoc.cypher.runFirstColumn("MATCH (this)<-[emoted: EMOTED]-(: User) RETURN COUNT(DISTINCT emoted)", {this: post, cypherParams: $cypherParams}, false),comments: [sortedElement IN apoc.coll.sortMulti([(`post`)<-[:`COMMENTS`]-(`post_comments`:`Comment`) | post_comments { .deleted , .disabled , .id , .id ,createdAt: `post_comments`.createdAt, .disabled , .deleted , .content , .contentExcerpt ,author: head([(`post_comments`)<-[:`WROTE`]-(`post_comments_author`:`User`) | post_comments_author { .id , .slug , .name , .avatar , .disabled , .deleted ,shoutedCount: apoc.cypher.runFirstColumn("MATCH (this)-[: SHOUTED]->(r: Post) WHERE NOT r.deleted = true AND NOT r.disabled = true RETURN COUNT(DISTINCT r)", {this: post_comments_author, cypherParams: $cypherParams}, false),contributionsCount: apoc.cypher.runFirstColumn("MATCH (this)-[: WROTE]->(r: Post)
WHERE NOT r.deleted = true AND NOT r.disabled = true
RETURN COUNT(r)", {this: post_comments_author, cypherParams: $cypherParams}, false),commentedCount: apoc.cypher.runFirstColumn("MATCH (this)-[: WROTE]->(: Comment)-[: COMMENTS]->(p: Post) WHERE NOT p.deleted = true AND NOT p.disabled = true RETURN COUNT(DISTINCT(p))", {this: post_comments_author, cypherParams: $cypherParams}, false),followedByCount: apoc.cypher.runFirstColumn("MATCH (this)<-[: FOLLOWS]-(r: User) RETURN COUNT(DISTINCT r)", {this: post_comments_author, cypherParams: $cypherParams}, false),followedByCurrentUser: apoc.cypher.runFirstColumn("MATCH (this)<-[: FOLLOWS]-(u: User { id: $cypherParams.currentUserId})
RETURN COUNT(u) >= 1", {this: post_comments_author, cypherParams: $cypherParams}, false),location: head([ post_comments_author_location IN apoc.cypher.runFirstColumn("MATCH (this)-[: IS_IN]->(l: Location) RETURN l", {this: post_comments_author, cypherParams: $cypherParams}, true) | post_comments_author_location { .nameEN }]) ,badges: [(`post_comments_author`)<-[:`REWARDED`]-(`post_comments_author_badges`:`Badge`) | post_comments_author_badges { .id , .icon }] }]) }], ['^createdAt']) | sortedElement { .*,  }] } AS `post`

and the error thrown in the console is

Error GraphQL error: Invalid input '}': expected whitespace, comment, literal entry, property selector, variable selector or all properties selector (line 7, column 512 (offset: 4643))
"RETURN COUNT(u) >= 1", {this: post_comments_author, cypherParams: $cypherParams}, false),location: head([ post_comments_author_location IN apoc.cypher.runFirstColumn("MATCH (this)-[: IS_IN]->(l: Location) RETURN l", {this: post_comments_author, cypherParams: $cypherParams}, true) | post_comments_author_location { .nameEN }]) ,badges: [(`post_comments_author`)<-[:`REWARDED`]-(`post_comments_author_badges`:`Badge`) | post_comments_author_badges { .id , .icon }] }]) }], ['^createdAt']) | 
sortedElement { .*,  }] } AS `post`"
                     ^

Here is our Post and Comment schema in case it's helpful for anyone

type Post {
  id: ID!
  activityId: String
  objectId: String
  author: User @relation(name: "WROTE", direction: "IN")
  title: String!
  slug: String
  content: String!
  contentExcerpt: String
  image: String
  imageUpload: Upload
  visibility: Visibility
  deleted: Boolean
  disabled: Boolean
  disabledBy: User @relation(name: "DISABLED", direction: "IN")
  createdAt: DateTime
  updatedAt: String
  language: String
  relatedContributions: [Post]!
  @cypher(
  statement: """
  MATCH (this)-[: TAGGED|CATEGORIZED]->(categoryOrTag)<-[: TAGGED|CATEGORIZED]-(post: 
  Post)
  WHERE NOT post.deleted AND NOT post.disabled
  RETURN DISTINCT post
  LIMIT 10
  """
  )

  tags: [Tag]! @relation(name: "TAGGED", direction: "OUT")
  categories: [Category]! @relation(name: "CATEGORIZED", direction: "OUT")

  comments: [Comment]! @relation(name: "COMMENTS", direction: "IN")
  commentsCount: Int!
  @cypher(
  statement: "MATCH (this)<-[: COMMENTS]-(r: Comment) WHERE NOT r.deleted = true AND NOT 
  r.disabled = true RETURN COUNT(DISTINCT r)"
  )

  shoutedBy: [User]! @relation(name: "SHOUTED", direction: "IN")
  shoutedCount: Int!
  @cypher(
  statement: "MATCH (this)<-[: SHOUTED]-(r: User) WHERE NOT r.deleted = true AND NOT 
  r.disabled = true RETURN COUNT(DISTINCT r)"
  )

  # Has the currently logged in user shouted that post?
  shoutedByCurrentUser: Boolean!
  @cypher(
  statement: "MATCH (this)<-[: SHOUTED]-(u: User { id: $cypherParams.currentUserId}) RETURN 
  COUNT(u) >= 1"
  )

  emotions: [EMOTED]
  emotionsCount: Int!
  @cypher(statement: "MATCH (this)<-[emoted: EMOTED]-(: User) RETURN COUNT(DISTINCT 
  emoted)")
}

input _PostInput {
  id: ID!
}

type Mutation {
  CreatePost(
  id: ID
  title: String!
  slug: String
  content: String!
  image: String
  imageUpload: Upload
  visibility: Visibility
  language: String
  categoryIds: [ID]
  contentExcerpt: String
  ): Post
  UpdatePost(
  id: ID!
  title: String!
  slug: String
  content: String!
  contentExcerpt: String
  image: String
  imageUpload: Upload
  visibility: Visibility
  language: String
  categoryIds: [ID]
  ): Post
  DeletePost(id: ID!): Post
  AddPostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED
  RemovePostEmotions(to: _PostInput!, data: _EMOTEDInput!): EMOTED
}
type Query {
  PostsEmotionsCountByEmotion(postId: ID!, data: _EMOTEDInput!): Int!
  PostsEmotionsByCurrentUser(postId: ID!): [String]
}
type Comment {
  id: ID!
  activityId: String
  author: User @relation(name: "WROTE", direction: "IN")
  content: String!
  contentExcerpt: String
  post: Post @relation(name: "COMMENTS", direction: "OUT")
  createdAt: DateTime
  updatedAt: String
  deleted: Boolean
  disabled: Boolean
  disabledBy: User @relation(name: "DISABLED", direction: "IN")
}

type Mutation {
  CreateComment(
  id: ID
  postId: ID!
  content: String!
  contentExcerpt: String
  deleted: Boolean
  disabled: Boolean
  ): Comment
  UpdateComment(
  id: ID!
  content: String!
  contentExcerpt: String
  deleted: Boolean
  disabled: Boolean
  ): Comment
  DeleteComment(id: ID!): Comment
}

Wanna say a big thank you to @johnymontana and the other contributors to this library.
Maybe, we can help come to a solution for this issue.

@g3rd
Copy link

g3rd commented Nov 1, 2019

Has anyone found a workaround or fix for this issue?

@johnymontana johnymontana added the Priority Bug Fix Issue is a bug impacting users that should be prioritized label Nov 3, 2019
@johnymontana
Copy link
Contributor

Thanks for pinging this @g3rd - will try to address it this week.

I suppose a temporary workaround might be to store an additional property that is the epoch milliseconds version of the timestamp as a Long and use that for ordering instead of the DateTime field?

@debugpoint136
Copy link

debugpoint136 commented Nov 17, 2019

I am facing the same problem on spatial Point as well.

Error-report.pdf

@mobsean
Copy link

mobsean commented Feb 19, 2020

Thanks for pinging this @g3rd - will try to address it this week.

I suppose a temporary workaround might be to store an additional property that is the epoch milliseconds version of the timestamp as a Long and use that for ordering instead of the DateTime field?

The proposed workaround works! Thanks!

@loicmarie
Copy link

This issue still occurs in 2.16.3 with nested requests such as:

query ($id: ID!, $first: Int!, $offset: Int!) {
  User(id: $id) {
    posts(first: $first, offset: $offset, orderBy: createdAt_desc) {
      id
    }
  }
}

@WTX1
Copy link

WTX1 commented Oct 3, 2020

This issue still occurs in 2.16.3 with nested requests such as:

query ($id: ID!, $first: Int!, $offset: Int!) {
  User(id: $id) {
    posts(first: $first, offset: $offset, orderBy: createdAt_desc) {
      id
    }
  }
}

Hi
Make sure you have installed the APOC library first

You can follow the instructions here : https://neo4j.com/labs/apoc/4.1/installation/

@Kubera2017
Copy link

@erik7z
Copy link

erik7z commented Feb 24, 2021

Sorting by date works when you requesting it only from one entity:

query {
  Question {
    nodeId
    comments(orderBy: createdAt_asc) {
      nodeId
      text
    }
  }
}

But if you requesting it on more entities, than it fails:

query {
  Question {
    nodeId
    comments(orderBy: createdAt_asc) {
      nodeId
      text
    }
    answers(orderBy: createdAt_asc) {
      nodeId
      text
    }
  }
}

and gives following error:
Neo4jError: Failed to invoke function apoc.coll.sortMulti: Caused by: java.lang.ClassCastException: class java.util.HashMap cannot be cast to class java.lang.Comparable (java.util.HashMap and java.lang.Comparable are in module java.base of loader 'bootstrap')

# for free to subscribe to this conversation on GitHub. Already have an account? #.
Labels
Priority Bug Fix Issue is a bug impacting users that should be prioritized
Projects
None yet
Development

No branches or pull requests