Skip to main content

Large Collections

In Joist, large collections are one-to-many collections (like author.books) that would fundamentally load too much data (like a single author having 100k books), such that we want to prevent code from accidentally loading the collection by mistake.

Normally, joist-codegen automatically generates loadable one-to-many collections in your domain modal. For example, given a books.author_id foreign key, your code can immediately do:

const author = await em.load(Author, "a:1");
const books = await author.books.load();

Or use books in a load hint:

const author = await em.load(Author, "a:1", "books");
console.log(author.books.get);

Both of which will load/preload the full author.books collection into memory for easy access.

Usually this is great, unless we know when designing the schema that author.books.load() is fundamentally likely to pull in too much data and blow our up EntityManager's entity limit (which is 10,000 entities by default).

In this scenario, we can tell Joist to treat books as a large collection, by setting large: true in the joist-config.json:

{
"entities": {
"Author": {
"relations": {
"books": { "large": true }
}
}
}
}

Now, joist-codegen still generates an Author.books property, however it will be typed as a LargeCollection which:

  • Does not have a .load() method, and
  • Cannot be used in a load hint

Both of which prevent the collection from accidentally being fully loaded into memory, and prevents developers from having to "just know" not to load author.books while writing business logic.

Instead, the LargeCollection relation only supports a few known-safe methods that work without fully loading it into memory:

const author = await em.load(Author, "a:1");
const b1 = await em.load(Book, "b:1");

// Adding/removing the book
author.books.add(b1);
author.books.remove(b1);

// Probing if `b1` is in `author.books`
await author.books.includes(b1);

// Probing if `bookId` is in `author.books`
const b2 = await author.books.find(bookId);