Queries
The QueryStore
class wraps the EventStore
and allows you to subscribe to computed state in the form on Queries
The role of the query store is to keep track of all queries, wrap them in the rxjs share
operator and ensure only one instance of each is created
IMPORTANT
For performance reasons UI components should only subscribe to the QueryStore
and NOT the EventStore
Creating a query store
import { EventStore, QueryStore } from "applesauce-core";
const eventStore = new EventStore();
// the query store takes the upstream event store as the first argument
const queryStore = new QueryStore(eventStore);
Running a query
The queryStore.createQuery
can be used to create and register a query in the query store
Queries are not started until the they are subscribe to. and they are destroyed when all subscriptions are closed
// The first argument is the query constructor and the remaining are passed to the query
const observable = queryStore.createQuery(TimelineQuery, [{ kinds: [1] }]);
// start the query by subscribing to it
observable.subscribe((events) => {
console.log(events);
});
// adding events to the event store will update the timeline query
eventStore.add({kind: 1, content: 'new note', ...})
Performance
The query store keeps track of what queries have been created and will ensure that only a single instance is created
const notes = queryStore.createQuery(TimelineQuery, [{ kinds: [1] }]);
// lots of code...
const otherTimeline = queryStore.createQuery(TimelineQuery, [{ kinds: [1] }]);
// because the query with the same arguments was created before
// the second call will return the same observable
console.log(otherTimeline === notes);
// this will create a new query because the filter is different
const files = queryStore.createQuery(TimelineQuery, [{ kinds: [1063] }]);
Prebuilt queries
Queries are methods that construct complex observable pipelines off of the EventStore
They are run inside the QueryStore
which ensures there are not duplicate queries
- The
SingleEventQuery
can be used to subscribe to a single event. - The
MultipleEventsQuery
can be used to subscribe to multiple events. - The
ReplaceableQuery
can be used to subscribe to the latest version of a replaceable event. - The
ReplaceableSetQuery
can be used to subscribe to an array ofAddressPointer
. - The
TimelineQuery
can be used to subscribe to a sorted array of events that match filters. - The
ProfileQuery
can be used to subscribe to a single pubkey's profile (kind 0). - The
MailboxesQuery
can be used to subscribe to a single pubkey's mailboxes (kind 10002). - The
ReactionsQuery
can be used to subscribe to all NIP-25 reactions to an event.
Custom Queries
A custom query is simply a function that returns a Query
object
For example here is a custom query that will subscribe to and parse a NIP-78 app event that contains json
import { map } from "rxjs/operators";
function AppSettingsQuery<T>(pubkey: string): Query<T> {
return {
key: pubkey,
run: (eventStore) => {
return eventStore.replaceable(30078, pubkey, "app-settings").pipe(
map((event) => {
if (!event) return undefined;
return JSON.parse(event.content) as T;
}),
);
},
};
}
const sub = queryStore
.createQuery(AppSettingsQuery, "3bf0c63fcb93463407af97a5e5ee64fa883d107ef9e558472c4eb9aaaefa459d")
.subscribe((json) => {
// json will either be undefined or { theme: string }
if (json) console.log("updated data", json);
});
eventStore.add({
kind: 30078,
content: '{"theme": "dark"}',
// rest of event
});