Skip to content

Query builder needs a union method to combine similar collections into one #58

@samwillis

Description

@samwillis

Union operator

We want a way to union two or more collections so that they can be used as a single source within a live query.

There needs to be both static and dynamic union operators, with the latter mutable.

Use cases:

  • Sharding data across multiple collections for smaller sync
  • Dynamically creating datasets from multiple sources - such as a feed reader with a collection for each feed and then a union of all of them for a "all" view.

Static union operator

q
  .from({
    source: union(collection1, collection2)
  })
  .where(({ source }) => gt(source.value, 1))

Implementation

This is the simplest to implement, it can map to a concat operator in the D2 graph. However, there is complexity if we want to use any of the indexes in the underlying collections. A first version could just de-optimise the query to use a full scan and then we can add optimizations later.

Dynamic union operator with manual mutation

Often a union may not be static, but something that you want to be able to add and remove sources from without having to restart the live query.

// Union returns a set like object
const sourcesUnion = union(collection1)

q
  .from({
    source: sourcesUnion
  })
  .where(({ source }) => gt(source.value, 1))

sources.add(collection2)

// do something

sources.remove(collection1)

Implementation

This is more complex to implement, we need to be able to mutate the set as the results of the live query change. We would need to maintain some sort of history of what has been sent in order to pass in the deletes when a source is removed.

Dynamic union operator constructed from a live query itself

An additional unionFromLiveQuery function that takes a live query, and a function that takes a results from that live query, and returns a collection. This wraps the union function, mutating the set as the results of the live query change.

const sourcesUnion = unionFromLiveQuery(
  (q) => q
    .from({
      rssFeed: rssFeedsCollection
    }),
  ({ rssFeed }) => createCollection(
    rssFeedsCollectionOptions({
      url: rssFeed.url
    })
  )
)

q
  .from({
    source: sourcesUnion
  })
  .where(({ source }) => gt(source.value, 1))

Implementation

This is a little simpler to implement as its really just a wrapper around the union function with mutations driven from the live query.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions