Specifying dynamic number of roles in n-ary relation - vaticle-typedb

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.

Related

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;

How to retrieve only the relation ID, not the whole entity in MikroORM?

I have the following basic test entities:
#Entity()
class Author {
#PrimaryKey()
public id!: number;
#Property()
public name!: string;
}
#Entity()
class Book {
#PrimaryKey()
public id!: number;
#Property()
public title!: string;
#ManyToOne({joinColumn: 'authorID'})
public author!: Author;
}
What i'm trying, is to select only a single Book record, with its 'author', but I care only about its ID, I don't want to actually load the entity.
If I simply call this, it won't work (no author data loaded at all):
em.getRepository(Book).findOne({id: 1}, {fields: ['id', 'title', 'author.id']});
'author.id' doesn't do the trick, the SQL doesn't even contain the 'authorID' field.
If I add 'author' to the fields list as well, it works, author is loaded (only with the ID), but as a separate entity, with a separate, additional SQL statement! That's what I'm trying to avoid.
em.getRepository(Book).findOne({id: 1}, {fields: ['id', 'title', 'author', 'author.id']})
#1. SQL
select `b0`.`id`, `b0`.`title`, `b0`.`authorID` from `book` as `b0` where `b0`.`id` = 1 limit 1
#2. SQL (this wouldn't be neccessary as I want only the ID)
select `a0`.`id` from `author` as `a0` where `a0`.`id` in (2)
--> Result:
Book: { id: 1, title: 'a book', author: { id: 2 } }
The only way I found is to add the specific 'authorID' field too to the Book entity:
#Property()
public authorID!: number;
But, I'd like to avoid introducing these foreign key columns, it would be better to handle through the already existing and used 'author' relation (only by the 'id' property).
Does any solution exists where I could retrieve a relation's ID without generating a 2nd SELECT statement (for the relation), and even avoid introducing the foreign key (next to the already existing relation property)? Would be great to receive through the relation without any extra sql statement.
Thanks in advance.
It is correct behaviour you see the second query, that is how population works, and the fact that you want just a single property from the entity does not change anything, you still populate the relation, and each relation will use its own query to load it. You can use LoadStrategy.JOINED if you want to use a single query. But that would still do a join for that relation, which is not needed for your use case.
Given you only want the FK to be present, you dont need to care about the target entity at all. This should do the trick too:
em.getRepository(Book).findOne(1, {
fields: ['id', 'title', 'author'],
populate: [],
});
This way you say you want those 3 properties to be part of what's selected from the Book entity. You already have the author property, which represents the FK. You will end up with what you want once you serialize such entity. During runtime, you will see entity reference there - an entity with just the PK. It is represented as Ref<Author> when you console.log such entity.
Note that you need that populate: [] there, as otherwise it would be inferred from your fields which contains author property, and that would trigger the full load of 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;

Database design to store values and arguments of functions

I'm facing the following problem, where I need to design a filter engine with nested conditional logic.
I'm representing the logic as a tree where each branch first value is "OR" or "AND"; the second value can either be
a name of a function
another branch with further conditional structure
For example:
$tree = [
'nodetype' => 'ruleset',
'conditional' => 'OR',
'children' => [
[
'nodetype' => 'method',
'methodName' => 'startsWith'
'arguments' => [
'startsWithThis' => 'john',
'subject' => 'john doe'
]
],
[
'nodetype' => 'ruleset'
'conditional' => 'AND',
'children' => [
...more nesting
]
]
]
];
This tree is then recursively evaluated using Symfony's Expression language component (I've registered custom expressions for methods like startsWith etc).
The issue is that methods will differ from one another in their number of arguments they accept and the order of those arguments. I'm not sure how to store this in a relational database, without serialising the whole tree to a json string; which I'd like to avoid.
What I came up with so far is the following database structure:
filters:
id
name
filter_arguments:
id
filter_id
name
filter_usecases:
id
filter_id
filter_usecase_values
id
filter_usecase_id
filter_argument_id
value
However this table design does not address the issue of storing the "OR" / "AND" nature of a branch; and it also cannot represent nested filters (e.g. parent-child relation of branches).
How do I go about this? Is there a specific term that describes what I'm trying to achieve here? I'd gladly read more about this but I don't even know what to google.
To take a quick stab at it, going just from the data:
node
id
nodetype
conditional
method_name
children
id
parent_node_id
child_node_id
arguments
id
node_id
key
value
Note that the relationships (children) and argument data are not in the node table, but rather are specified by cross reference tables you will have to join with when you retrieve nodes. I would expect that it is the "children" table which will become the central actor in your recursing the tree, while "node" and "arguments" will be the joined tables.
Please let us know the solution you end up using successfully.

Resources