FQL equivalent of GraphQL "connect"?

Hi folks, working to grok FQL and have a basic question: What’s the FQL equivalent of “connect” in a GraphQL query? i.e. If I’m adding to the “many” side of a one-to-many relation for an object that already exists, how do I do that in FQL? Here’s the GQL equivalent (from the docs):

mutation {
  createCar(data: {
    plate: "CCC-123"
    owner: {
      connect: "235184188140028419"
    }
  }) {
    _id
    plate
    owner {
      name
    }
  }
}

I can’t figure out the right way to do the same using FQL? Using the above example, how would I add a additional car to an existing user in FQL? Would appreciate if someone could point me in the right direction! Thanks.

The first place to start is understanding how Fauna stores different kinds of relationships for GraphQL. The docs go into detail about this here. We can use that as precedence for managing relationships directly.

Data representation

Here, we are concerned about one-to-many relationships. In that case, Fauna’s GraphQL API stores the references that make up the relationships in the “many” side.

So, given a GraphQL schema like this:

type Owner {
  name: String!
  cars: [Car]! @relation
}

type Car {
  plate: String!
  owner: Owner @relation

the data will be stored like this:

// Owner
{
  "ref": Ref(Collection("Owner"), "309362529386102849"),
  "ts": 1631290049590000,
  "data": {
    "name": "Paul"
  }
}
// Car
{
  "ref": Ref(Collection("Car"), "320076030282825794"),
  "ts": 1641507139410000,
  "data": {
    plate: "CCC-123",
    owner: Ref(Collection("Owner"), "309362529386102849")
  }
}

Since the relationship is stored in the many-side, set another Cars owner to the same value.

// Car
{
  "ref": Ref(Collection("Car"), "32007603027489236458"),
  "ts": 1641507139410000,
  "data": {
    plate: "DDD-555",
    owner: Ref(Collection("Owner"), "309362529386102849")
  }
}

“Connect” nested queries

To connect a Car to an Owner, set Car.owner to the Owner Ref. To remove the connection, set Car.owner to null.

// connect car and owner
Update(Ref(Collection("Car"), "320076030282825794"), {
  data: {
    owner: Ref(Collection("Owner"), "309362529386102849")
  }
})
// disconnect car and owner
Update(Ref(Collection("Car"), "320076030282825794"), {
  data: {
    owner: null
  }
})

To create a Car with an Owner within the same transaction, make sure to set the Car.owner field.

// create car with owner
Create(Collection("Car"), {
  data: {
    plate: "CCC-123",
    owner: Ref(Collection("Owner"), "309362529386102849")
  }
})

“Create” nested queries

A “create” nested query could be done like this in GraphQL

mutation {
  createCar(data: {
    plate: "CCC-123"
    owner: {
      create: {
        name: "Paul"
      }
    }
  }) {
    _id
    plate
    owner {
      name
    }
  }
}

This can be done directly in FQL. Here we will use Create to create a new Owner, which returns a Document. We then use Select to pick the Ref from the Document.

// create car with new owner
Create(Collection("Car"), {
  data: {
    plate: "CCC-123",
    owner: Select("ref",
      Create(Collection("Owner"), {
        data: {
          name: "Paul"
        }
      })
    )
  }
})

Validation

In practice, you probably don’t want to just blindly store any Ref without checking if it exists. You would probably use the Exists function to look for the Refs first.

// create car with owner
Create(Collection("Car"), {
  data: {
    plate: "CCC-123",
    owner: Let(
      {
        ref: Ref(Collection("Owner"), "309362529386102849")
      },
      If(
        Exists(Var("ref")),
        Var("ref"),
        // connect to nothing if the Owner Ref does not exist
        null
        
        // alternatively, you can abort with some custom error message
        // Abort("Owner does not exist")
      )
    )
  }
})

Query with Indexes

Since the relationship is stored within the Cars, in order to get a list of the Cars owned by a single Owner, we have to use an Index (Note this is how Fauna’s GraphQL API does it, too).

CreateIndex({
  name: "car_owner_by_owner",
  source: Collection("Car"),
  terms: [{ field: ["data", "owner"] }]
})
Map(
  Paginate(
    Match(Index("car_owner_by_owner"), Ref(Collection("Owner"), "309362529386102849"))
  ),
  Lambda("ref", Get(Var("ref")))
)

// returns
{
  data: [
    {
      "ref": Ref(Collection("Car"), "320076030282825794"),
      "ts": 1641507139410000,
      "data": {
        plate: "CCC-123",
        owner: Ref(Collection("Owner"), "309362529386102849")
      }
    },
    {
      "ref": Ref(Collection("Car"), "32007603027489236458"),
      "ts": 1641507139410000,
      "data": {
        plate: "DDD-555",
        owner: Ref(Collection("Owner"), "309362529386102849")
      }
    }
  ]
}
1 Like

This topic was automatically closed 21 days after the last reply. New replies are no longer allowed.