-
Notifications
You must be signed in to change notification settings - Fork 41
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Introduce support for bi-temporal versioning in the Graph Service #928
Conversation
// @ts-expect-error -- @todo, how do we convince TS that we're only setting this when the generic is satisfied | ||
const subgraph: Subgraph<Temporal, EntityRootType<Temporal>> = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Didn't want to sink much time into this but completely ignoring tsc
here is also not great.. I haven't found a good way to inform TSC that the generic has been narrowed at certain points within function bodies. This is a recurring inconvenience and in the following PR there will be many places where it's something like if (subgraph.temporalAxes !== undefined)
... and it would be really great if there was a way to then tell TS "hey Temporal
is actually true
here"
/* | ||
* @todo - we _should_ be able to use `Extract<GraphElementForIdentifier<TemporalSupport, VertexId<any, any>>` | ||
* here to actually get a strong type (like `EntityId` or `BaseUri`). TypeScript seems to break on using it with a | ||
* generic though. So for now we write `string` because all of the baseId's of `VertexId` are string aliases anyway. | ||
*/ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is actually quite frustrating and if we end up making the BaseIds template strings or branded types this will catch us out.
* - isSubgraph<DataTypeRootType> | ||
* - isSubgraph<PropertyTypeRootType> | ||
* - isSubgraph<EntityTypeRootType> | ||
* - isSubgraph<EntityRootType> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, slipped through on the previous one
export * from "./edges/outward-edge-alias.js"; | ||
export * from "./edges/variants.js"; | ||
|
||
/** @todo - Re-express these and `Vertices` as `Record`s? */ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm unsure why I ended up expressing them like this originally, it seems that they'd be better as Record
s, and perhaps they'd behave slightly nicer sometimes with narrowing.
I don't want to change it here though because I want to make sure it doesn't break any code in usage in MBD, so will probably look into this as part of the follow-up PR.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Phew! Thanks @Alfred-Mountfield, fantastic work.
My comments are mostly about naming / explaining stuff.
I started to optimise things but I think you're aware that we can reduce a lot of looping so I stopped.
I haven't looked at the TS errors you flag as I thought I'd get this in and then we can revisit the most egregious ones – let me know which you think might be worth further investigation.
* @todo This assumes that the left and right entity ID of a link entity is static for its entire lifetime, that is | ||
* not necessarily going to continue being the case |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Presumably the relevant task to link would be whatever change we might introduce which affects this – what is it?
export type EntityValidInterval = { | ||
entityId: EntityId; | ||
validInterval: TimeInterval<LimitedTemporalBound, TemporalBound>; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we add a comment to this type explaining this please?
I think it would also help if the name captured that this relates to the period an edge was valid for rather than an entity.
e.g.
export type EdgeApplicableFor = {
entityId: EntityId;
interval: TimeInterval<LimitedTemporalBound, TemporalBound>;
};
I also wondered if introducing the word 'valid' risked confusion with 'valid time'. We don't use it outside this context.
libs/@blockprotocol/graph/src/stdlib/subgraph/element/map-revisions.ts
Outdated
Show resolved
Hide resolved
libs/@blockprotocol/graph/src/stdlib/subgraph/element/entity.ts
Outdated
Show resolved
Hide resolved
/* | ||
these casts are safe as we check for `targetRevisionInformation === undefined` above and that's only ever | ||
defined if `Temporal extends true` | ||
*/ | ||
(entity as Entity<true>).metadata.temporalVersioning[ | ||
(subgraph as Subgraph<true>).temporalAxes.resolved.variable.axis | ||
], | ||
targetTime, | ||
); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Technically there's nothing to stop JS users passing a non-temporal subgraph and targetRevisionInformation
but I know that worrying about that is a whole can of worms 😁
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've been slightly concerned about this too.. I hope they'll at least read the documentation? 😅
libs/@blockprotocol/graph/src/stdlib/subgraph/element/entity.ts
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great work, Alfie! I've gotten through most files now and added some comments :)
) as Entity<Temporal>[]; | ||
} else { | ||
return getEntityRevisionsByEntityId( | ||
subgraph as Subgraph<true>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't we have Subgraph<false>
in this branch?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
You are very correct
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
) as Entity<Temporal>[]; | ||
} else { | ||
return getEntityRevisionsByEntityId( | ||
subgraph as Subgraph<true>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same as above (you are very correct), thanks for spotting! Hard to find these things and annoying that TS doesn't care
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
intervalOverlapsInterval( | ||
interval, | ||
/* | ||
these casts are safe as we check for `targetRevisionInformation === undefined` above and that's only ever |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This targetRevisionInformation === undefined
check doesn't seem t obe present in this function, it happens in getEntityRevision
line 70. Is this check meant to happen here as well or is the comment not relevant?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a mistake, it should be interval !== undefined
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🌟 What is the purpose of this PR?
As expressed in previous PRs, we intend to add the foundations of support for bi-temporal datastores within the BP as part of the 0.3 release. This PR modifies the Graph Service to support such use-cases. It is necessarily a large PR as adding generics tends to propagate immensely.
I recommend you review this commit by commit.
🔗 Related links
🚫 Blocked by
N/A
🔍 What does this change?
Please see commits :(
📜 Does this require a change to the docs?
stdlib
andstdlib-temporal
endpoints for now. And am exposing the most general implementations of everything on the API🐾 Next steps
🛡 What tests cover this?
tsc
andeslint
.❓ How to test this?
lint:tsc
andlint:eslint
pass for the@blockprotocol/graph
package📹 Demo
N/A