geldata / geldata/gel-python

Polymorphic splats in QB

Open
#756 0 comments 0 reactions 1 assignee Claimed by @dnwpark View on GitHub
ORM
Dominant language
Python
Stars
415
Forks
50
PR merge metrics
No merged PRs in 30d

Description

Polymorphic types are not friendly to plain `*` splats, as those will only include the common fields. So when dealing with polymorphic types we need to be able to use subtype-specific splats.

Consider this schema:
```
abstract type Foo {
prop: str;
}

type Bar extending Foo {
bar_prop: str;
}

type Baz extending Foo {
baz_prop: str;
}

type Kek {
pek: Foo;
}
```

Let's say we want the equivalent to `select Kek{**}`. In EdgeQL it would be something like this:
```
select Kek {
*,
pek: {
*,
[is Bar].*,
[is Baz].*,
}
}
```

We can't omit the `*` in `pek`, because without it both of the polymorphic splats try to include the common properties and that causes a clash and an error.

So the QB equivalent is:
```
from models import default

q = default.Kek.select(
"*",
pek=lambda k: k.pek.select(
"*",
*default.Bar,
*default.Baz,
)
)
```

This works OK for the above case, but not in general. This form only works when the sub-types don't share any property names and thus don't cause clashes.

If we had a slightly different schema:
```
abstract type Foo {
prop: str;
}

type Bar extending Foo {
extra_prop: str;
}

type Baz extending Foo {
extra_prop: str;
}

type Kek {
pek: Foo;
}
```
We would have an issue with `extra_prop` clashing and so we cannot use a splat here. In EdgeQL, we would have to resolve this manually with a coalesce:
```
select Kek {
*,
pek: {
*,
extra_prop :=
[is Bar].extra_prop ??
[is Baz].extra_prop,
}
}
```

This means that the QB needs to support referring to polymorpshic fields individually as part of expressions or as fields included in the `select`. I propose that we need a method for type intersection `[is Bar]` and we can call it `when_type` as a shorter and more intuitive form of "type intersection".

So this clash can be resolved like this:
```
q = default.Kek.select(
"*",
pek=lambda k: k.pek.select(
"*",
extra_prop=lambda foo: std.coalesce(
foo.when_type(default.Bar).extra_prop,
foo.when_type(default.Baz).extra_prop,
),
)
)
```

For the original schema, the query can be rewritten without splats:
```
q = default.Kek.select(
"*",
pek=lambda k: k.pek.select(
"*",
default.Bar.bar_prop,
default.Baz.baz_prop,
)
)
```
However, if we have `when_type`, then maybe we can also write this instead:
```
q = default.Kek.select(
"*",
pek=lambda k: k.pek.select(
"*",
k.when_type(default.Bar).bar_prop,
k.when_type(default.Baz).baz_prop,
)
)
```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.