elersong / fireorm24

ORM for Firebase Firestore 🔥 updated for 2024
MIT License
1 stars 0 forks source link

Support Eager Loading for Subcollections #36

Open elersong opened 3 months ago

elersong commented 3 months ago

Description

When querying a document that contains a subcollection, the subcollection is not automatically queried and must be queried manually. This enhancement proposes adding support for eager loading of subcollections to simplify querying nested data structures.

Steps to Reproduce

  1. Query a document that contains a subcollection.
  2. Observe that the subcollection is not automatically loaded and must be queried manually.

Expected Behavior

Subcollections should be automatically queried and loaded when querying the parent document, based on specified eager loading properties.

Actual Behavior

Subcollections are not automatically queried and must be manually loaded after querying the parent document.

Acceptance Criteria

Additional Context

Proposed API Changes

  1. Add Eager Loading Support:

    • Introduce an eagerLoad method to specify subcollections that should be eagerly loaded when querying the parent document.
    // Example of adding eager loading support to the query builder
    class QueryBuilder<T> {
     private eagerLoadProps: Array<IWherePropParam<T>> = [];
    
     eagerLoad(...props: Array<IWherePropParam<T>>): this {
       for (const prop of props) {
         if (isSubCollection(prop)) {
           this.eagerLoadProps.push(prop);
         }
       }
       return this;
     }
    
     // Other existing methods...
    }
  2. Modify Repository Execution:

    • Update the repository execution method to handle eager loading of subcollections.
    // Example of modifying the repository execution method
    class BaseFirestoreRepository<T> {
     async execute(
       queries: Array<IFireOrmQueryLine>,
       limitVal?: number,
       orderByObj?: IOrderByParams,
       single?: boolean,
       customQuery?: ICustomQuery<T>,
       eagerLoadedProps?: Array<IWherePropParam<T>>
     ): Promise<T[]> {
       let query = queries.reduce<Query>((acc, cur) => {
         const op = cur.operator as WhereFilterOp;
         return acc.where(cur.prop, op, cur.val);
       }, this.firestoreColRef);
    
       const executedQuery = query.get().then(this.extractTFromColSnap);
    
       if (eagerLoadedProps && eagerLoadedProps.length > 0) {
         const results = await executedQuery;
         await Promise.all(
           results.map(async result => {
             for (const prop of eagerLoadedProps) {
               const subCollection = result[prop as keyof T] as ISubCollection<any>;
               result[prop as keyof T] = await subCollection.get();
             }
           })
         );
         return results;
       }
    
       return executedQuery;
     }
    
     // Other existing methods...
    }
  3. Unit Tests:

    • Add unit tests to validate the eager loading functionality for subcollections.
    // Example unit test for eager loading subcollections
    test('should eagerly load subcollections', async () => {
     const bandRepository = getRepository(Band);
     const band = await bandRepository
       .whereGreaterThan(band => band.formationYear, 1985)
       .whereArrayContains(band => band.genres, 'progressive-rock')
       .eagerLoad(band => band.albums)
       .find();
    
     expect(band.albums).toBeDefined();
     expect(band.albums.length).toBeGreaterThan(0);
    });

Example Implementation

class Album {
  id: string;
  name: string;
  year: number;
}

@Collection()
class Band {
  id: string;
  name: string;
  formationYear: number;
  genres: Array<string>;

  @SubCollection(Album, 'albums')
  albums?: ISubCollection<Album>;
}

const bandRepository = getRepository(Band);

const band = await bandRepository
  .whereGreaterThan(band => band.formationYear, 1985)
  .whereArrayContains(band => band.genres, 'progressive-rock')
  .eagerLoad(band => band.albums)
  .find();

console.log(band.albums.map(album => album.name)); // Correctly prints the albums' names

Original Issue