Lens Traversal
Lenses provide quick navigation the object graph, for example to navigate from an Author a:1 to all of its books, and all of its book’s reviews, you can write:
// Load an author as usualconst author = await em.load(Author, "a:1");// The `a.books.reviews` creates a lens/path to navigateconst reviews = await author.load(a => a.books.reviews);console.log(`Found ${reviews.length} reviews`);Behind the scenes, the above code executes exactly the same as using Joist’s populate hints to preload and then .get + .flatMap across preloaded relations:
// Load an author but with a populate hintconst author = await em.load( Author, "a:1", { books: "reviews" });// Now flatMap book reviews w/o any awaitsconst reviews = author.books.get.flatMap((book) => { return book.reviews.get;})console.log(`Found ${reviews.length} reviews`);Both of these features prevent await hell (by having only a single await and then otherwise synchronous code), and which one is better depends on your need:
- If you need to apply filters and transformation logic, the populate hint with explicit
.getsand.flatMap`s is better b/c you can intersperse your custom logic as needed. - If you just need to do a simple/no filtering/no transformation navigation of the object graph, then the lens
.loadapproach is more succint.
Explanation
Section titled “Explanation”In the above example, the author.load method passes its lambda the parameter a; this parameter is just a proxy/lens records/“marks” what path to take through the object graph.
Once the lambda returns that path (i.e. a.books.reviews or author -> books -> reviews), then the load method internally loads/follows those paths, and returns the collection of entities that was at the “end” of the path.
Typing
Section titled “Typing”In the above example, the a parameter is a Lens<Author>, where Lens is a mapped type that exposes Author’s relations as simple keys.
Those keys themselves return new Lenss, i.e. a.books returns Lens<Book, Book[]> (the 2nd Book[] is because books returns multiple Books).
Then .reviews returns Lens<BookReview, BookReview[]>, and since it is the “last path” / last lens, that is who the author.load method knows that its return type should be BookReview[].