Michel,
Thank you very much for your explanation. I have a follow up question about how I would query such a data structure in DQL with a filter in a recurse query.
Explanation: We want to perform a recursive search to display a network graph. We currently perform a recursive query to do this, but we want to filter out certain paths. In this example we have three companies associated with projects, and projects associated with contracts. There are (for this example) hundreds of millions of projects but a small list of contracts, so based on your feedback above, the contracts are now pulled out as separate nodes and referred to indirectly by the projects, so the same contract string is not stored a hundreds of times, but rather is a pointer to one string for that contract. When we do this, the filtering we are used to performing does not work. We are providing a simplified example of our problem with a schema, upsert, and query to illustrate.
#--- Schema
# Types
type <Company> {
company.name
company.hasProject
}
type <Project> {
project.name
project.hasContract
}
type <Contract> {
contract.name
}
# Predicates
<company.name>: string @index(hash) .
<contract.name>: string @index(hash) .
<project.name>: string @index(hash) .
<company.hasProject>: [uid] @reverse .
<project.hasContract>: uid .
Here’s our upsert:
#--- Upsert
upsert {
query {
getCompanyAlpha(func:eq(company.name, "Alpha")) {
compA as uid
}
getCompanyBravo(func:eq(company.name, "Bravo")) {
compB as uid
}
getCompanyBravo(func:eq(company.name, "Charlie")) {
compC as uid
}
getProj1(func:eq(project.name, "Proj1")) {
proj1 as uid
}
getProj2(func:eq(project.name, "Proj2")) {
proj2 as uid
}
getProj3(func:eq(project.name, "Proj3")) {
proj3 as uid
}
getContract1(func:eq(contract.name, "TheContract1")) {
contract1 as uid
}
getContract2(func:eq(contract.name, "TheContract2")) {
contract2 as uid
}
}
mutation {
set {
uid(compA) <company.name> "Alpha" .
uid(compB) <company.name> "Bravo" .
uid(compC) <company.name> "Charlie" .
uid(proj1) <project.name> "Proj1" .
uid(proj2) <project.name> "Proj2" .
uid(proj3) <project.name> "Proj3" .
uid(contract1) <contract.name> "TheContract1" .
uid(contract2) <contract.name> "TheContract2" .
uid(compA) <company.hasProject> uid(proj1) .
uid(compA) <company.hasProject> uid(proj2) .
uid(compA) <company.hasProject> uid(proj3) .
uid(compB) <company.hasProject> uid(proj1) .
uid(compC) <company.hasProject> uid(proj3) .
uid(proj1) <project.hasContract> uid(contract1) .
uid(proj2) <project.hasContract> uid(contract1) .
uid(proj3) <project.hasContract> uid(contract2) .
}
}
}
And here is the query that we are used to, but it doesn’t work here because of the extra level of indirection. If our project.hasContract would be a predicate between a project node and a contract string literal value, we’d know how to query it with the filter below. But now we have an extra level where we have a contract node with a string literal value, and I’m not sure how to filter on that.
So, in other words, I realize that the query below won’t work because hasProject doesn’t point to a node with a contract.name predicate and a string literal value… but I don’t understand how I can build a filter that can accomplish this now that we’re one predicate removed from the actual string.
#--- Query
{
find(func: eq(company.name, "Alpha")) @recurse {
company.name
company.hasProject @filter(eq(contract.name, "TheContract2"))
~company.hasProject
project.name
project.hasContract
contract.name
}
}
Thank you very much for all of your help!