New expression #relay(pattern: true) was introduced in change log for relay.js 0.5.
But can't figure out from description nor tests what exactly it does and when I should use it when writing fatQueries.
Some example would be very helpful.
Consider a GraphQL query like the following:
viewer {
friends(first: 10) {
totalCount
edges { node { name } }
pageInfo { hasNextPage }
}
}
When defining a fat query for a Relay mutation, to include the name of a field without specifying any of its subfields tells Relay that any subfield of that field could change as a result of that mutation.
Unfortunately, to omit connection arguments such as find, first, and last on the friends field will cause a validation error on the connection-argument-dependent fields edges and pageInfo:
getFatQuery() {
return Relay.QL`
fragment on AddFriendMutationPayload {
viewer {
friends { edges, pageInfo } # Will throw the validation error below
}
}
`;
}
// Uncaught Error: GraphQL validation/transform error ``You supplied the `pageInfo`
// field on a connection named `friends`, but you did not supply an argument necessary
// to do so. Use either the `find`, `first`, or `last` argument.`` in file
// `/path/to/MyMutation.js`.
You can use the #relay(pattern: true) directive to indicate that want to use the fat query to pattern match against the tracked query, instead of to use it as a fully-fledged query.
getFatQuery() {
return Relay.QL`
fragment on AddFriendMutationPayload #relay(pattern: true) {
viewer {
friends { edges, pageInfo } # Valid!
}
}
`;
}
For more information about mutations see: https://facebook.github.io/relay/docs/guides-mutations.html#content
Related
I have three domain classes as follows :
Case {
...
Reserve reserve
...
}
Reserve {
...
Amount amount
...
}
Amount {
...
Double value
String currency
...
}
I have a createcriteria as follows
List<Case> cases = []
cases = Case.createCriteria().list( ) {
/* I want to access currency of each case here */
}
Is it possible to access value of Currency for each Case inside the createCriteria? I am new to grails and I tried looking for documentation for this but couldn't find any.
like #cfrick commented:
def cases = Case.createCriteria().list {
reserve {
amount {
eq('currency', 'EUR')
}
}
}
You can use sqlRestriction() to write native sql query inside createCriteria.
More info here
I'm creating a voting applications with polls that can be voted. I'm currently stuck on voting for a poll (essentially, creating a vote).
My schema:
(Long story short: polls are accessible from the main store, but the viewer's polls and votes can be accessed directly by him (as fields). votes also are accessible from the min store, but the poll's votes can be accessed also directly by it.
query {
store {
polls { //poll connection
edges {
node { //poll
votes { //vote connection
...
}
}
}
}
votes { //vote connection
edges {
node { //vote
...
}
}
}
}
viewer {
polls { //poll connection
edges {
node { //poll
votes { //vote connection
...
}
}
}
}
votes { //vote connection
edges {
node { //vote
...
}
}
}
}
}
This complex schema makes me confused about how should I define my fat query, since it should define all that could be changing.
Here's what can change:
A vote is created (so voteEdge in the fat query).
A vote is added to the votes connection under store.
A vote is added to the votes connection under viewer.
A vote is added to the votes connection under some poll in the polls connection under store.
(only if the viewer is also the poll's creator, which is possible): A vote is added to the votes connection under some poll in the polls connection under viewer.
So my question is how should I express this in my fat query, should this be suffice?
getFatQuery() {
return Relay.QL`
fragment on CreateVoteMutation {
voteEdge,
viewer{
polls,
voteCount
votes,
},
store{
polls,
voteCount,
votes,
}
}
`;
}
Or should I somehow include votes under polls?
getFatQuery() {
return Relay.QL`
fragment on CreateVoteMutation {
voteEdge,
viewer{
polls{
edges{
node{
voteCount,
votes
}
}
},
voteCount
votes,
},
store{
polls{
edges{
node{
voteCount,
votes
}
}
},
voteCount,
votes,
}
}
`;
}
Thanks!
Looks like you have the right idea. When defining your FatQuery in Relay, you should keep your return fields as optimal as possible, since that's the benefit of GraphQL (you don't need to return more than you will use in your client). So your FatQuery should look like this:
getFatQuery() {
return Relay.QL`
fragment on CreateVoteMutation {
voteEdge,
viewer{
polls { // this is most likely a GraphQL object type, so you'll need a sub-selection, but you don't necessarily need to return all the Votes again since you do that later
name
timestamp
}
voteCount // scalar integer
votes { // again, here you'll need to return a sub-selection since Votes is a GraphQL object type
name
timestamp
}
},
store{
polls { // this is most likely a GraphQL object type, so you'll need a sub-selection, but you don't necessarily need to return all the Votes again since you do that later
name
timestamp
}
voteCount // scalar integer
votes { // again, here you'll need to return a sub-selection since Votes is a GraphQL object type
name
timestamp
}
}
}
`;
}
The idea is that you could technically return votes again as a sub-selection of polls; however, there's no need to since you return is under viewer already. this will give you the total voteCount and all Votes in your system. If you wanted to filter the Votes and VoteCount by Polls, then you could do the opposite, which would look something like this:
getFatQuery() {
return Relay.QL`
fragment on CreateVoteMutation {
voteEdge,
viewer{
polls { // this is most likely a GraphQL object type, so you'll need a sub-selection, but you don't necessarily need to return all the Votes again since you do that later
name
timestamp
voteCount // scalar integer
votes { // again, here you'll need to return a sub-selection since Votes is a GraphQL object type
name
timestamp
}
}
},
store{
polls { // this is most likely a GraphQL object type, so you'll need a sub-selection, but you don't necessarily need to return all the Votes again since you do that later
name
timestamp
voteCount // scalar integer
votes { // again, here you'll need to return a sub-selection since Votes is a GraphQL object type
name
timestamp
}
}
}
}
`;
}
Hope this helps!
I have the following criteria:
Alert.createCriteria().add(Restrictions.isNull('resolvedDate'))
And I need to filter with a "property's property" field: alert.device.room.id
Something like:
Restriction.eq("alert.device.room.id", roomId)
How can I add this restriction to the main criteria?
You can write this in much more of a Grails 'way' using the HibernateCriteriaBuilder's DSL:
Using Nested Closures
Alert.createCriteria().list() {
isNull('resolvedDate')
device {
room {
idEq(roomId)
}
}
}
idEq(roomId) could be replaced with eq('id', roomId) and then used with non-id properties as well.
Using createAlias
Alert.createCriteria().list() {
isNull('resolvedDate')
createAlias('device', 'd')
createAlias('d.room', 'r')
eq('r.id', roomId)
}
When you have defined field as an union of two types (in example machines contains Ships and Droid) then in Relay you can do something like that:
fragment on Faction# relay(plural: true) {
name,
machines {
... on Ship {
name
}
... on Droid {
name,
primaryFunction
}
}
}
so under machines prop your objects are correctly evaluated, but if you want to do that using fragments from external components:
fragment on Faction# relay(plural: true) {
name,
machines {
${StarWarsShip.getFragment('ship')}
${StarWarsDroid.getFragment('droid')}
}
}
then you end up with fragment definitions under machines. It looks like you are trapped and can't check which object is which type in machines array so you can't decide which component should be used.
There exists a __typename field with which you should be able to introspect the type of each record:
Query
fragment on Faction #relay(plural: true) {
name,
machines {
__typename # <-- add this
${StarWarsShip.getFragment('ship')}
${StarWarsDroid.getFragment('droid')}
}
}
App code
this.props.faction.machines.map(machine =>
machine.__typename === 'Droid'
? <Droid droid={machine} />
: <Ship ship={machine} />
);
I'm using the Neo4J Traversal API and trying to traverse from "1" to find nodes "2" and "3" fitting the pattern below:
1-[:A]-2-[:B]-3
However, in my traversal, I'm getting caught out because the following relationship also exists:
1-[:B]-3
As far as I understand, my TraversalDescription needs to specify both relationship types, but I'm unsure of the most elegant way to traverse the :A relationship first, and then branch out to the :B relationship. Unfortunately the relationship Direction can't be used to differentiate in my case.
My Scala code is:
db.traversalDescription()
.evaluator(isAThenBRelationship)
.breadthFirst()
.relationships(A)
.relationships(B)
private val isAThenBRelationship = new PathEvaluator.Adapter() {
override def evaluate(path: Path, state: BranchState[Nothing]): Evaluation = {
if (path.length == 0) {
EXCLUDE_AND_CONTINUE
} else if (path.length == 1) {
Evaluation.of(false, path.relationships().iterator().next().getType.name() == A.toString)
} else {
Evaluation.of(path.relationships().iterator().next().getType.name() == B.toString, false)
}
}
}
As an aside, what's a better way of comparing relationships than this?
path.relationships().iterator().next().getType.name() == MyRelationship.toString
Using relationships() multiple times does not imply an order. Instead there is a internal list which relationships() adds something to.
To limit a certain relationship type to a certain depth, you need to implement and use your own PathExpander. The example below uses Java and implements the PathExpander using an anonymous inner class:
traversalDescription.expand(new PathExpander<Object>() {
#Override
public Iterable<Relationship> expand(Path path, BranchState<Object> state) {
switch (path.length()) {
case 0:
return path.endNode().getRelationships(
DynamicRelationshipType.withName("A") );
case 1:
return path.endNode().getRelationships(
DynamicRelationshipType.withName("B") );
default:
return Iterables.empty();
}
}
#Override
public PathExpander<Object> reverse() {
// not used for unidirectional traversals
throw new UnsupportedOperationException();
}
});
Regarding your second question:
Neo4j contains a very convenient class IteratorUtils. With that your snippet can be written as (assuming MyRelationship is a instance of RelationshipType:
IteratorUtil.first(path.relationships()).getType().equals(MyRelationship)
Additionally to #StefanArmbruster's answer, here's the equivalent Scala code:
db.traversalDescription()
.breadthFirst()
.expand(isAThenBRelationship)
private val isAThenBRelationship =
new PathExpander[Object]() {
override def expand(path: Path, state: BranchState[Object]) =
path.length() match {
case 0 => path.endNode().getRelationships(DynamicRelationshipType.withName("A"))
case 1 => path.endNode().getRelationships(DynamicRelationshipType.withName("B"))
case _ => Iterables.empty()
}
override def reverse(): PathExpander[Object] = ???
}
Note that the expander must come after the relationships.