Skip to content
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

[mds-ingest-service] Add pagination and order support to getEvents via ingest #643

Merged

Conversation

cjlynch12
Copy link

πŸ“š Purpose

This PR adds pagination and order support for the existing getEvents() service method in mds-ingest-service.

πŸ‘Œ Resolves:

πŸ“¦ Impacts:

  • mds-ingest-service

βœ… PR Checklist

@@ -42,7 +42,7 @@ export class EventEntity extends IdentityColumn(RecordedColumn(class {})) implem
provider_id: EventEntityModel['provider_id']

@Column('bigint', { transformer: BigintTransformer, primary: true })
timestamp: EventEntityModel['timestamp']
timestamp: Timestamp
Copy link
Author

@cjlynch12 cjlynch12 Jul 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Strange type behavior here with the typeorm-cursor-pagination lib.
When this is left as EventEntityModel['timestamp'], and you pass limit to the buildPaginator(...) function, it results in the following error:

unknown type in cursor: [object]1625149089973

Which corresponds to the time_range: {begin} parameter.

Interestingly, this is only an issue when limit is passed. If the type declaration here is left as EventEntityModel['timestamp'] and limit isn't provided, it works fine. As soon as limit is provided, it breaks.

Changing this to Timestamp directly here fixes the issue in both cases.

This doesn't have any issues afaict, all previous tests are still passing with this change. 🀷

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. We’ve seen this before.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've recently stopped defining entity properties in terms of Entity or (especially) Domain model properties and use the base types instead. Some libraries that use metadata reflection have issues tracing the aliases to the base type. In recent repositories, you will see a pattern something like:

export class SomeEntity {
  @Column(...)
  entity_id: UUID

  @Column(...)
  timestamp: Timestamp
}

/* Create an alias to keep consistent with other services */
export SomeEntityModel = SomeEntity

@@ -199,14 +219,61 @@ class IngestReadWriteRepository extends ReadWriteRepository {
query.andWhere('events.provider_id = ANY(:provider_ids)', { provider_ids })
}

const entities = await query.limit(limit).getMany()
// Use query instead of paginator to manage order if using a joined field
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

couldn't figure out how to pass an order column string for a joined field to buildPaginator without it throwing an error, if there's a better way to handle this field happy to change it.

Copy link

@nessur nessur left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice work!

@@ -70,6 +70,19 @@ export type TimeRange = {
start: Timestamp
end: Timestamp
}

export const GetVehicleEventsOrderColumn = <const>['timestamp', 'vehicle_id', 'provider_id', 'vehicle_state']
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just curious as I've seen this in other places as I learn typescript. Is it common to name the value and type name the same? I've seen it where the value name is descriptive and would name it like "GetVehicleEventOrderColumns" (with an s to describe these are all the columns) and the type is then without the "s". Also, is it preferred to use vs "as const"? Are they the same?

Copy link
Author

@cjlynch12 cjlynch12 Jul 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've seen instances of both

export const FooTypes = <const>['bar', 'baz']
export type FooType = typeof FooTypes[number]

and

export const FooType = <const>['bar', 'baz']
export type FooType = typeof FooType[number]

In various places in the code base. I think the latter is the preferred.

My assumption is that the latter just reduces the overhead of knowing which type to choose (FooTypes vs FooTypes) since TS will interpret it as the type declaration. (@drtyh2o feel free to tell me I'm off base here πŸ˜„ ).

As for
<const> foo vs foo as const, they are equivalent except in a .tsx file.

Copy link

@mdurling mdurling left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are probably opportunities for creating some cursor utilities as there seems to be a lot of similarities between this backend and the trips backend, but this LGTM.

@@ -42,7 +42,7 @@ export class EventEntity extends IdentityColumn(RecordedColumn(class {})) implem
provider_id: EventEntityModel['provider_id']

@Column('bigint', { transformer: BigintTransformer, primary: true })
timestamp: EventEntityModel['timestamp']
timestamp: Timestamp
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep. We’ve seen this before.

@@ -42,7 +42,7 @@ export class EventEntity extends IdentityColumn(RecordedColumn(class {})) implem
provider_id: EventEntityModel['provider_id']

@Column('bigint', { transformer: BigintTransformer, primary: true })
timestamp: EventEntityModel['timestamp']
timestamp: Timestamp
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've recently stopped defining entity properties in terms of Entity or (especially) Domain model properties and use the base types instead. Some libraries that use metadata reflection have issues tracing the aliases to the base type. In recent repositories, you will see a pattern something like:

export class SomeEntity {
  @Column(...)
  entity_id: UUID

  @Column(...)
  timestamp: Timestamp
}

/* Create an alias to keep consistent with other services */
export SomeEntityModel = SomeEntity

const entities = await query.limit(limit).getMany()
// Use query instead of paginator to manage order if using a joined field
if (order && order.column === 'vehicle_id') {
query.orderBy('devices.vehicle_id', order.direction)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm unsure how the query.orderBy and paginationKeys interact, but I assume the pagination keys will get appended to the orderBy. Is timestamp always the secondary sort column?

Copy link
Author

@cjlynch12 cjlynch12 Jul 1, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good call out, I took a look at this and it turns out that paginationKeys always override anything added to the query with orderBy or addOrderBy. If you remove paginationKeys param entirely, it just uses id (the default).

There seems to be an open issue for this (can't sort on relational tables / override paginationKeys) - and the maintainer has ack'd it recently.
benjamin658/typeorm-cursor-pagination#4

So for now, removed the ability to sort by vehicle_id. Will follow up with the open issue. Not worth sinking a bunch of time for this one sort-able field atm.

That being said, sorting on joined tables will most likely come up in the future at some point - so hopefully this gets addressed in typeorm-cursor-pagination shortly.

PS - updated tests since apparently they were false positives for sorting on vehicle_id.

@lacuna-tech-bulldozer lacuna-tech-bulldozer bot merged commit f4500c6 into develop Jul 2, 2021
@lacuna-tech-bulldozer lacuna-tech-bulldozer bot deleted the carter/feature/add-sorting-paging-ingest-events branch July 2, 2021 16:24
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants