One-to-many links
Closed this issue · 7 comments
I'm currently putting together an example but I'm unsure how graphql-weaver works.
const schema = await weaveSchemas({
endpoints: [
{
namespace: 'properties',
typePrefix: 'Property',
url: 'https://v7l45qkw3.lp.gql.zone/graphql',
fieldMetadata: {
'PropertyProperty.booking': {
link: {
field: 'bookings.bookingsByPropertyId', // field Song in namespace library
argument: 'propertyId', // argument of library.Song
batchMode: true,
keyField: 'propertyId'
}
},
'Query.properties.properties': {
join: {
linkField: 'bookings'
}
}
}
},
{
namespace: 'bookings',
typePrefix: 'Booking',
url: 'https://41p4j4309.lp.gql.zone/graphql'
},
{
namespace: 'weather',
typePrefix: 'Weather',
url: 'https://5rrx10z19.lp.gql.zone/graphql'
}
]
});
would love some help fleshing this out.
Are you trying to do a one-to-many link between a Property and its Bookings? Looking up the related bookings by PropertyId?
I'm working on something similar, and not having much success. It looks like the linking between schemas only supports a one-to-one mapping. That is, its expecting your Property to have a single BookingId that it can use to get booking details from the Booking schema... one-to-one.
Hi @lifeiscontent,
graphql-weaver currently only supports forward links. This means that the join id must be available in the place where you want to create the link. In you example, this applies to the field Booking.propertyId
. With the following config, you can link from bookings to properties:
const schema = await weaveSchemas({
endpoints: [
{
namespace: 'properties',
typePrefix: 'Property',
url: 'https://v7l45qkw3.lp.gql.zone/graphql'
},
{
namespace: 'bookings',
typePrefix: 'Booking',
url: 'https://41p4j4309.lp.gql.zone/graphql',
fieldMetadata: {
'Booking.propertyId': { // type name and field name in the *original* schema
link: {
field: 'properties.propertyById', // field path in the *weaved* schema
argument: 'id', // argument of properties.propertyById
batchMode: false, // propertyById returns one property and not many
linkFieldName: 'property' // name of the field in Booking where the object should be made available
}
}
}
},
{
namespace: 'weather',
typePrefix: 'Weather',
url: 'https://5rrx10z19.lp.gql.zone/graphql'
}
]
});
This allows you to do a query like this:
query {
bookings {
bookings {
propertyId
id
startTime
property { # the new field
name
location {
name
}
}
}
}
}
This is quite inefficient, because propertyById
needs to be queried once per referenced property. If you add a version that accepts a list of ids and returns a list of properties, this can be sped up significantly.
If you add a field with a relay-style filter
argument that allows arbitrary filtering, you then can filter bookings by their properties. This also works for orderBy
and first
. Configure it like this:
const schema = await weaveSchemas({
endpoints: [
{
namespace: 'properties',
typePrefix: 'Property',
url: 'https://v7l45qkw3.lp.gql.zone/graphql'
},
{
namespace: 'bookings',
typePrefix: 'Booking',
url: 'https://41p4j4309.lp.gql.zone/graphql',
fieldMetadata: {
'Booking.propertyId': { // type name and field name in the *original* schema
link: {
field: 'properties.allProperties', // field path in the *weaved* schema
argument: 'filter.id_in', // argument of allProperties which accepts a list of ids to be matched
batchMode: true, // allProperties returns multiple properties
keyField: 'propertyId', // now needed to match properties back to their ids
linkFieldName: 'property' // name of the field in Booking where the object should be made available
}
},
'Query.bookings': {
join: {
linkField: 'property'
}
}
}
},
{
namespace: 'weather',
typePrefix: 'Weather',
url: 'https://5rrx10z19.lp.gql.zone/graphql'
}
]
});
Then, to only find bookings in California, ordered by city name, you could use this (assuming the property schema already offers this filter functionality), use a query like this:
query {
bookings {
bookings(filter:{ property_address_state: "California"}, orderBy: property_address_city_ASC) {
propertyId
id
startTime
property {
name
location {
name
}
}
}
}
}
@adamkl: graphql-weaver supports one-to-one and one-to-many relationships, the latter via a field that contains a list of foreign keys. It does not yet support backwards links (how you usually would do a merge in SQL-like fashion). But if the properties schema in this example here would have a list of booking ids, graphql-weaver could link it to the respective bookings.
@Yogu Yes, I was able to get the one-to-many link to work the way you explained, with a list of foreign keys. That can put some constraints on the back-end data model, and, as you said, it is at odds with how you would do the same in SQL.
I'm thinking that backward links would be a good feature to add (especially since both @lifeiscontent and myself both ran into issues trying to do just that).
I'd definitely like to help you guys with this, I'm just going through some approvals on my end to get permission to contribute on work time.
@adamkl That's great! Let's discuss how to implement this feature once you got approval to contribute.
@Yogu So I've been working a bit on this on my own (still waiting for approvals) and I've actually got it working in a very preliminary form. Just needed to change a couple of lines of code.
Still needs work testing out difference scenarios (I can already think of a limitation that should be resolved), documentation, and I'd like you guys to take a look obviously, so should I push my branch to your repo for now?
hI @adamkl, that's really great news! You can fork this repo, push to your repo and then create a pull request. If you're unsure whether your employer allows the contribution, please write that again in the pull request.
Just a quick update on this. I looks like approval should come sometime next week, so I've already created a fork and I'll be getting my changes ready. Look for a PR sometime next week I hope.