share / sharedb

Realtime database backend based on Operational Transformation (OT)
Other
6.21k stars 449 forks source link

Server Rendering for Queries? #501

Open curran opened 3 years ago

curran commented 3 years ago

Greetings,

Is it possible to execute a ShareDB Query server-side, then pass the results data into the client via server rendering and use that to data hydrate a client-side ShareDB query?

Use case: a page that lists a number of ShareDB documents, where server rendering is in play.

Desired behavior: the query runs against the database once during server rendering, then in the client side the same query can be instantiated with results from the first execution, then subscribed for updates without triggering the query to be executed again.

I notice there is an internal method on Queries called _ingestSnapshots. This seems useful for the task at hand.

Is it possible to leverage that method? Might it be possible to expose that method as part of the public API?

curran commented 3 years ago

I have a feeling Racer might support this.

curran commented 3 years ago

I notice that createSubscribeQuery has options.results, described as "Prior query results, if available, such as from server rendering." However it's not clear how to generate these results and what type of objects they are expected to be.

Should results be an array of Doc instances? If so, how would one go about populating them? Could one call doc.ingestSnapshot for each of those? For that, we'd need an array of Snapshots corresponding to query results. For that, it would indeed be useful to have a utility method on Doc that generates a Snapshot. If we had that, we could iterate over results from a fetched query and serialize that during server rendering.

curran commented 3 years ago

Found it in Racer thanks to @ericyhwang

https://github.com/derbyjs/racer/blob/master/lib/Model/bundle.js

https://github.com/derbyjs/racer/blob/master/lib/Model/unbundle.js

curran commented 2 years ago

I'm still trying to figure out how to do this. The Racer logic does not appear to be directly usable.

It seems that connection.createSubscribeQuery() from the client side (as seen in Leaderboard) is the most common approach, but has anyone experimented with server rendering query results? Are there any examples out there where the query is executed in the server, then the results are deserialized and instantiated client side?

I understand that connection.createSubscribeQuery(collection, query [, options [, callback]]) can be invoked where options.results is defined, but how exactly to define it is not clear.

The documentation states:

But what kind of Object[] should it be? An array of snapshots? An array of fetched Docs? An array of subscribed Docs?

I am also unable to find any test code that populates options.results (in test/client/query-subscribe.js).

Thanks in advance!

ericyhwang commented 2 years ago

The source code lists options.results as an array of Doc instances, as obtained from connection.get(collection, id): https://github.com/share/sharedb/blob/1cca122c63329665ebfdeceb1984921badb0cf4d/lib/client/connection.js#L563-L567

I'll update the docs to indicate the specific type of options.results!

To initially populate a Doc on the client based on server-rendered data, you'd use Doc#ingestSnapshot

curran commented 2 years ago

Thank you @ericyhwang for investigating!

To summarize: