fix idb search, only 1 read query at a time

This commit is contained in:
Martti Malmi
2024-01-04 22:30:20 +02:00
parent 2fa4065414
commit f82109b6b3

View File

@ -11,6 +11,7 @@ type Tag = {
}; };
type SaveQueueEntry = { event: TaggedNostrEvent; tags: Tag[] }; type SaveQueueEntry = { event: TaggedNostrEvent; tags: Tag[] };
type Task = () => Promise<void>;
class IndexedDB extends Dexie { class IndexedDB extends Dexie {
events!: Table<TaggedNostrEvent>; events!: Table<TaggedNostrEvent>;
@ -20,6 +21,8 @@ class IndexedDB extends Dexie {
private subscribedAuthors = new Set<string>(); private subscribedAuthors = new Set<string>();
private subscribedTags = new Set<string>(); private subscribedTags = new Set<string>();
private subscribedAuthorsAndKinds = new Set<string>(); private subscribedAuthorsAndKinds = new Set<string>();
private readQueue: Map<string, Task> = new Map();
private isProcessingQueue = false;
constructor() { constructor() {
super("EventDB"); super("EventDB");
@ -93,78 +96,71 @@ class IndexedDB extends Dexie {
this.saveQueue.push({ event, tags }); this.saveQueue.push({ event, tags });
} }
_throttle(func, limit) { private async startReadQueue() {
let inThrottle; if (!this.isProcessingQueue && this.readQueue.size > 0) {
return function (...args) { this.isProcessingQueue = true;
if (!inThrottle) { for (const [key, task] of this.readQueue.entries()) {
inThrottle = true; console.log('starting idb task', key);
setTimeout(() => { console.time(key);
inThrottle = false; await task();
func.apply(this, args); this.readQueue.delete(key);
}, limit); console.timeEnd(key);
}
this.isProcessingQueue = false;
} }
};
} }
subscribeToAuthors = this._throttle(async function (callback: (event: TaggedNostrEvent) => void, limit?: number) { private enqueueRead(key: string, task: () => Promise<void>) {
this.readQueue.set(key, task);
this.startReadQueue();
}
getByAuthors = async (callback: (event: TaggedNostrEvent) => void, limit?: number) => {
this.enqueueRead('getByAuthors', async () => {
const authors = [...this.subscribedAuthors]; const authors = [...this.subscribedAuthors];
this.subscribedAuthors.clear(); this.subscribedAuthors.clear();
// Start timing
console.time("subscribeToAuthors");
await this.events await this.events
.where("pubkey") .where("pubkey")
.anyOf(authors) .anyOf(authors)
.limit(limit || 1000) .limit(limit || 1000)
.each(callback); .each(callback);
});
};
// End timing and log the elapsed time getByEventIds = async (callback: (event: TaggedNostrEvent) => void) => {
console.timeEnd("subscribeToAuthors"); this.enqueueRead('getByEventIds', async () => {
}, 200);
subscribeToEventIds = this._throttle(async function (callback: (event: TaggedNostrEvent) => void) {
const ids = [...this.subscribedEventIds]; const ids = [...this.subscribedEventIds];
this.subscribedEventIds.clear(); this.subscribedEventIds.clear();
console.time("subscribeToEventIds");
await this.events.where("id").anyOf(ids).each(callback); await this.events.where("id").anyOf(ids).each(callback);
});
};
console.timeEnd("subscribeToEventIds"); getByTags = async (callback: (event: TaggedNostrEvent) => void) => {
}, 200); this.enqueueRead('getByTags', async () => {
subscribeToTags = this._throttle(async function (callback: (event: TaggedNostrEvent) => void) {
const tagPairs = [...this.subscribedTags].map(tag => tag.split("|")); const tagPairs = [...this.subscribedTags].map(tag => tag.split("|"));
this.subscribedTags.clear(); this.subscribedTags.clear();
console.time("subscribeToTags");
await this.tags await this.tags
.where("[type+value]") .where("[type+value]")
.anyOf(tagPairs) .anyOf(tagPairs)
.each(tag => this.subscribedEventIds.add(tag.eventId)); .each(tag => this.subscribedEventIds.add(tag.eventId));
await this.subscribeToEventIds(callback); await this.getByEventIds(callback);
});
};
console.timeEnd("subscribeToTags"); getByAuthorsAndKinds = async (callback: (event: TaggedNostrEvent) => void) => {
}, 200); this.enqueueRead('authorsAndKinds', async () => {
subscribeToAuthorsAndKinds = this._throttle(async function (callback: (event: TaggedNostrEvent) => void) {
const authorsAndKinds = [...this.subscribedAuthorsAndKinds]; const authorsAndKinds = [...this.subscribedAuthorsAndKinds];
this.subscribedAuthorsAndKinds.clear(); this.subscribedAuthorsAndKinds.clear();
console.time("subscribeToAuthorsAndKinds");
// parse pair[1] as int
const pairs = authorsAndKinds.map(pair => { const pairs = authorsAndKinds.map(pair => {
const [author, kind] = pair.split("|"); const [author, kind] = pair.split("|");
return [author, parseInt(kind)]; return [author, parseInt(kind)];
}); });
await this.events.where("[pubkey+kind]").anyOf(pairs).each(callback); await this.events.where("[pubkey+kind]").anyOf(pairs).each(callback);
});
console.timeEnd("subscribeToAuthorsAndKinds"); };
}, 200);
async find(filter: Filter, callback: (event: TaggedNostrEvent) => void): Promise<void> { async find(filter: Filter, callback: (event: TaggedNostrEvent) => void): Promise<void> {
if (!filter) return; if (!filter) return;
@ -180,7 +176,7 @@ class IndexedDB extends Dexie {
this.subscribedTags.add("p|" + eventId); this.subscribedTags.add("p|" + eventId);
} }
await this.subscribeToTags(cb); await this.getByTags(cb);
return; return;
} }
@ -189,7 +185,7 @@ class IndexedDB extends Dexie {
this.subscribedTags.add("e|" + eventId); this.subscribedTags.add("e|" + eventId);
} }
await this.subscribeToTags(cb); await this.getByTags(cb);
return; return;
} }
@ -198,26 +194,26 @@ class IndexedDB extends Dexie {
this.subscribedTags.add("d|" + eventId); this.subscribedTags.add("d|" + eventId);
} }
await this.subscribeToTags(cb); await this.getByTags(cb);
return; return;
} }
if (filter.ids?.length) { if (filter.ids?.length) {
filter.ids.forEach(id => this.subscribedEventIds.add(id)); filter.ids.forEach(id => this.subscribedEventIds.add(id));
await this.subscribeToEventIds(cb); await this.getByEventIds(cb);
return; return;
} }
if (filter.authors?.length && filter.kinds?.length) { if (filter.authors?.length && filter.kinds?.length) {
const permutations = filter.authors.flatMap(author => filter.kinds!.map(kind => author + "|" + kind)); const permutations = filter.authors.flatMap(author => filter.kinds!.map(kind => author + "|" + kind));
permutations.forEach(permutation => this.subscribedAuthorsAndKinds.add(permutation)); permutations.forEach(permutation => this.subscribedAuthorsAndKinds.add(permutation));
await this.subscribeToAuthorsAndKinds(cb); await this.getByAuthorsAndKinds(cb);
return; return;
} }
if (filter.authors?.length) { if (filter.authors?.length) {
filter.authors.forEach(author => this.subscribedAuthors.add(author)); filter.authors.forEach(author => this.subscribedAuthors.add(author));
await this.subscribeToAuthors(cb); await this.getByAuthors(cb);
return; return;
} }
@ -226,15 +222,18 @@ class IndexedDB extends Dexie {
query = query.where("kind").anyOf(filter.kinds); query = query.where("kind").anyOf(filter.kinds);
} }
if (filter.search) { if (filter.search) {
const regexp = new RegExp(filter.search, "i"); const term = filter.search.replace(" sort:popular", "");
const regexp = new RegExp(term, "i");
query = query.filter((event: Event) => event.content?.match(regexp)); query = query.filter((event: Event) => event.content?.match(regexp));
filter.limit = filter.limit || 100;
} }
if (filter.limit) { if (filter.limit) {
query = query.limit(filter.limit); query = query.limit(filter.limit);
} }
// TODO test that the sort is actually working // TODO test that the sort is actually working
await query.each(e => { const filterString = JSON.stringify(filter);
cb(e); this.enqueueRead(filterString, async () => {
await query.each(cb);
}); });
} }
} }