graphql icon indicating copy to clipboard operation
graphql copied to clipboard

Thoughts and wishes for API v7

Open andreloeffelmann opened this issue 1 year ago • 8 comments

Since the first alpha releases for API v7 are out, I want to share some thoughts on it.

  • It seems like the new filter operators are coming. Great to hear that! I hope you have #4827 in mind? Would be a really nice feature to have string comparison ignoring case finally :)
  • #5819 removes single element relationships. I understand the intention behind that. However, as already discussed in several tickets, e.g. here, the :1 relationship is a BIG and important thing (a Product has ONE Price and is produced by ONE Company). Our API relies heavily on this feature. The release notes on v7 describe how to use the @cypher directive to achieve the removed behaviour. Since filtering on @cypher fields is possible, this seems to be working. But that's only for READ scenarios. How about WRITE? It is not sufficient to just READ nodes connect in a :1 manner. We also have to CREATE, UPDATE, DELETE them. Is this coming as a new feature as proposed by me in #1962? Or how are you planning to workaround WRITE scenarios?

But overall we are excited and look forward to v7! Is there an approximate timeline as to when we can expect a final release??

andreloeffelmann avatar Jan 08 '25 07:01 andreloeffelmann

Definitely, this kind of feature request is one of the many reasons we are making this change to the input types! We may not add it directly in 7.0.0, but it can certainly be expected not far behind.

I think the important thing to note here is that we are removing this functionality because we simply cannot guarantee the adherence to any particular relationship cardinality. The long term answer to this problem is constraints in the database to control this, but this will be a long long way off. In the interim, I'm afraid that the answer may be more around workarounds. For example, having a Cypher field to read the data, which contains the logic of how to decide which node to return. And then mapping a relationship field which is write-only and using this for the modification of data, just without any guarantees of cardinality. We'll look to do a write up of a workaround such as this because I agree that it's perhaps not intuitive.

But overall we are excited and look forward to v7! Is there an approximate timeline as to when we can expect a final release??

We are aiming for around the end of March/beginning of April. 🙂

darrellwarde avatar Jan 14 '25 15:01 darrellwarde

Hey @darrellwarde, any news on the workaround for CREATE,UPDATE,DELETE mutations to handle the :1 relationships via cypher? Are you still planning to release end of March/beginning of April?

andreloeffelmann avatar Mar 14 '25 09:03 andreloeffelmann

Hey @angrykoala , maybe you can give an answer to my question from above?

Hey @darrellwarde, any news on the workaround for CREATE,UPDATE,DELETE mutations to handle the :1 relationships via cypher? Are you still planning to release end of March/beginning of April?

andreloeffelmann avatar Mar 26 '25 14:03 andreloeffelmann

Hi @andreloeffelmann

Yes, we are still aiming to beginning of April. The 7 alpha is already looking a bit more stable in terms of breaking changes, and now working on fixing some bugs.

Any particular info you want about the 1: cypher workaround?

In general, as Darrell described, it shouldn't be too hard to achieve some basic functionality for 1-1 relationships using the @cypher directive, although there may be some limitations, any in particular that you find? You should be able to try a fairly stable version of 7.0 alpha already if you want to try these

angrykoala avatar Mar 26 '25 15:03 angrykoala

Hi @angrykoala

Great to hear that - looking forward to it :)

I am talking about the workaround @darrellwarde mentioned in https://github.com/neo4j/graphql/issues/5932#issuecomment-2590242956:

I'm afraid that the answer may be more around workarounds. For example, having a Cypher field to read the data, which contains the logic of how to decide which node to return. And then mapping a relationship field which is write-only and using this for the modification of data, just without any guarantees of cardinality. We'll look to do a write up of a workaround such as this because I agree that it's perhaps not intuitive.

I have not found any information on this anywhere until now. As I wrote in my first comment of this ticket:

However, as already discussed in several tickets, e.g. here, the :1 relationship is a BIG and important thing (a Product has ONE Price and is produced by ONE Company). Our API relies heavily on this feature. The release notes on v7 describe how to use the @cypher directive to achieve the removed behaviour. Since filtering on @cypher fields is possible, this seems to be working. But that's only for READ scenarios. How about WRITE? It is not sufficient to just READ nodes connect in a :1 manner. We also have to CREATE, UPDATE, DELETE them. Is this coming as a new feature as proposed by me in Extend the @cypher directive with statements for create/update/delete mutations to be able to mutate properties which are resolved by a @cypher directive #1962? Or how are you planning to workaround WRITE scenarios?

... it is not only about READing 1: relationships with the cypher directive. The important CREATE,UPDATE,DELETE stuff is still missing and I do not see how to solve that despite having read each release note of the alpha versions of v7.

That said: we are not able to upgrade to v7 without having the possibililty to realize CRUD operations for 1: relationships

Can you explain the workaround for me?

andreloeffelmann avatar Mar 27 '25 07:03 andreloeffelmann

Ah, thanks for the clarification @andreloeffelmann

CRUD operations can be a bit more tricky to emulate, but the gist is:

  1. Have a normal, many to many relationships, this one will be used for CRUD operations over this relationship (you can use the @query directive to make it not-readable. The @relationship configuration may also help in making sure that only the operations you want are accesible. From the point of view of the client, these are many to many CRUD operations
  2. Have a second field that is readonly with @cypher, this one matches the relationship, but only returns one item. This is the 1: relationship exposed to read operations

So, in essence, having 2 different fields , one for modifying the relationship, other for reading as a 1:

You may notice that this does not really guarantee cardinality, and that is the main reason why we had to remove 1: relationships for now, as the solution existing in 5 LTS does not really guarantee this either

Does this help? We are aware of the limitations of these workarounds and will work on finding better approaches to this, but hopefully will be enough to move towards v7

angrykoala avatar Mar 27 '25 09:03 angrykoala

Hi @angrykoala

thanks for the update! So you are basically saying that this type definition:

type Product @node {
  productId: String!
  price: Price @relationship (type: "PRODUCT_HAS_PRICE", direction: OUT, queryDirection: DIRECTED) 
}

type Price @node {
  value: Float
  currency: String
}

should be replaced by this:

type Product @node {
  productId: String!
  price: Price @cypher(
            statement: """
            MATCH(this)-[:PRODUCT_HAS_PRICE]->(p:Price)
            RETURN p
            """
            columnName: "p"
        )
  prices: [Price!]! @relationship (type: "PRODUCT_HAS_PRICE", direction: OUT, queryDirection: DIRECTED) @selectable(onRead: false, onAggregate: false)
}

type Price @node {
  value: Float
  currency: String
}

Note that I replaced @query with @selectable since prices is a field, not a type.

Is this the idea?

If so, I hope it will be possible to do this:?

type Product @node {
  productId: String!
  price: Price @cypher(
            statement: """
            MATCH(this)-[:PRODUCT_HAS_PRICE]->(p:Price)
            RETURN p
            """
            columnName: "p"
        )
  price: [Price!]! @relationship (type: "PRODUCT_HAS_PRICE", direction: OUT, queryDirection: DIRECTED) @selectable(onRead: false, onAggregate: false)
}

type Price @node {
  value: Float
  currency: String
}

Note the difference: the price field is called price in both ways, not prices (plural) for the relationship. Since the one with the @cypher directive is for READ scenarios only and the one with the @relationship and @settable directive is for CUD scenarios, the introspection result should be fine.

What about https://github.com/neo4j/graphql/issues/1962 which I have proposed almost 3 years ago? This would allow a much cleaner solution.

andreloeffelmann avatar Mar 27 '25 09:03 andreloeffelmann

Yeah, that would be the rough idea. I don't think the second snippet will work, as the source is not valid GraphQL.

#1962 (and the related #2265) is something that we still have as a long term goal to improve the Cypher directive. It is a non-trivial feature with many edge cases, so it will not be a short term alternative for v7.

angrykoala avatar Mar 27 '25 10:03 angrykoala

Hi @andreloeffelmann

I'll close this issue now, as the first two items have been addressed already and the third one is covered by #2265

Feel free to comment if I missed anything else to be addressed in this issue

angrykoala avatar Jul 21 '25 09:07 angrykoala