diff --git a/34.md b/34.md index b2ba81cc..42011c1b 100644 --- a/34.md +++ b/34.md @@ -6,72 +6,62 @@ Algorithmic Filter `draft` `optional` `author:arthurfranca` +This NIP introduces a set of simple algorithms meant to support diverse apps' event sorting needs. + +`Relays` MUST store an extra event field for each algo. Today there are two: `asc` and `seen_at`. + According to [NIP-01](01.md), filters with `limit` attribute are replied with events sorted in **descending** order by the `created_at` event field (newest events first). -For filters containing `limit` attribute, this NIP-34 adds `nip34` as a new filter attribute -whose value indicates the algorithm the `client` wishes to use for sorting events. - -For instance, if a `client` sends the message `["REQ", , { kinds: [1], ..., limit: 5, nip34: "a" }]`, -it is asking the `relay` to sort five kind 1 events in **descending** order by the `nip34a` event field instead of by `created_at`. - -In the above example, `relays` that want to support the NIP-34a algorithm must add the `nip34a` event field to each event saved to their internal databases. The new field is populated following rules described at the NIP-34a NIP extension. - -Besides that, upon replying to such requests, the supporting `relay` MUST add `nip34: { score: "" }` field to each returned event JSON. - -It is a backward-compatible proposal because non-supporting `relays` ignore the `nip34` filter attribute and sort by `created_at`, as usual. - -`Relays` should advertise support using both [NIP-11](11.md) `supported_nips` and `supported_nip_extensions` fields. - -`Relays` should limit max daily user events, such as likes, reposts, zaps and comments, per IP to avoid bad actors gaming the algorithms. - -## Motivation - -Different algorithms are required to support diverse apps' event sorting needs. - -However, they should be presented as optional and backward-compatible features that are easy to support. - -Considering the request example above, we expect most `relays` to simply swap the `created_at` field -on a regular query for the `nip34a` one. If a `relay` implementation uses a SQL DB, the above filter would turn this: +But now when a `client` requests events while filtering by an algo field, the `relay` MUST replace the `created_at` field in the query with the algo field. For example, a SQL relay should turn this: `SELECT * FROM events WHERE kind in (1) ORDER BY created_at DESC LIMIT 5` Into this: -`SELECT * FROM events WHERE kind in (1) ORDER BY nip34a DESC LIMIT 5` +`SELECT * FROM events WHERE kind in (1) ORDER BY asc DESC LIMIT 5` + +Upon replying to such requests, the supporting `relay` MUST add `algo: { score: "" }` field to each returned event JSON. + +## Querying + +An extra `algo` filter key holds the selected algorithm as value. For example, the above +query is ran in response to the following request: + +`["REQ", , { kinds: [1], limit: 5, algo: "asc" }]` + +## Algorithms + +### Asc + +`Relay` computes `asc` field once upon receiving the event. The lower the `created_at`, the higher `asc` will be. + +```js +function getAsc (createdAt) { + const maxDateNowSeconds = 8640000000000 // 8.64e15 ms / 1000 + + // Make it lower the greater the ts is (the newer the createdAt is) + // maxDateNowSeconds - -maxDateNowSeconds equals 17280000000000 and is lower than Number.MAX_SAFE_INTEGER + return maxDateNowSeconds - createdAt +} +event.asc = getAsc(event.created_at) +``` + +### Seen At + +`Relay` computes `seen_at` field once upon receiving the event. The event field is set with the timestamp of the moment the `relay` first became aware of it, in seconds. + +```js +event.seen_at = Math.floor(Date.now() / 1000) +``` ## Relay Connection URL Query Parameter -For each supported NIP-34 algorithm, the `relay` MUST allow `clients` to specify the algo as a `nip34` query param on connection URL string. +For each algorithm, the `relay` MUST allow `clients` to specify the algo as an `algo` query param on the connection URL string. -For example, if supporting the NIP-34a algo, a `relay` whose regular URL is `wss://relay.url/r1` must -also respond at `wss://relay.url/r1?nip34=a`. +In other words, a `relay` whose regular URL is `wss://relay.url/r1` MUST also respond at `wss://relay.url/r1?algo=asc` and `wss://relay.url/r1?algo=seen_at`. -Then when `clients` connect to `wss://relay.url/r1?nip34=a` and request events using `limit` -filter attribute, the relay will automatically sort events in **descending** order by the `nip34a` event field instead of by `created_at` when replying. Clients can still override the chosen algorithm if using the -`nip34` filter attribute. +Then, for example, when `clients` connect to `wss://relay.url/r1?algo=asc` and request events using the `limit` +filter attribute, the relay will automatically sort events using the `asc` event field instead of the `created_at` one when replying. -## How Clients Make Custom Requests for a Specific User - -The algorithms are generic, meaning the "score" represented by the new event field does not differ for different users. - -However, smart `clients` may keep track of pubkeys from whom the user is consuming content, also hashtags, for instance, and use such pubkeys/hashtags as a way to tailor the query to that specific user. For example: - -`["REQ", , { kinds: [1], limit: 5, nip34: "b", authors: ["pubkey1 user follows", "pubkey2 user has read content from recently", "pubkey3 that user has liked content recently"] }, { kinds: [1], limit: 5, nip34: "b", "#t": ["cat", "bitcoin"] }]` - -## Rules for Submitting a NIP-34 Algorithm - -Each algorithm section describes **how** and **when** to compute the corresponding database field for each event. - -For example, [NIP-34asc](34asc.md) extension teaches `relays` the math used to update the `nip34asc` event field. - -The NIP extension MUST have atleast one example in any programming language, preferably with inline comments. - -Bugfixes and small updates to embrace new event kinds may be submitted at any time by the algorithm author(s). - -## Available Algorithms - -| Extension | Name | Description | Modification Date | -| ------------------- | ----------| -------------------------------------------------- | ----------------- | -| [34asc](34asc.md) | Ascending | Events with older `created_at` are retrieved first | 18 / jul / 2023 | -| [34seen](34seen.md) | Seen At | Events are desc sorted by first seen at timestamp | 18 / jul / 2023 | +Clients can still override the chosen algorithm if using the `algo` filter attribute on `REQ` messages. diff --git a/34asc.md b/34asc.md deleted file mode 100644 index 5fcca827..00000000 --- a/34asc.md +++ /dev/null @@ -1,34 +0,0 @@ -NIP-34asc -========= - -Ascending ---------- - -`draft` `optional` `author:arthurfranca` - -Events with older `created_at` are retrieved first. - -## Motivation - -For thread building it may better showing first comments at top to make it easier to understand -the context of newer comments. - -## Implementation - -`Relay` computes `nip34asc` field once upon receiving the event. - -The lower the `created_at`, the higher `nip34asc` will be. - -### Javascript - -```js -function getNip34asc (createdAt) { - const maxDateNowSeconds = 8640000000000 // 8.64e15 ms / 1000 - - // Make it lower the greater the ts is (the newer the createdAt is) - // maxDateNowSeconds - -maxDateNowSeconds equals 17280000000000 and is lower than Number.MAX_SAFE_INTEGER - return maxDateNowSeconds - createdAt -} - -event.nip34asc = getNip34asc(event.created_at) -``` diff --git a/34seen.md b/34seen.md deleted file mode 100644 index 15c37cd2..00000000 --- a/34seen.md +++ /dev/null @@ -1,26 +0,0 @@ -NIP-34seen -========== - -Seen At -------- - -`draft` `optional` `author:arthurfranca` `author:mikedilger` - -Events are desc sorted by first seen at timestamp. - -## Motivation - -Some `clients` may want to check if `relay` has recently received old events -created before last time they checked. - -## Implementation - -`Relay` computes `nip34seen` field once upon receiving the event. - -The event field is set with the timestamp of the moment the `relay` first became aware of it. - -### Javascript - -```js -event.nip34seen = Math.floor(Date.now() / 1000) -```