It's Fuckin Recursive!

@param $qb
@param $assoc
@param $alias
@param $params
500595
protected function buildAssociationQuery($qb, $assoc, $alias, $params)
    {
        $data = $this->em->getClassMetadata($assoc);
        foreach ($params as $key => $val) {
            if (isset($data->associationMappings)) {
                if ($map = array_key_exists($key, $data->associationMappings)) {
                    if (is_array($val)) {
                        $qb->innerJoin("$alias.$key", "_$key");
                        foreach ($val as $column => $v) {
                            if (is_array($v)) {
                                $this->buildAssociationQuery($qb, $map['targetEntity'], $column, $v);
                                continue;
                            }
                            $paramname = "_$key" . '__' . $column;
                            $qb->andWhere("_$key.$column = :$paramname");
                            $qb->setParameter($paramname, $v);
                        }
                        continue;
                    }
                }
            }
            if ($val === null) {
                $qb->andWhere("s.$key IS NULL");
            } else {
                $paramname = str_replace(".", "", "s_$key");
                $qb->andWhere("s.$key = :$paramname");
                $qb->setParameter($paramname, $val);
            }
        }
    }
376364
public static function q_assoc($q): array
    {
        $res = [];
        $qh = self::getInstance()->sql_query($q);

        /** @noinspection PhpAssignmentInConditionInspection */
        /** @noinspection PhpStatementHasEmptyBodyInspection */
        while ($res[] = $qh->fetch(PDO::FETCH_ASSOC)) {
            // Do nothing
        }

        \array_pop($res);

        return $res;
    }
418184
public function applyExprFromArray($qb, $alias, array $criteria)
	{
		$pos = 0;
		
		foreach ($criteria as $field => $exprValue) {
			$this->applyExprFromString($qb, $alias, $field, $exprValue, ':'.$field.($pos++));
		}
	}
1025188
private function setWhereAllCallback(QueryBuilder $qb)
    {
        if (!empty($this->callbacks['WhereAll'])) {
            foreach ($this->callbacks['WhereAll'] as $callback) {
                $callback($qb);
            }
        }

        return $this;
    }
1307047
public function transform(QueryBuilder $qb, $dqlAlias)
    {
        if ($this->expression instanceof Filter) {
            $expression = $this->expression->getFilter($qb, $dqlAlias);
        } else {
            $expression = ArgumentToOperandConverter::toField($this->expression);
            $expression = $expression->transform($qb, $dqlAlias);
        }

        return sprintf($this->getAliasFormat(), $expression, $this->alias);
    }
104790
public static function findByMetadataQueryBuilder(
        QueryBuilder $qb,
        $join,
        array $metadata,
        $alias = 'metadata',
        $operator = 'eq',
        $whereOperator = 'and'
    ) {
        $qb->join($join, $alias);

        foreach ($metadata as $key => $data) {
            $andX = $qb->expr()->andX();

            foreach ($data as $field => $value) {
                $itemOperator = $operator;

                if (is_array($value)) {
                    $value = $value['value'];
                    $itemOperator = $value['operator'];
                }

                $parameterName = sprintf('%s_%s_%s', $alias, $field, $key);
                $andX->add(call_user_func_array(
                    array($qb->expr(), $itemOperator),
                    array(
                        sprintf('%s.%s', $alias, $field),
                        sprintf(':%s', $parameterName),
                    )
                ));

                $qb->setParameter($parameterName, $value);
            }

            call_user_func_array(array($qb, sprintf('%sWhere', $whereOperator)), array($andX));
        }

        return $qb;
    }
792090
public function applyMetrics($qb, &$metrics, $associationMappings, $classMetadata, $joins)
    {
        foreach ($metrics as $metricItemKey => $metricItem) {
            $metric = $metricItem['field'];
            $metric_name = str_replace('.', '___', $metric) . '___' . $metricItem['aggregation'];
            $metrics[$metricItemKey]['code_name'] = $metric_name;

            if (false !== mb_strpos($metric, '.')) {
                $metric_parts = explode('.', $metric);
                $metrics[$metricItemKey]['label_name'] = $this->camelCaseToUnderscore($metric_parts[0]);

                /** @var array $associationMapping */
                $associationMapping = $associationMappings[$metric_parts[0]];

                $associationTargetClass = $classMetadata->getAssociationTargetClass($metric_parts[0]);

                $on = '';

                /** @var array $sourceToTargetKeyColumns */
                $sourceToTargetKeyColumns = $associationMapping['sourceToTargetKeyColumns'];

                foreach ($sourceToTargetKeyColumns as $source => $target) {
                    $on = 'entity.' . $source . ' = ' . $metric_parts[0] . '_join';
                }

                if (!\in_array($associationTargetClass, $joins)) {
                    $joins[] = $associationTargetClass;
                    $qb->leftJoin($associationTargetClass, $metric_parts[0] . '_join', 'WITH', $on);
                }

                $qb->addSelect($metricItem['aggregation'] . '(' . $metric . ') as ' . $metric_name);
            } else {
                $metrics[$metricItemKey]['label_name'] = $this->camelCaseToUnderscore($metric);

                $qb->addSelect($metricItem['aggregation'] . '(' . 'entity.' . $metric . ') as ' . $metric_name);
            }
        }

        return $qb;
    }
319677
public function orderByMultiple($order = array('_leftnode' => 'asc'), $alias = null)
    {
        if (true === empty($order)) {
            $order = array('_leftnode' => 'asc');
        }

        $this->resetDQLPart('orderBy');
        $alias = $this->getFirstAlias($alias);
        foreach ($order as $field => $sort) {
            if (0 < count($this->getDQLPart('orderBy'))) {
                $this->addOrderBy($alias.'.'.$field, $sort);
            } else {
                $this->orderBy($alias.'.'.$field, $sort);
            }
        }

        return $this;
    }
950651
private function filterByLocation(QueryBuilder $qb,
        ?Location $location = null, ?string $locationType = null,
        array $excludedLocations = [])
    {
        if (null == $locationType && null == $location) {
            return $qb;
        }

        $alias = $qb->getAllAliases()[0];
        $qb
            ->leftJoin($alias . '.location', 'l');

        if (null !== $locationType) {
            $qb
                ->andWhere('l.typeValue = :locationType')
                ->setParameter('locationType', $locationType);
        }
        if (null !== $location) {
            $qb
                ->andWhere('l.treeLft >= :treeLeft')
                ->andWhere('l.treeRgt <= :treeRight')
                ->orderBy('l.treeLvl', 'DESC')
                ->setParameter('treeLeft', $location->getTreeLft())
                ->setParameter('treeRight', $location->getTreeRgt());
        }
        if (count($excludedLocations)) {
            $qb
                ->andWhere('l NOT IN (:locations)')
                ->setParameter('locations', $excludedLocations);
        }

        return $qb;
    }
971424
public function cardinality($alias, $field = null, $script = null, $precision = null, $rehash = null)
    {
        $aggregation = new CardinalityAggregation($alias);

        $aggregation->setField($field);

        $aggregation->setScript($script);

        $aggregation->setPrecisionThreshold($precision);

        $aggregation->setRehash($rehash);

        $this->append($aggregation);
    }
856572
protected function applyWhere(QueryBuilder $qb, array $where): void
    {
        $whereType = $this->getContext()->getDefinition()->getArgument('where')->getType();

        /** @var InputObjectDefinition $whereDefinition */
        $whereDefinition = $this->getContext()->getEndpoint()->getType($whereType);

        /** @var ObjectDefinition $node */
        $node = $this->getContext()->getEndpoint()->getType($this->getContext()->getDefinition()->getNode());

        foreach ($where as $filterName => $condition) {
            $filterDefinition = $whereDefinition->getField($filterName);

            //TODO: load filters from services
            /** @var FilterInterface $filter */
            $filter = (new \ReflectionClass($filterDefinition->getResolver()))->newInstanceWithoutConstructor();

            $fieldName = $filterDefinition->getMeta('filter_field');
            if ($fieldName && $node->hasField($fieldName)) {
                $relatedField = $node->getField($fieldName);
                $filterContext = new FilterContext($this->getContext()->getEndpoint(), $node, $relatedField);
            } else {
                $filterContext = new FilterContext($this->getContext()->getEndpoint(), $node);
            }

            $filter($filterContext, $qb, $condition);
        }
    }
826249
public function alias($alias, $name) {
		if (is_array($alias)) {
			foreach ($alias as $key) {
				$this->alias($key, $name);
			}

			return;
		}

		$this->aliases[$alias] = $name;
	}
460249
public function setupQb(QueryBuilder $qb, $filters)
    {
        $this->qb = $qb;
        $this->c = 0;
        $this->bind = array();

        $filters = $this->_unify($filters);

        $this->_setupOrderyBy($qb, $filters['orderby']);

        $this->_setFilters($qb, $filters['filters']);

        $filters['offset'] and $qb->setFirstResult($filters['offset']);

        $filters['limit'] and $qb->setMaxResults($filters['limit']);

        return $qb;
    }
1053660
public function count($alias = null)
    {
        // prepare new query
        $q = $this->dsql()->del('fields')->del('order');

        // add expression field to query
        return $q->field($q->count(), $alias);
    }
456215
public static function create(SchemaQName $qname, string $alias, string $value): self
    {
        return new self($qname, $alias, $value);
    }
686395
public function applyToQueryBuilder(QueryBuilder $qb, &$parameterOffset = 0)
	{
		$orderRules = $this->orderRules;
		foreach ($orderRules as $orderRule) {
			$field = $orderRule[self::FIELD_POS];
			switch ($field) {
				case self::LEFT_FIELD:
				case self::RIGHT_FIELD:
				case self::LEVEL_FIELD:
					break;
				default:
					throw new Exception\InvalidArgument("Field $field is not recognized");
			}

			$field = "e.$field";
			$directionSQL = null;;

			$direction = $orderRule[self::DIRECTION_POS];
			switch ($direction) {
				case self::DIRECTION_ASCENDING:
					$directionSQL = 'ASC';
					break;
				case self::DIRECTION_DESCENDING:
					$directionSQL = 'DESC';
					break;
				default:
					throw new Exception\InvalidArgument("Direction $direction is not recognized");
			}
			$qb->addOrderBy($field, $directionSQL);
		}
	}
773465
function ( query, associations ) {
		_.each( associations, function ( assoc ) {
			// if the associations is to be populated with the full records...
			if ( assoc.include === "record" ) query.populate( assoc.alias );
		} );
		return query;
	}