import DataLoader from "dataloader" ; import { Entity , EntityManager } from "../EntityManager" ; import { getEm , keyToString , ManyToManyCollection } from "../index" ; import { JoinRow } from "../relations/ManyToManyCollection" ; import { getOrSet } from "../utils" ; export function manyToManyDataLoader < T extends Entity , U extends Entity > ( em , collection ) { return getOrSet ( em . loadLoaders , collection . joinTableName , ( ) => { return new DataLoader < string , Entity [ ] > ( ( keys ) => load ( collection , keys ) ) ; } ) ; } /**
 * Loads join rows (batched).
 *
 * I.e. we can load the `books_to_tags` join rows for multiple `Book`s at a time, or even
 * load `books_to_tags` for several `Book`s and several `Tag`s in a single SQL query.
 */ async function load < T extends Entity , U extends Entity > ( collection , keys ) { const { joinTableName } = collection ; const em = getEm ( collection . entity ) ; const rowsByKey = { } ; const emJoinRows = getOrSet ( em . __data . joinRows , joinTableName , [ ] ) ; const rows = await em . driver . loadManyToMany ( em , collection , keys ) ; const column1 = collection . columnName ; const meta1 = collection . entity . __orm . metadata ; const column2 = collection . otherColumnName ; const meta2 = collection . otherMeta ; await Promise . all ( rows . map ( async ( dbRow ) => { let emRow = emJoinRows . find ( ( jr ) => { return ( ( jr [ column1 ] as Entity ) . id === keyToString ( meta1 , dbRow [ column1 ] ) && ( jr [ column2 ] as Entity ) . id === keyToString ( meta2 , dbRow [ column2 ] ) ) ; } ) ; if ( ! emRow ) { const p1 = em . load ( meta1 . cstr , keyToString ( meta1 , dbRow [ column1 ] ) ! ) ; const p2 = em . load ( meta2 . cstr , keyToString ( meta2 , dbRow [ column2 ] ) ! ) ; const [ e1 , e2 ] = await Promise . all ( [ p1 , p2 ] ) ; emRow = { id : dbRow . id , m2m : collection , [ column1 ] : e1 , [ column2 ] : e2 , created_at : dbRow . created_at } ; emJoinRows . push ( emRow ) ; } else { emRow . id = dbRow . id ; } getOrSet ( rowsByKey , `${ column1 }=${ ( emRow [ column1 ] as Entity ) . id }` , [ ] ) . push ( emRow ) ; getOrSet ( rowsByKey , `${ column2 }=${ ( emRow [ column2 ] as Entity ) . id }` , [ ] ) . push ( emRow ) ; } ) ) ; return keys . map ( ( key ) => { const [ column ] = key . split ( "=" ) ; const joinRows = rowsByKey [ key ] || [ ] ; const otherColumn = column === collection . columnName ? collection . otherColumnName : collection . columnName ; return joinRows . map ( ( joinRow ) => joinRow [ otherColumn ] as Entity ) ; } ) ; }