# ESAV: ElasticSearch AppView Jetstream plugged into ElasticSearch with a queryable api (also now supports live queries) ## Queries ### the boring one send ES queries to `/xrpc/party.whey.esav.esQuery` either POST or GET q= the format is just a normal elasticsearch query, any of them should work ### the cooler Live one (sync over websocket) ESAV Live is a real-time indexing and query service ESAV. the websocket provides a live stream of new document at:// URIs that match a specific query send Live queries to `wss://{your domain}/xrpc/party.whey.esav.esSync` once a connection is open, the client must send a subscription request (the live query itelf). this message is a JSON object containing the query that defines the feed you are interested in. the query format is basically a dumbed down Elasticsearch Query DSL (stripped to the few stuff thats actually implemented, and thatll produce a deterministic order / result) for example, to subscribe to all statuses from the `xyz.statusphere.status` collection, send the JSON request bleow: ```json { "type": "subscribe", "queryId": "statusphere-main-page", "esquery": { "query": { "term": { "$metadata.collection": "xyz.statusphere.status" } }, "sort": [ { "$metadata.indexedAt": "desc" } ], "size": 100 } } ``` and then server will send back JSON messages as new documents matching your query are indexed. the primary message type youll get is a `query-delta` which will contain an array of new document URIs for each active query, and also the set of new or modified documents to store. example server message: ```json { "type": "query-delta", "documents": { "at://did:plc:mn45tewwnse5btfftvd3powc/xyz.statusphere.status/3lvsr2zqf3k2n": { "cid": "bafyreidym6ad6k7grbz7nrqrnddht2rwxlnimbgbfddthrmftv7mbfk7gy", "doc": { "$metadata.uri": "at://did:plc:mn45tewwnse5btfftvd3powc/xyz.statusphere.status/3lvsr2zqf3k2n", "$metadata.cid": "bafyreidym6ad6k7grbz7nrqrnddht2rwxlnimbgbfddthrmftv7mbfk7gy", "$metadata.indexedAt": "2025-08-07T12:40:09.426Z", "$metadata.did": "did:plc:mn45tewwnse5btfftvd3powc", "$metadata.collection": "xyz.statusphere.status", "$metadata.rkey": "3lvsr2zqf3k2n", "status": "👍", "$raw": { "$type": "xyz.statusphere.status", "createdAt": "2025-08-07T12:40:08.602Z", "status": "👍" } } } }, "queries": { "statusphere-main-page": { "ecid": "bafyreidsxtbtnwce2o72wrwtwhcvc7olosdtvb2i4g6ezbs4kaxn3v3qna", "result": [ "at://did:plc:mn45tewwnse5btfftvd3powc/xyz.statusphere.status/3lvsr2zqf3k2n", "at://did:plc:mn45tewwnse5btfftvd3powc/xyz.statusphere.status/3lvsov4tatf2n", "at://did:plc:mn45tewwnse5btfftvd3powc/xyz.statusphere.status/3lvsouyvrbr2t" ] } } } ``` in here, only one document is given because this is not the initial response after a registered live query. or alternatively, a live query was registered using a still-valid "ecid" value (the window is like 5 minutes so its really only for dropped connections). your application should listen for these messages and prepend the new URIs to its list of statuses, creating the real-time effect ### helper functions because we need to sometimes resolve their did from handle, or handle from did and also because despite your usage of a custom lexicon, we still need to fetch app.bsky.actor.profile for their pfp right fetch `https://esav.whey.party/xrpc/party.whey.esav.resolveIdentity` with these params: - `did`: The DID of the user. - `handle`: The handle of the user. - `includeBskyProfile` (optional): `true` to include the the entire bsky profile object as well. **Example Request:** `https://esav.whey.party/xrpc/party.whey.esav.resolveIdentity?did=did:plc:cjfima2v3vnyfuzieu7bvjx7&includeBskyProfile=true` gets me **Example Response (`200 OK`):** ```json { "did":"did:plc:cjfima2v3vnyfuzieu7bvjx7", "pdsUrl":"https://pds-nd.whey.party", "handle":"forumtest.whey.party", "profile": { "$type": "app.bsky.actor.profile", "avatar": { "$type": "blob", "ref": { "$link": "bafkreiabb6nrbpgguh5xaake3shoyxh2i7lyj7nj7j3ejk77hdlvt4lhmu" }, "mimeType": "image/png", "size": 35262 }, "banner": { "$type": "blob", "ref": { "$link": "bafkreieupktgazj4vxuxbj6dyf4trvltgok5ukdsnvvblsqqw26cmsvn7y" }, "mimeType": "image/jpeg", "size": 931415 }, "createdAt": "2025-08-04T06:27:34.561Z", "description": "ForumTest discussion and development", "displayName": "ForumTest" } } ``` > **Note on Performance**: This endpoint is called frequently. It is highly recommended to implement a client-side cache (e.g., a simple JavaScript `Map` or object) to store profile data keyed by DID. This will prevent your application from making redundant network requests for the same user profile, significantly improving performance. ## stuff built with ESAV ### Statusphere ESAV Live an example usage of ESAV Live with the "hello world!" of atproto check it out here -> [https://statusphere.whey.party/](https://statusphere.whey.party/) repo: [https://tangled.sh/@whey.party/statusphere-esav-live](https://tangled.sh/@whey.party/statusphere-esav-live) ### ForumTest atproto forum thing test check it out here -> [https://forumtest.whey.party](https://forumtest.whey.party) repo: [https://tangled.sh/@whey.party/forumtest](https://tangled.sh/@whey.party/forumtest)