refactor request builder to handle relay hints

This commit is contained in:
2023-05-25 19:52:03 +01:00
parent ca92b365e0
commit 9a33466c7c
11 changed files with 242 additions and 118 deletions

View File

@ -1,10 +1,14 @@
import { RawReqFilter, u256, HexKey, EventKind } from "@snort/nostr";
import { appendDedupe } from "SnortUtils";
import { appendDedupe, dedupe } from "SnortUtils";
import { QueryBase } from "./Query";
import { diffFilters } from "./RequestSplitter";
import { RelayCache, splitByWriteRelays } from "./GossipModel";
import { mergeSimilar } from "./RequestMerger";
/**
* Which strategy is used when building REQ filters
*/
export enum NostrRequestStrategy {
export enum RequestStrategy {
/**
* Use the users default relays to fetch events,
* this is the fallback option when there is no better way to query a given filter set
@ -26,10 +30,9 @@ export enum NostrRequestStrategy {
* A built REQ filter ready for sending to System
*/
export interface BuiltRawReqFilter {
id: string;
filter: Array<RawReqFilter>;
relays: Array<string>;
strategy: NostrRequestStrategy;
filter: RawReqFilter;
relay: string;
strategy: RequestStrategy;
}
export interface RequestBuilderOptions {
@ -76,8 +79,55 @@ export class RequestBuilder {
return this;
}
build(): Array<RawReqFilter> {
return this.#builders.map(a => a.filter);
buildRaw(): Array<RawReqFilter> {
return this.#builders.map(f => f.filter);
}
build(relays: RelayCache): Array<BuiltRawReqFilter> {
const expanded = this.#builders.map(a => a.build(relays)).flat();
return this.#mergeSimilar(expanded);
}
/**
* Detects a change in request from a previous set of filters
* @param q All previous filters merged
* @returns
*/
buildDiff(relays: RelayCache, q: QueryBase): Array<BuiltRawReqFilter> {
const next = this.buildRaw();
const diff = diffFilters(q.filters, next);
if (diff.changed) {
}
return [];
}
/**
* Merge a set of expanded filters into the smallest number of subscriptions by merging similar requests
* @param expanded
* @returns
*/
#mergeSimilar(expanded: Array<BuiltRawReqFilter>) {
const relayMerged = expanded.reduce((acc, v) => {
const existing = acc.get(v.relay);
if (existing) {
existing.push(v);
} else {
acc.set(v.relay, [v]);
}
return acc;
}, new Map<string, Array<BuiltRawReqFilter>>());
const filtersSquashed = [...relayMerged.values()].flatMap(a => {
return mergeSimilar(a.map(b => b.filter)).map(b => {
return {
filter: b,
relay: a[0].relay,
strategy: a[0].strategy,
} as BuiltRawReqFilter;
});
});
return filtersSquashed;
}
}
@ -86,7 +136,7 @@ export class RequestBuilder {
*/
export class RequestFilterBuilder {
#filter: RawReqFilter = {};
#relayHints: Map<u256, Array<string>> = new Map();
#relayHints = new Map<u256, Array<string>>();
get filter() {
return { ...this.#filter };
@ -149,4 +199,44 @@ export class RequestFilterBuilder {
this.#filter.search = keyword;
return this;
}
/**
* Build/expand this filter into a set of relay specific queries
*/
build(relays: RelayCache): Array<BuiltRawReqFilter> {
// when querying for specific event ids with relay hints
// take the first approach which is to split the filter by relay
if (this.#filter.ids && this.#relayHints.size > 0) {
const relays = dedupe([...this.#relayHints.values()].flat());
return relays.map(r => {
return {
filter: {
...this.#filter,
ids: [...this.#relayHints.entries()].filter(([, v]) => v.includes(r)).map(([k]) => k),
},
relay: r,
strategy: RequestStrategy.RelayHintedEventIds,
};
});
}
// If any authors are set use the gossip model to fetch data for each author
if (this.#filter.authors) {
const split = splitByWriteRelays(relays, this.#filter);
return split.map(a => {
return {
...a,
strategy: RequestStrategy.AuthorsRelays,
};
});
}
return [
{
filter: this.filter,
relay: "*",
strategy: RequestStrategy.DefaultRelays,
},
];
}
}