Compile intersections
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import {uniq, tryCatch, now, isNil} from '@welshman/lib'
|
||||
import type {Rumor, Filter} from '@welshman/util'
|
||||
import {Tags, getIdFilters, mergeFilters} from '@welshman/util'
|
||||
import {Tags, intersectFilters, BOGUS_RELAY_URL, getIdFilters, unionFilters} from '@welshman/util'
|
||||
import type {RequestItem, DVMItem, Scope, Feed, DynamicFilter, FeedOptions} from './core'
|
||||
import {FeedType, getSubFeeds} from './core'
|
||||
|
||||
@@ -19,6 +19,7 @@ export class FeedCompiler<E extends Rumor> {
|
||||
switch(type) {
|
||||
case FeedType.Relay:
|
||||
case FeedType.Union:
|
||||
case FeedType.Intersection:
|
||||
return getSubFeeds([type, ...feed] as Feed).every(this.canCompile)
|
||||
case FeedType.Filter:
|
||||
case FeedType.List:
|
||||
@@ -33,6 +34,8 @@ export class FeedCompiler<E extends Rumor> {
|
||||
switch(type) {
|
||||
case FeedType.Union:
|
||||
return await this._compileUnion(feed as Feed[])
|
||||
case FeedType.Intersection:
|
||||
return await this._compileIntersection(feed as Feed[])
|
||||
case FeedType.Relay:
|
||||
/* eslint no-case-declarations: 0 */
|
||||
const {relays, filters} = await this._compileUnion(feed.slice(1) as Feed[])
|
||||
@@ -72,10 +75,30 @@ export class FeedCompiler<E extends Rumor> {
|
||||
|
||||
return {
|
||||
relays: uniq(relays),
|
||||
filters: mergeFilters(filters),
|
||||
filters: unionFilters(filters),
|
||||
}
|
||||
}
|
||||
|
||||
async _compileIntersection(feeds: Feed[]): Promise<RequestItem> {
|
||||
const items = await Promise.all(feeds.map(this.compile))
|
||||
const filters = intersectFilters(items.map(item => item.filters))
|
||||
|
||||
let relays = uniq(items.flatMap(item => item.relays))
|
||||
let hasRelays = relays.length > 0
|
||||
|
||||
items.forEach((item, i) => {
|
||||
if (item.relays.length > 0) {
|
||||
relays = relays.filter(relay => item.relays.includes(relay))
|
||||
}
|
||||
})
|
||||
|
||||
if (hasRelays && relays.length === 0) {
|
||||
relays.push(BOGUS_RELAY_URL)
|
||||
}
|
||||
|
||||
return {relays, filters}
|
||||
}
|
||||
|
||||
async _compileLists(addresses: string[]): Promise<RequestItem> {
|
||||
const events: E[] = []
|
||||
|
||||
|
||||
@@ -31,6 +31,6 @@
|
||||
"typescript": "~5.1.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@welshman/util": "0.0.2"
|
||||
"@welshman/util": "0.0.3"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import type {Event} from 'nostr-tools'
|
||||
import {Emitter, randomId, groupBy, batch, defer, uniq, uniqBy} from '@welshman/lib'
|
||||
import type {Deferred} from '@welshman/lib'
|
||||
import {matchFilters, mergeFilters} from '@welshman/util'
|
||||
import {matchFilters, unionFilters} from '@welshman/util'
|
||||
import type {Filter} from '@welshman/util'
|
||||
import {Tracker} from "./Tracker"
|
||||
import {Connection} from './Connection'
|
||||
@@ -83,7 +83,7 @@ export const mergeSubscriptions = (subs: Subscription[]) => {
|
||||
const mergedSub = makeSubscription({
|
||||
relays: [relay],
|
||||
timeout: callerSubs[0].request.timeout,
|
||||
filters: mergeFilters(callerSubs.flatMap((sub: Subscription) => sub.request.filters)),
|
||||
filters: unionFilters(callerSubs.flatMap((sub: Subscription) => sub.request.filters)),
|
||||
})
|
||||
|
||||
for (const {id, emitter} of callerSubs) {
|
||||
|
||||
@@ -33,7 +33,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@welshman/lib": "0.0.2",
|
||||
"@welshman/util": "0.0.2",
|
||||
"@welshman/util": "0.0.3",
|
||||
"isomorphic-ws": "^5.0.0",
|
||||
"ws": "^8.16.0"
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ export const calculateFilterGroup = ({since, until, limit, search, ...filter}: F
|
||||
return group.sort().join("-")
|
||||
}
|
||||
|
||||
export const mergeFilters = (filters: Filter[]) => {
|
||||
export const unionFilters = (filters: Filter[]) => {
|
||||
const result = []
|
||||
|
||||
for (const group of Object.values(groupBy(calculateFilterGroup, filters))) {
|
||||
@@ -96,6 +96,38 @@ export const mergeFilters = (filters: Filter[]) => {
|
||||
return result
|
||||
}
|
||||
|
||||
export const intersectFilters = (groups: Filter[][]) => {
|
||||
let result = groups[0]
|
||||
|
||||
for (const filters of groups.slice(1)) {
|
||||
result = result.flatMap(f1 => {
|
||||
return filters.map(f2 => {
|
||||
const f3: Filter = {}
|
||||
|
||||
for (const k of uniq([...Object.keys(f1), ...Object.keys(f2)]) as (keyof Filter)[]) {
|
||||
if (k === 'since' || k === 'limit') {
|
||||
f3[k] = Math.max(f1[k] || 0, f2[k] || 0)
|
||||
} else if (k === 'until') {
|
||||
f3[k] = Math.min(f1[k] || f2[k] || 0, f2[k] || f1[k] || 0)
|
||||
} else if (k === 'search') {
|
||||
if (f1[k] && f2[k] && f1[k] !== f2[k]) {
|
||||
f3[k] = [f1[k], f2[k]].join(' ')
|
||||
} else {
|
||||
f3[k] = f1[k] || f2[k]
|
||||
}
|
||||
} else {
|
||||
f3[k] = uniq([...(f1[k] || []), ...(f2[k] || [])]) as any[]
|
||||
}
|
||||
}
|
||||
|
||||
return f3
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return unionFilters(result)
|
||||
}
|
||||
|
||||
export const getIdFilters = (idsOrAddresses: Iterable<string>) => {
|
||||
const ids = []
|
||||
const aFilters = []
|
||||
@@ -114,7 +146,7 @@ export const getIdFilters = (idsOrAddresses: Iterable<string>) => {
|
||||
}
|
||||
}
|
||||
|
||||
const filters = mergeFilters(aFilters)
|
||||
const filters = unionFilters(aFilters)
|
||||
|
||||
if (ids.length > 0) {
|
||||
filters.push({ids})
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
import {normalizeUrl, stripProtocol} from '@welshman/lib'
|
||||
|
||||
export const LOCAL_RELAY_URL = "local://welshman.relay"
|
||||
|
||||
export const BOGUS_RELAY_URL = "bogus://welshman.relay"
|
||||
|
||||
export const isShareableRelayUrl = (url: string) =>
|
||||
Boolean(
|
||||
typeof url === 'string' &&
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@welshman/util",
|
||||
"version": "0.0.2",
|
||||
"version": "0.0.3",
|
||||
"author": "hodlbod",
|
||||
"license": "MIT",
|
||||
"description": "A collection of nostr-related utilities.",
|
||||
|
||||
Reference in New Issue
Block a user