Compile intersections

This commit is contained in:
Jon Staab
2024-04-29 12:51:47 -07:00
parent b88c074d86
commit 00b142243b
8 changed files with 71 additions and 12 deletions
+3 -3
View File
@@ -3064,7 +3064,7 @@
"version": "0.0.2",
"license": "MIT",
"dependencies": {
"@welshman/util": "0.0.2"
"@welshman/util": "0.0.3"
},
"devDependencies": {
"gts": "^5.0.1",
@@ -3102,7 +3102,7 @@
"license": "MIT",
"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"
},
@@ -3115,7 +3115,7 @@
},
"packages/util": {
"name": "@welshman/util",
"version": "0.0.2",
"version": "0.0.3",
"license": "MIT",
"dependencies": {
"@welshman/lib": "0.0.2",
+25 -2
View File
@@ -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[] = []
+1 -1
View File
@@ -31,6 +31,6 @@
"typescript": "~5.1.6"
},
"dependencies": {
"@welshman/util": "0.0.2"
"@welshman/util": "0.0.3"
}
}
+2 -2
View File
@@ -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) {
+1 -1
View File
@@ -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"
}
+34 -2
View File
@@ -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})
+4
View File
@@ -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 -1
View File
@@ -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.",