2

Say I have this schema:

Client {
 services: [Service] @relation(name: "ClientServices")
}
Service {
 clients: [Client] @relation(name: "ClientServices")
}

And I want to connect some services to a client, like this:

mutation {
 updateClient(id: ..., data : {
   services:  {
     connect: ["123", "456"] ## where there are valid service _id's
  }
 }) { ... }
}

Fantastic. Works great. But now I want to replace all the connections (services) with other ones, but still keep the 123 service.

mutation {
 updateClient(id: ..., data : {
   services:  {
     connect: ["123", "789"] ## notice kept 123, and now want 789
  }
 }) { ... }
}

This will result in a instance not unique error from Fauna. Because its trying to connect 123 but its already in the ClientServices collection (the auto-generated collection that manages m-m relationships.)

Is there an easy way to disconnect all and reconnect the ones I want in one call without a UDF?

O. Mills
  • 216
  • 2
  • 10

2 Answers2

1

At this point, Fauna has create, disconnect and connect. You seem to be looking for a replace that automatically calculates a diff and takes the correct actions in terms of disconnect and connect. I don't think that's currently possible with pure GraphQL. You would indeed need to write a UDF. It might be an interesting feature for others as well so it could be a good candidate for a feature request on the forums: https://forums.fauna.com/

Brecht De Rooms
  • 1,802
  • 7
  • 15
  • Can't you just compute the diff in JS and send both the `disconnect` and `connect` fields in your GQL query? – colllin Nov 12 '20 at 22:20
  • Yes, that's possible, that was probably not entirely clear from my response. I assumed though that the user was looking for something where he doesn't have to compute it in the client. – Brecht De Rooms Nov 20 '20 at 12:53
1

I believe you can connect and disconnect in the same mutation (without a UDF):

mutation {
 updateClient(id: ..., data : {
   services:  {
     connect: ["789"],
     disconnect: ["456"]
  }
 }) { ... }
}

This assumes you have the ability to compute the diff on the client. For example, using lodash:

const _filter = require('lodash/filter');
const _find = require('lodash/find');
const _map = require('lodash/map');

let orig = [{id: "123"}, {id: "456"}]
let replacements = [{id: "123"}, {id: "789"}]

let toConnect = _filter(replacements, o => !_find(orig, {id: o.id}))
let toDisconnect = _filter(orig, o => !_find(replacements, {id: o.id}))

mutate(
    // ...
    {
        connect: _map(toConnect, 'id'),
        disconnect: _map(toDisconnect, 'id')
    },
    // ...
)

As an easy solution for providing replace functionality, maybe the Fauna GQL API could provide some guarantees around order of operations. If the disconnect is always performed before the connect, then you could always disconnect orig and connect replacements. This guarantee seems reasonable, since I can't really imagine a sane reason for connecting and immediately disconnecting a relation in the same API call.

colllin
  • 9,442
  • 9
  • 49
  • 65
  • 1
    Thanks @colllin - I spent too long writing a UDF to replace.. and didn't think of combining the two (connect and disconnect). It works great! – O. Mills Nov 25 '20 at 00:33