Skip to content

Using @reference for 1:n relationships #295

@phillipcurl

Description

@phillipcurl

Hi cruddl team! Thank you for all of your great work on this project. I apologize in advance for the length of this issue, but want to make sure I'm providing as much context as possible.

We currently have a scenario where we're leveraging the @relation directive for a number of 1:n relationships. This has given us a lot of flexibility in our API in terms of being able to fetch for entities in both directions of that relationship. What we're seeing as our collections have grown in size, though, is slow performance when those edges are leveraged, particularly when used in filtering.

Example of current schema:

type House @rootEntity {
  description: String
  people: [Person] @relation(inverseOf: "house")
}

type Person @rootEntity {
  name: String
  house: House @relation
}

We really like that this allows us to do both of the following queries:

query {
  allHouses {
    id
    description
    people {
      id
      name
    }
  }
  allPeople {
    id
    house {
      id
      description
    }
  }
}

Where this had led to slow performance for larger collections, is that querying for allPeople that are associated with a particular House.id generates the following AQL:

WITH houses
RETURN {
  "allPeople": (
    FOR v_person1
    IN people
    FILTER (FIRST((
      FOR v_node1 // this nested traversal is costly 
      IN OUTBOUND v_person1 people_house
      FILTER v_node1 != null
      RETURN v_node1
    ))._key IN ["some_id"])
    RETURN {
      "id": v_person1._key,
      "name": v_person1.`name`
    }
  )
}

The filtering in that query has been inefficient on large collections because of the graph traversal, regardless of how optimized the indices are. We know that we can solve this by leveraging @reference and bypassing the edge traversal when filtering in those scenarios. Updating the schema to:

type HouseWithRef @rootEntity {
  description: String
  uuid: String @key
}

type PersonWithRef @rootEntity {
  name: String
  houseUuid: String
  house: HouseWithRef @reference(keyField: "houseUuid")
}

and running a query like:

query AllPeople($filter: PersonWithRefFilter) {
  allPersonWithRefs(filter: $filter) {
    id
    name
    house {
      id
      description
  }
}

{
  "filter": {
    "houseUuid_in": ["some_id", "another_id"]
  }
}

generates the following AQL, which is much more efficient:

RETURN {
  "allPersonWithRefs": (
    FOR v_personWithRef1
    IN personWithRefs
    FILTER (v_personWithRef1.`houseUuid` IN ["some_id","another_id"])
    LET v_houseWithRef1 = (IS_NULL(v_personWithRef1.`houseUuid`) ? null : FIRST((
      FOR v_house1
      IN houseWithRefs
      FILTER ((v_house1.`uuid` > NULL) && (v_house1.`uuid` == v_personWithRef1.`houseUuid`))
      LIMIT 1
      RETURN v_house1
    )))
    RETURN {
      "id": v_personWithRef1._key,
      "name": v_personWithRef1.`name`,
      "house": (IS_NULL(v_houseWithRef1) ? null : {
        "id": v_houseWithRef1._key,
        "description": v_houseWithRef1.`description`
      })
    }
  )
}

The downside to doing this is that you lose the queryability in both directions that leveraging @relation provides (e.g. querying for people in a house). So finally my question, is there a way in cruddl to leverage @reference and generate the resolvers for resolving entities in the opposite direction of the reference - essentially a @reference-based version of @relation(inverseOf: "")? I know the modeling docs mention "In contrast to relations, it is however not possible to navigate from the referenced object to the referencing object", but I wanted to check with you all.

If there's not a way to do this in cruddl, have there been any discussions on adding support for it? This can be done in pure AQL and would really help improve the performance when using cruddl for things that are a 1:n relationship. From the Arango docs:

ArangoDB does not require you to store your data in graph structures with edges and vertices, you can also decide to embed attributes such as which groups a user is part of, or _ids of documents in another document instead of connecting the documents with edges. It can be a meaningful performance optimization for 1:n relationships, if your data is not focused on relations and you don’t need graph traversal with varying depth.

Finally, is there anything else that's lost when switching a relationship from @relation to @reference aside from being able to query in both directions?

Please let me know if there's any other context I can provide, and thank you again for all of your great work on this project!

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions