@@ -253,16 +253,13 @@ export class ActivityPubDB extends EventTarget {
253253 await tx . done ( )
254254 }
255255
256- async * searchNotes ( { timeline, attributedTo, inReplyTo } = { } , { skip = 0 , limit = DEFAULT_LIMIT , sort = - 1 } = { } ) {
256+ async * searchNotes ( { timeline, attributedTo, inReplyTo, excludeReplies } = { } , { skip = 0 , limit = DEFAULT_LIMIT , sort = - 1 } = { } ) {
257257 const tx = this . db . transaction ( NOTES_STORE , 'readonly' )
258258 let indexName , keyRange
259259
260260 if ( inReplyTo ) {
261261 indexName = IN_REPLY_TO_FIELD
262262 keyRange = IDBKeyRange . only ( inReplyTo )
263- } else if ( timeline ) {
264- indexName = 'timeline'
265- keyRange = IDBKeyRange . only ( timeline )
266263 } else if ( attributedTo ) {
267264 indexName = ATTRIBUTED_TO_FIELD
268265 keyRange = IDBKeyRange . only ( attributedTo )
@@ -272,23 +269,68 @@ export class ActivityPubDB extends EventTarget {
272269 }
273270
274271 const index = tx . store . index ( indexName )
275- let cursor = await index . openCursor ( keyRange )
276272
277- const notes = [ ]
278- while ( cursor ) {
279- notes . push ( cursor . value )
280- cursor = await cursor . continue ( )
281- }
273+ // For random sort
274+ if ( sort === 0 ) {
275+ const totalNotes = await index . count ( )
276+ let count = 0 // Add a count variable to keep track of successful yields
277+
278+ while ( count < limit ) { // Use while loop based on count to ensure we return the correct number of items
279+ const randomSkip = Math . floor ( Math . random ( ) * totalNotes )
280+ const cursor = await index . openCursor ( )
281+ if ( ! cursor ) break // Avoid null cursor cases
282+
283+ // Advance the cursor by randomSkip
284+ if ( randomSkip > 0 ) {
285+ await cursor . advance ( randomSkip )
286+ }
287+
288+ const note = cursor . value
289+
290+ // Apply filtering logic
291+ if ( ( ! excludeReplies || ! note . inReplyTo ) && ( ! timeline || ( note . timeline && note . timeline . includes ( timeline ) ) ) ) {
292+ yield note
293+ count ++ // Increment count only after a successful yield
294+ }
295+ }
296+ } else { // For regular sorting (newest/oldest)
297+ const direction = sort > 0 ? 'next' : 'prev'
298+ let cursor = await index . openCursor ( keyRange , direction )
299+
300+ let skipped = 0
301+ let count = 0
302+
303+ // Process cursor in a loop to keep the transaction active
304+ while ( cursor && count < limit ) {
305+ const note = cursor . value
282306
283- // Now sort notes by 'published' field
284- notes . sort ( ( a , b ) => ( sort > 0 ? 1 : - 1 ) * ( a . published - b . published ) )
307+ let includeNote = true
285308
286- // Apply skip and limit
287- const selectedNotes = notes . slice ( skip , skip + limit )
288- for ( const note of selectedNotes ) {
289- yield note
309+ // Filter by timeline
310+ if ( timeline && ( ! note . timeline || ! note . timeline . includes ( timeline ) ) ) {
311+ includeNote = false
312+ }
313+ // Exclude replies if required
314+ if ( excludeReplies && note . inReplyTo ) {
315+ includeNote = false
316+ }
317+
318+ // If the note matches the filter criteria, yield it
319+ if ( includeNote ) {
320+ if ( skipped < skip ) {
321+ skipped ++
322+ } else {
323+ yield note
324+ count ++
325+ }
326+ }
327+
328+ // Move to the next cursor
329+ cursor = await cursor . continue ( )
330+ }
290331 }
291332
333+ // Ensure the transaction completes
292334 await tx . done
293335 }
294336
@@ -300,12 +342,22 @@ export class ActivityPubDB extends EventTarget {
300342 // Add 'following' to timeline if the actor is followed
301343 const isFollowing = await this . isActorFollowed ( url )
302344 if ( isFollowing ) {
303- for await ( const note of this . searchNotes ( { attributedTo : actor . id } ) ) {
345+ const tx = this . db . transaction ( NOTES_STORE , 'readwrite' )
346+ const store = tx . objectStore ( NOTES_STORE )
347+ const index = store . index ( ATTRIBUTED_TO_FIELD )
348+ const keyRange = IDBKeyRange . only ( actor . id )
349+ let cursor = await index . openCursor ( keyRange )
350+
351+ while ( cursor ) {
352+ const note = cursor . value
304353 if ( ! note . timeline . includes ( TIMELINE_FOLLOWING ) ) {
305354 note . timeline . push ( TIMELINE_FOLLOWING )
306- await this . db . put ( NOTES_STORE , note )
355+ cursor . update ( note )
307356 }
357+ cursor = await cursor . continue ( )
308358 }
359+
360+ await tx . done
309361 }
310362
311363 // If actor has an 'outbox', ingest it as a collection
@@ -314,9 +366,6 @@ export class ActivityPubDB extends EventTarget {
314366 } else {
315367 console . error ( `No outbox found for actor at URL ${ url } ` )
316368 }
317-
318- // This is where we might add more features to our actor ingestion process.
319- // e.g., if (actor.followers) { ... }
320369 }
321370
322371 async ingestActivityCollection ( collectionOrUrl , actorId , isInitial = false ) {
0 commit comments