loadCollections<C, P> method

Future<Map<EntityEntry<P>, List<C>>> loadCollections<C, P>(
  1. ForeignKey<C, P> relation,
  2. Iterable<EntityEntry<P>> parents
)

Loads children for parents in batches.

Implementation

Future<Map<EntityEntry<P>, List<C>>> loadCollections<C, P>(
    ForeignKey<C, P> relation, Iterable<EntityEntry<P>> parents) {
  final entries = List<EntityEntry<P>>.of(parents);
  _checkRegisteredRelationship(this, relation);
  for (final entry in entries) {
    _checkRelationshipEntry(entry, relation.parent);
    if (!identical(entry.context, this)) {
      throw const OrmStateException(
          'Relationship entries belong to different contexts');
    }
  }
  return _serialize(() async {
    final result = Map<EntityEntry<P>, List<C>>.identity();
    final wanted = <EntityKey, EntityEntry<P>>{};
    for (final entry in entries) {
      _checkRelationshipEntry(entry, relation.parent);
      result[entry] = [];
      final key =
          _relationshipEntityKey(relation.targetColumns, entry.entity);
      if (key == null) continue;
      final previous = wanted[key];
      if (previous != null && !identical(previous, entry)) {
        throw const OrmStateException(
            'Tracked parents have ambiguous relationship target values');
      }
      wanted[key] = entry;
    }
    for (final chunk
        in _relationshipChunks(wanted.keys, relation.columns.length)) {
      final predicate = _relationshipPredicate(relation.columns, chunk);
      final rows = await connection.query(
          'SELECT ${_relationshipProjection(relation.child)} '
          'FROM ${quoteIdentifier(relation.child.tableName)} '
          'WHERE ${predicate.$1}',
          predicate.$2);
      for (final values in rows.rows) {
        _materialize(
            relation.child, _relationshipReader(rows.columns, values));
      }
    }
    // Current tracked FK values and explicit links take precedence over old
    // store values. This also includes newly linked, unsaved children.
    for (final candidate in _entries.values.toList(growable: false)) {
      if (!identical(candidate.type, relation.child) ||
          candidate.state == EntityState.deleted ||
          candidate.state == EntityState.detached) {
        continue;
      }
      final child = candidate as EntityEntry<C>;
      EntityEntry<P>? parent;
      final links = _relationshipLinks[child];
      if (links != null && links.containsKey(relation)) {
        _validateRelationshipLinks(child);
        parent = links[relation] as EntityEntry<P>?;
      } else {
        final key = _relationshipEntityKey(relation.columns, child.entity);
        if (key != null) parent = wanted[key];
      }
      if (parent != null && result.containsKey(parent)) {
        result[parent]!.add(child.entity);
      }
    }
    return Map<EntityEntry<P>, List<C>>.unmodifiable({
      for (final entry in result.entries)
        entry.key: List<C>.unmodifiable(entry.value)
    });
  });
}