How to define a transitive relation in graql - vaticle-typedb

my model consists of organizations that have projects and the projects have buckets. The buckets can be shared to other projects in the same organization. I defined a rule for project sharing which means that two projects are in this relation when first is sharing a bucket with the other.
define
organization sub entity,
plays organizationRole,
key identifier;
project sub entity,
plays projectRole,
plays projectSourceRole,
plays projectTargetRole,
plays transitiveProjectSourceRole,
plays transitiveProjectTargetRole,
key identifier;
bucket sub entity,
plays bucketRole,
plays sharedBucketSourceRole,
plays sharedBucketTargetRole,
key identifier;
organizationToProject sub relation,
relates organizationRole,
relates projectRole;
projectToBucket sub relation,
relates projectRole,
relates bucketRole;
sharedBucket sub relation,
relates sharedBucketSourceRole,
relates sharedBucketTargetRole;
projectSharing sub relation,
relates projectSourceRole,
relates projectTargetRole;
project-sharing sub rule,
when {
(projectRole: $ps, bucketRole: $bs) isa projectToBucket;
(projectRole: $pt, bucketRole: $bt) isa projectToBucket;
(sharedBucketSourceRole: $bs, sharedBucketTargetRole: $bt) isa sharedBucket;
$ps != $pt;
}, then {
(projectSourceRole: $ps, projectTargetRole: $pt) isa projectSharing;
};
It works fine. (see )
But I want to define a rule for transitive relation (according to some ancestry example I found in the docs) so I added to the schema:
transitiveProjectSharing sub relation,
relates transitiveProjectSourceRole,
relates transitiveProjectTargetRole;
transitive-project-sharing sub rule,
when {
(projectSourceRole: $a, projectTargetRole: $b) isa projectSharing;
(projectSourceRole: $b, projectTargetRole: $c) isa projectSharing;
}, then {
(transitiveProjectSourceRole: $a, transitiveProjectTargetRole: $c) isa transitiveProjectSharing;
};
It finds nothing (but there are several transitive ones, see )
Would anyone have a suggestion where is the mistake?

I've got a hint from Grakn.ai Slack. The transitivity should be defined on the projectSharing role itself, additional transitiveProjectSharing is not necessary. So the definition should be:
transitive-project-sharing sub rule,
when {
(projectSourceRole: $a, projectTargetRole: $b) isa projectSharing;
(projectSourceRole: $b, projectTargetRole: $c) isa projectSharing;
}, then {
(projectSourceRole: $a, projectTargetRole: $c) isa projectSharing;
};
and the query works well:

As a slight improvement over your awesomely elegant answer (thanks!!), you can also use subtyping to differentiate explicit relationships from inferred relationships:
indirectProjectSharing sub projectSharing;
transitive-project-sharing sub rule,
when {
(projectSourceRole: $a, projectTargetRole: $b) isa projectSharing;
(projectSourceRole: $b, projectTargetRole: $c) isa projectSharing;
}, then {
(projectSourceRole: $a, projectTargetRole: $c) isa indirectProjectSharing;
};
Since indirectProjectSharing is a subtype of projectSharing, the rule's condition will match both relations, and you'll be able to refer to either type in your own queries depending on the use case.

Related

How to query rules in a TypeDB database?

In my schema I think I defined this rule:
define rule transitive-hierarchy-rule:
when {
(before: $a, after: $b) isa hierarchy;
(before: $b, after: $c) isa hierarchy;
} then {
(before: $a, after: $c) isa hierarchy;
};
Notice from the documentation:
"facts defined via rules are not stored in the knowledge graph. [...]
However, by defining the rule in the schema, at query time the extra
fact will be generated."
Also notice from the documentation:
Currently, for a match query to trigger reasoning and obtain
inferences from rules, you must use a read transaction. We are working
towards enabling reasoning in write transactions in subsequent
releases.
However in my query match (before: $x, after: $y) isa hierarchy inside a read transaction doesn't return the expected results:
{ $x iid 0x826e800b8000000000000000 isa task; $y iid 0x826e80048000000000000002 isa task; }
{ $x iid 0x826e800a8000000000000000 isa task; $y iid 0x826e800b8000000000000000 isa task; }
The above two results I had explicity defined. I was expecting the third result to be inferred via the rule.
So I am trying to debug the situation. Could it be possible that I did not define the rule properly?
So question: how do I query more about the rules that have been defined in the database schema?
Have you been sure to turn on inference? The 'infer' option must be turned on for rules to be triggered whilst running queries.
See the infer option for the java client API: https://docs.vaticle.com/docs/client-api/java#options

How to query role types in a TypeDB relation?

Suppose I have the following in the schema:
define
person sub entity, plays employment:employee;
company sub entity, plays employment:employer;
employment sub relation, relates employee, relates employer;
Then it imposes the restriction that employer must be of type company and employee must be of type person.
How do I find about this restriction through a query?
So far the best that I know is:
match $x sub relation; get $x;
It does show that employment is a relation. But it doesn't show the role types that are allowed/ permitted. How to query that?
match $x sub relation;
$x relates $y;
get $x, $y;
You can use the same constructs for querying the schema (or data) as you did to write it. So you can also find which types play the roles through the query:
match $x sub relation;
$x relates $y;
$z plays $y;
get $x, $y, $z;

Specifying dynamic number of roles in n-ary relation

I am aware that Grakn allows us to specify n-ary edges/relations.
But, can they allow us to specify many-to-1 relationships when we don't know the size of "many" beforehand?
For example:
If I want to connect two entities 'a' and 'b' to entity 'c', I can do that.
But, what if I don't know before hand how many entities I would want to connect to 'c'.
What if I wanted to keep that dynamic? (sometimes connect 2 entities to 1 or 4 entities to 1)
Does Grakn allow me to represent that?
I hope I have been able to describe my question clearly.
Please let me know. Thank you.
In Grakn, these many-to-one connections (where the "many" is dynamic) can be stored as the relation instances themselves.
For example, suppose you have a family with a parent who has some children, but we don't know how many there are, and new children can be added at any time.
Then you would
define
name sub attribute, datatype string;
person sub entity, has name, plays parent, plays child;
parenthood sub relation, relates parent, relates child;
Now suppose you have matched four people, $a, $b, $c and $d, then you can make a 2-to-1 relationship by inserting two parenthood instances:
insert (parent: $a, child: $b) isa parenthood;
insert (parent: $a, child: $c) isa parenthood;
and it becomes a 3-to-1 relationship when you insert a third parenthood:
insert (parent: $a, child: $d) isa parenthood;
and so on.
I think you don't necessarily want to do exactly as you described, adding a new role would require dynamically changing the schema. Possible but more likely you want to dynamically choose the number of role players. It's worth clarifying that a role can be played many times within the same relation instance.
Let's re-use Alex Walker's parenthood example. He creates a new relation instance for each parenthood relation. This is a perfectly valid and encouraged approach.
The alternative approach is to have an unknown number of parents and children in the same relation. The schema remains exactly the same, but you are able to add data in this way:
match [ do matching for $a, $b, $c, $d ]
insert (parent: $a, child: $b, child: $c, child: $d) isa parenthood;
The two approaches have different semantic meanings. In this second case there is a single relation connecting the parent to their three children. Modelling with a single relation says that the combination of role players are necessary for the relation.
In the parenthood case, the 3 children are not prerequisite, but two parents are prerequisite to having a child (most of the time):
match [ do matching for $p1, $p2, $c1 ]
insert (parent: $p1, parent: $p2, child: $c1) isa parenthood;
If those two parents have another child, you have two options. Either create a new relation between the two parents and the new child (approach 1), or add the new child to the existing relation (approach 2). The latter you can do like this:
match
[ do matching for $p1, $p2, $c1, $c2 ]
$ph(parent: $p1, parent: $p2, child: $c1) isa parenthood;
insert
$ph(child: $c2) isa parenthood;
The one relation will then look like
$ph(parent: $p1, parent: $p2, child: $c1, child: $c2) isa parenthood;
Notice the use of a variable $ph to match for an existing relation and add a role player to it.

Query inner elements based on role in grakn

I have a keyspace name 'server' in grakn with one entity named 'node' having an attribute "name". This keyspace is having only one relation named 'node_relation' that connects entities. The entity node acts the role as 'base' and 'dependent' in the node_relation.
This graph is aligned in such a way that all the elements in the left side are acting as base and right side act ad dependent in the node_relation.
I need to query nodes that act as base node for an entity and if the queried node has any node that is acting as a base node, I need to get it also until the queried node is not having any nodes with role base.
For example, if I want all nodes with role base for node named "F", I should get the answer as of "A,B,C,D,E,DD,EE,FF,XX,YY,ZZ".
Currently i am using the query
match $x isa node, has name "F";$y isa node;$rel(base:$x,$y);get $y;**
This query return E and DD as result and i will execute this query until a node is not having any node with role base.
Is their any some way to get all the node in a single query ?
Disclaimer: I'm pretty new to Grakn still.
The kind of relation you are looking for is transitive (if a->b and b->c then a->c). You can use Grakn rules to model this relation then use it in your query; Grakn infers the transitive relation when you execute the query.
Here's how I wrote the rules, include this in your schema.gql:
node_superrelation sub relation,
relates base,
relates dependent;
node-relation-is-superrelation-rule sub rule,
when {
(base: $a, dependent: $b) isa node_relation;
},
then {
(base: $a, dependent: $b) isa node_superrelation;
};
node-superrelation-is-transitive-rule sub rule,
when {
(base: $a, dependent: $b) isa node_superrelation;
(base: $b, dependent: $c) isa node_superrelation;
},
then {
(base: $a, dependent: $c) isa node_superrelation;
};
Now you can use the following query to get all bases of node F. Note that we specifically request those related by a node_superrelation.
match $d isa node, has name "F";$b isa node; $rel(base:$b,dependent:$d) isa node_superrelation;get $b;
You could make node_relation transitive and achieve this in one rule, but then queries using node_relation of F would always include all of the results without using a limit, which is probably not what you want. You could also define new roles for node_superrelation, which may also further simplify things, or use the shared roles to some other advantage. Hopefully, you can see that Grakn's rules are really very powerful and should let you describe these kinds of relations in a way that makes sense for your model!
Hope this helps!
EDIT: Just to add quickly that the convention in Grakn is to use hyphens rather than underscores.

Is there a way to 'get' all connected entities and their attributes connected to a specific instance of an entity?

I want to display all the data points for a specific instance of an entity.
I understand how to write the query in a specific form but I want it to be more general and be more concise.
This is what I currently have:
match
$t isa technology, has version "v9.5";
$as isa app-server, has database-server $ds, has dot-net-network $dnn, has XXXXXX $x1, has XXXXXX $x2;
$ds isa database-server, has XXXXXX $x3, has XXXXXX $x4;
$r1(container: $t, containee: $as);
$r2(container: $t, containee: $ds);
get; offset 0; limit 30;
There are several more entities connecting to my container.
In general in Graql we can make ambiguous queries by providing less constraints in the query, or changing a constraint to a more relaxed one.
In your case, I believe you want to ask a question specifically about an entity instance, described by this pattern: $t isa technology, has version "v9.5";. You want to find the entities it is connected to via a relation. You then wish to find all of the attributes of those connected entities, but without specifying all of the types of attribute those entities could own according to the schema.
The most generic way to get the connected concepts is:
match
$t isa technology, has version "v9.5";
$r($t, $e);
get $e;
If you want connected entities only:
match
$t isa technology, has version "v9.5";
$r($t, $e);
$e isa entity;
get $e;
This is because all of your user-defined entities inherit from entity. You can do the same for relation and attribute, or thing which is the super type of all three.
The full answer to get all of the attributes of a concept, is to use the same principal, giving the base type attribute:
match
$t isa technology, has version "v9.5";
$r($t, $e);
$e isa entity, has attribute $a;
get $e, $a;
Bonus
You can then find attributes common to two patterns, in this case two technology instances:
match
$t1 isa technology, has version "v9.5";
$e1 isa entity, has attribute $a;
$r1($t1, $e1);
$t2 isa technology, has version "v9.6";
$e2 isa entity, has attribute $e;
$r2($t2, $x2);
get $a;

Resources