fix idb search, only 1 read query at a time
This commit is contained in:
@ -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);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user