"use strict";
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
const index = require("../../collection/index.cjs");
const ir = require("../ir.cjs");
const errors = require("../../errors.cjs");
const refProxy = require("./ref-proxy.cjs");
const functions = require("./functions.cjs");
class BaseQueryBuilder {
  constructor(query = {}) {
    this.query = {};
    this.query = { ...query };
  }
  /**
   * Creates a CollectionRef or QueryRef from a source object
   * @param source - An object with a single key-value pair
   * @param context - Context string for error messages (e.g., "from clause", "join clause")
   * @returns A tuple of [alias, ref] where alias is the source key and ref is the created reference
   */
  _createRefForSource(source, context) {
    let keys;
    try {
      keys = Object.keys(source);
    } catch {
      const type = source === null ? `null` : `undefined`;
      throw new errors.InvalidSourceTypeError(context, type);
    }
    if (Array.isArray(source)) {
      throw new errors.InvalidSourceTypeError(context, `array`);
    }
    if (keys.length !== 1) {
      if (keys.length === 0) {
        throw new errors.InvalidSourceTypeError(context, `empty object`);
      }
      if (keys.every((k) => !isNaN(Number(k)))) {
        throw new errors.InvalidSourceTypeError(context, `string`);
      }
      throw new errors.OnlyOneSourceAllowedError(context);
    }
    const alias = keys[0];
    const sourceValue = source[alias];
    let ref;
    if (sourceValue instanceof index.CollectionImpl) {
      ref = new ir.CollectionRef(sourceValue, alias);
    } else if (sourceValue instanceof BaseQueryBuilder) {
      const subQuery = sourceValue._getQuery();
      if (!subQuery.from) {
        throw new errors.SubQueryMustHaveFromClauseError(context);
      }
      ref = new ir.QueryRef(subQuery, alias);
    } else {
      throw new errors.InvalidSourceError(alias);
    }
    return [alias, ref];
  }
  /**
   * Specify the source table or subquery for the query
   *
   * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery
   * @returns A QueryBuilder with the specified source
   *
   * @example
   * ```ts
   * // Query from a collection
   * query.from({ users: usersCollection })
   *
   * // Query from a subquery
   * const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)
   * query.from({ activeUsers })
   * ```
   */
  from(source) {
    const [, from] = this._createRefForSource(source, `from clause`);
    return new BaseQueryBuilder({
      ...this.query,
      from
    });
  }
  /**
   * Join another table or subquery to the current query
   *
   * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery
   * @param onCallback - A function that receives table references and returns the join condition
   * @param type - The type of join: 'inner', 'left', 'right', or 'full' (defaults to 'left')
   * @returns A QueryBuilder with the joined table available
   *
   * @example
   * ```ts
   * // Left join users with posts
   * query
   *   .from({ users: usersCollection })
   *   .join({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))
   *
   * // Inner join with explicit type
   * query
   *   .from({ u: usersCollection })
   *   .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId), 'inner')
   * ```
   *
   * // Join with a subquery
   * const activeUsers = query.from({ u: usersCollection }).where(({u}) => u.active)
   * query
   *   .from({ activeUsers })
   *   .join({ p: postsCollection }, ({u, p}) => eq(u.id, p.userId))
   */
  join(source, onCallback, type = `left`) {
    const [alias, from] = this._createRefForSource(source, `join clause`);
    const currentAliases = this._getCurrentAliases();
    const newAliases = [...currentAliases, alias];
    const refProxy$1 = refProxy.createRefProxy(newAliases);
    const onExpression = onCallback(refProxy$1);
    let left;
    let right;
    if (onExpression.type === `func` && onExpression.name === `eq` && onExpression.args.length === 2) {
      left = onExpression.args[0];
      right = onExpression.args[1];
    } else {
      throw new errors.JoinConditionMustBeEqualityError();
    }
    const joinClause = {
      from,
      type,
      left,
      right
    };
    const existingJoins = this.query.join || [];
    return new BaseQueryBuilder({
      ...this.query,
      join: [...existingJoins, joinClause]
    });
  }
  /**
   * Perform a LEFT JOIN with another table or subquery
   *
   * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery
   * @param onCallback - A function that receives table references and returns the join condition
   * @returns A QueryBuilder with the left joined table available
   *
   * @example
   * ```ts
   * // Left join users with posts
   * query
   *   .from({ users: usersCollection })
   *   .leftJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))
   * ```
   */
  leftJoin(source, onCallback) {
    return this.join(source, onCallback, `left`);
  }
  /**
   * Perform a RIGHT JOIN with another table or subquery
   *
   * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery
   * @param onCallback - A function that receives table references and returns the join condition
   * @returns A QueryBuilder with the right joined table available
   *
   * @example
   * ```ts
   * // Right join users with posts
   * query
   *   .from({ users: usersCollection })
   *   .rightJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))
   * ```
   */
  rightJoin(source, onCallback) {
    return this.join(source, onCallback, `right`);
  }
  /**
   * Perform an INNER JOIN with another table or subquery
   *
   * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery
   * @param onCallback - A function that receives table references and returns the join condition
   * @returns A QueryBuilder with the inner joined table available
   *
   * @example
   * ```ts
   * // Inner join users with posts
   * query
   *   .from({ users: usersCollection })
   *   .innerJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))
   * ```
   */
  innerJoin(source, onCallback) {
    return this.join(source, onCallback, `inner`);
  }
  /**
   * Perform a FULL JOIN with another table or subquery
   *
   * @param source - An object with a single key-value pair where the key is the table alias and the value is a Collection or subquery
   * @param onCallback - A function that receives table references and returns the join condition
   * @returns A QueryBuilder with the full joined table available
   *
   * @example
   * ```ts
   * // Full join users with posts
   * query
   *   .from({ users: usersCollection })
   *   .fullJoin({ posts: postsCollection }, ({users, posts}) => eq(users.id, posts.userId))
   * ```
   */
  fullJoin(source, onCallback) {
    return this.join(source, onCallback, `full`);
  }
  /**
   * Filter rows based on a condition
   *
   * @param callback - A function that receives table references and returns an expression
   * @returns A QueryBuilder with the where condition applied
   *
   * @example
   * ```ts
   * // Simple condition
   * query
   *   .from({ users: usersCollection })
   *   .where(({users}) => gt(users.age, 18))
   *
   * // Multiple conditions
   * query
   *   .from({ users: usersCollection })
   *   .where(({users}) => and(
   *     gt(users.age, 18),
   *     eq(users.active, true)
   *   ))
   *
   * // Multiple where calls are ANDed together
   * query
   *   .from({ users: usersCollection })
   *   .where(({users}) => gt(users.age, 18))
   *   .where(({users}) => eq(users.active, true))
   * ```
   */
  where(callback) {
    const aliases = this._getCurrentAliases();
    const refProxy$1 = refProxy.createRefProxy(aliases);
    const rawExpression = callback(refProxy$1);
    const expression = refProxy.isRefProxy(rawExpression) ? refProxy.toExpression(rawExpression) : rawExpression;
    if (!ir.isExpressionLike(expression)) {
      throw new errors.InvalidWhereExpressionError(getValueTypeName(expression));
    }
    const existingWhere = this.query.where || [];
    return new BaseQueryBuilder({
      ...this.query,
      where: [...existingWhere, expression]
    });
  }
  /**
   * Filter grouped rows based on aggregate conditions
   *
   * @param callback - A function that receives table references and returns an expression
   * @returns A QueryBuilder with the having condition applied
   *
   * @example
   * ```ts
   * // Filter groups by count
   * query
   *   .from({ posts: postsCollection })
   *   .groupBy(({posts}) => posts.userId)
   *   .having(({posts}) => gt(count(posts.id), 5))
   *
   * // Filter by average
   * query
   *   .from({ orders: ordersCollection })
   *   .groupBy(({orders}) => orders.customerId)
   *   .having(({orders}) => gt(avg(orders.total), 100))
   *
   * // Multiple having calls are ANDed together
   * query
   *   .from({ orders: ordersCollection })
   *   .groupBy(({orders}) => orders.customerId)
   *   .having(({orders}) => gt(count(orders.id), 5))
   *   .having(({orders}) => gt(avg(orders.total), 100))
   * ```
   */
  having(callback) {
    const aliases = this._getCurrentAliases();
    const refProxy$1 = this.query.select || this.query.fnSelect ? refProxy.createRefProxyWithSelected(aliases) : refProxy.createRefProxy(aliases);
    const rawExpression = callback(refProxy$1);
    const expression = refProxy.isRefProxy(rawExpression) ? refProxy.toExpression(rawExpression) : rawExpression;
    if (!ir.isExpressionLike(expression)) {
      throw new errors.InvalidWhereExpressionError(getValueTypeName(expression));
    }
    const existingHaving = this.query.having || [];
    return new BaseQueryBuilder({
      ...this.query,
      having: [...existingHaving, expression]
    });
  }
  select(callback) {
    const aliases = this._getCurrentAliases();
    const refProxy$1 = refProxy.createRefProxy(aliases);
    let selectObject = callback(refProxy$1);
    if (refProxy.isRefProxy(selectObject) && selectObject.__path.length === 1) {
      const sentinelKey = `__SPREAD_SENTINEL__${selectObject.__path[0]}__0`;
      selectObject = { [sentinelKey]: true };
    }
    const select = buildNestedSelect(selectObject, aliases);
    return new BaseQueryBuilder({
      ...this.query,
      select,
      fnSelect: void 0
      // remove the fnSelect clause if it exists
    });
  }
  /**
   * Sort the query results by one or more columns
   *
   * @param callback - A function that receives table references and returns the field to sort by
   * @param direction - Sort direction: 'asc' for ascending, 'desc' for descending (defaults to 'asc')
   * @returns A QueryBuilder with the ordering applied
   *
   * @example
   * ```ts
   * // Sort by a single column
   * query
   *   .from({ users: usersCollection })
   *   .orderBy(({users}) => users.name)
   *
   * // Sort descending
   * query
   *   .from({ users: usersCollection })
   *   .orderBy(({users}) => users.createdAt, 'desc')
   *
   * // Multiple sorts (chain orderBy calls)
   * query
   *   .from({ users: usersCollection })
   *   .orderBy(({users}) => users.lastName)
   *   .orderBy(({users}) => users.firstName)
   * ```
   */
  orderBy(callback, options = `asc`) {
    const aliases = this._getCurrentAliases();
    const refProxy$1 = this.query.select || this.query.fnSelect ? refProxy.createRefProxyWithSelected(aliases) : refProxy.createRefProxy(aliases);
    const result = callback(refProxy$1);
    const opts = typeof options === `string` ? { direction: options, nulls: `first` } : {
      direction: options.direction ?? `asc`,
      nulls: options.nulls ?? `first`,
      stringSort: options.stringSort,
      locale: options.stringSort === `locale` ? options.locale : void 0,
      localeOptions: options.stringSort === `locale` ? options.localeOptions : void 0
    };
    const makeOrderByClause = (res) => {
      return {
        expression: refProxy.toExpression(res),
        compareOptions: opts
      };
    };
    const orderByClauses = Array.isArray(result) ? result.map((r) => makeOrderByClause(r)) : [makeOrderByClause(result)];
    const existingOrderBy = this.query.orderBy || [];
    return new BaseQueryBuilder({
      ...this.query,
      orderBy: [...existingOrderBy, ...orderByClauses]
    });
  }
  /**
   * Group rows by one or more columns for aggregation
   *
   * @param callback - A function that receives table references and returns the field(s) to group by
   * @returns A QueryBuilder with grouping applied (enables aggregate functions in SELECT and HAVING)
   *
   * @example
   * ```ts
   * // Group by a single column
   * query
   *   .from({ posts: postsCollection })
   *   .groupBy(({posts}) => posts.userId)
   *   .select(({posts, count}) => ({
   *     userId: posts.userId,
   *     postCount: count()
   *   }))
   *
   * // Group by multiple columns
   * query
   *   .from({ sales: salesCollection })
   *   .groupBy(({sales}) => [sales.region, sales.category])
   *   .select(({sales, sum}) => ({
   *     region: sales.region,
   *     category: sales.category,
   *     totalSales: sum(sales.amount)
   *   }))
   * ```
   */
  groupBy(callback) {
    const aliases = this._getCurrentAliases();
    const refProxy$1 = refProxy.createRefProxy(aliases);
    const result = callback(refProxy$1);
    const newExpressions = Array.isArray(result) ? result.map((r) => refProxy.toExpression(r)) : [refProxy.toExpression(result)];
    const existingGroupBy = this.query.groupBy || [];
    return new BaseQueryBuilder({
      ...this.query,
      groupBy: [...existingGroupBy, ...newExpressions]
    });
  }
  /**
   * Limit the number of rows returned by the query
   * `orderBy` is required for `limit`
   *
   * @param count - Maximum number of rows to return
   * @returns A QueryBuilder with the limit applied
   *
   * @example
   * ```ts
   * // Get top 5 posts by likes
   * query
   *   .from({ posts: postsCollection })
   *   .orderBy(({posts}) => posts.likes, 'desc')
   *   .limit(5)
   * ```
   */
  limit(count) {
    return new BaseQueryBuilder({
      ...this.query,
      limit: count
    });
  }
  /**
   * Skip a number of rows before returning results
   * `orderBy` is required for `offset`
   *
   * @param count - Number of rows to skip
   * @returns A QueryBuilder with the offset applied
   *
   * @example
   * ```ts
   * // Get second page of results
   * query
   *   .from({ posts: postsCollection })
   *   .orderBy(({posts}) => posts.createdAt, 'desc')
   *   .offset(page * pageSize)
   *   .limit(pageSize)
   * ```
   */
  offset(count) {
    return new BaseQueryBuilder({
      ...this.query,
      offset: count
    });
  }
  /**
   * Specify that the query should return distinct rows.
   * Deduplicates rows based on the selected columns.
   * @returns A QueryBuilder with distinct enabled
   *
   * @example
   * ```ts
   * // Get countries our users are from
   * query
   *   .from({ users: usersCollection })
   *   .select(({users}) => ({ country: users.country }))
   *   .distinct()
   * ```
   */
  distinct() {
    return new BaseQueryBuilder({
      ...this.query,
      distinct: true
    });
  }
  /**
   * Specify that the query should return a single result
   * @returns A QueryBuilder that returns the first result
   *
   * @example
   * ```ts
   * // Get the user matching the query
   * query
   *   .from({ users: usersCollection })
   *   .where(({users}) => eq(users.id, 1))
   *   .findOne()
   *```
   */
  findOne() {
    return new BaseQueryBuilder({
      ...this.query,
      // TODO: enforcing return only one result with also a default orderBy if none is specified
      // limit: 1,
      singleResult: true
    });
  }
  // Helper methods
  _getCurrentAliases() {
    const aliases = [];
    if (this.query.from) {
      aliases.push(this.query.from.alias);
    }
    if (this.query.join) {
      for (const join of this.query.join) {
        aliases.push(join.from.alias);
      }
    }
    return aliases;
  }
  /**
   * Functional variants of the query builder
   * These are imperative function that are called for ery row.
   * Warning: that these cannot be optimized by the query compiler, and may prevent
   * some type of optimizations being possible.
   * @example
   * ```ts
   * q.fn.select((row) => ({
   *   name: row.user.name.toUpperCase(),
   *   age: row.user.age + 1,
   * }))
   * ```
   */
  get fn() {
    const builder = this;
    return {
      /**
       * Select fields using a function that operates on each row
       * Warning: This cannot be optimized by the query compiler
       *
       * @param callback - A function that receives a row and returns the selected value
       * @returns A QueryBuilder with functional selection applied
       *
       * @example
       * ```ts
       * // Functional select (not optimized)
       * query
       *   .from({ users: usersCollection })
       *   .fn.select(row => ({
       *     name: row.users.name.toUpperCase(),
       *     age: row.users.age + 1,
       *   }))
       * ```
       */
      select(callback) {
        return new BaseQueryBuilder({
          ...builder.query,
          select: void 0,
          // remove the select clause if it exists
          fnSelect: callback
        });
      },
      /**
       * Filter rows using a function that operates on each row
       * Warning: This cannot be optimized by the query compiler
       *
       * @param callback - A function that receives a row and returns a boolean
       * @returns A QueryBuilder with functional filtering applied
       *
       * @example
       * ```ts
       * // Functional where (not optimized)
       * query
       *   .from({ users: usersCollection })
       *   .fn.where(row => row.users.name.startsWith('A'))
       * ```
       */
      where(callback) {
        return new BaseQueryBuilder({
          ...builder.query,
          fnWhere: [
            ...builder.query.fnWhere || [],
            callback
          ]
        });
      },
      /**
       * Filter grouped rows using a function that operates on each aggregated row
       * Warning: This cannot be optimized by the query compiler
       *
       * @param callback - A function that receives an aggregated row (with $selected when select() was called) and returns a boolean
       * @returns A QueryBuilder with functional having filter applied
       *
       * @example
       * ```ts
       * // Functional having (not optimized)
       * query
       *   .from({ posts: postsCollection })
       *   .groupBy(({posts}) => posts.userId)
       *   .select(({posts}) => ({ userId: posts.userId, count: count(posts.id) }))
       *   .fn.having(({ $selected }) => $selected.count > 5)
       * ```
       */
      having(callback) {
        return new BaseQueryBuilder({
          ...builder.query,
          fnHaving: [
            ...builder.query.fnHaving || [],
            callback
          ]
        });
      }
    };
  }
  _getQuery() {
    if (!this.query.from) {
      throw new errors.QueryMustHaveFromClauseError();
    }
    return this.query;
  }
}
function getValueTypeName(value) {
  if (value === null) return `null`;
  if (value === void 0) return `undefined`;
  if (typeof value === `object`) return `object`;
  return typeof value;
}
function toExpr(value) {
  if (value === void 0) return refProxy.toExpression(null);
  if (value instanceof ir.Aggregate || value instanceof ir.Func || value instanceof ir.PropRef || value instanceof ir.Value) {
    return value;
  }
  return refProxy.toExpression(value);
}
function isPlainObject(value) {
  return value !== null && typeof value === `object` && !ir.isExpressionLike(value) && !value.__refProxy;
}
function buildNestedSelect(obj, parentAliases = []) {
  if (!isPlainObject(obj)) return toExpr(obj);
  const out = {};
  for (const [k, v] of Object.entries(obj)) {
    if (typeof k === `string` && k.startsWith(`__SPREAD_SENTINEL__`)) {
      out[k] = v;
      continue;
    }
    if (v instanceof BaseQueryBuilder) {
      out[k] = buildIncludesSubquery(v, k, parentAliases, `collection`);
      continue;
    }
    if (v instanceof functions.ToArrayWrapper) {
      if (!(v.query instanceof BaseQueryBuilder)) {
        throw new Error(`toArray() must wrap a subquery builder`);
      }
      out[k] = buildIncludesSubquery(v.query, k, parentAliases, `array`);
      continue;
    }
    if (v instanceof functions.ConcatToArrayWrapper) {
      if (!(v.query instanceof BaseQueryBuilder)) {
        throw new Error(`concat(toArray(...)) must wrap a subquery builder`);
      }
      out[k] = buildIncludesSubquery(v.query, k, parentAliases, `concat`);
      continue;
    }
    out[k] = buildNestedSelect(v, parentAliases);
  }
  return out;
}
function collectRefsFromExpression(expr) {
  const refs = [];
  switch (expr.type) {
    case `ref`:
      refs.push(expr);
      break;
    case `func`:
      for (const arg of expr.args ?? []) {
        refs.push(...collectRefsFromExpression(arg));
      }
      break;
  }
  return refs;
}
function referencesParent(where, parentAliases) {
  const expr = typeof where === `object` && `expression` in where ? where.expression : where;
  return collectRefsFromExpression(expr).some(
    (ref) => ref.path[0] != null && parentAliases.includes(ref.path[0])
  );
}
function buildIncludesSubquery(childBuilder, fieldName, parentAliases, materialization) {
  const childQuery = childBuilder._getQuery();
  const childAliases = [childQuery.from.alias];
  if (childQuery.join) {
    for (const j of childQuery.join) {
      childAliases.push(j.from.alias);
    }
  }
  let parentRef;
  let childRef;
  let correlationWhereIndex = -1;
  let correlationAndArgIndex = -1;
  if (childQuery.where) {
    for (let i = 0; i < childQuery.where.length; i++) {
      const where = childQuery.where[i];
      const expr = typeof where === `object` && `expression` in where ? where.expression : where;
      if (expr.type === `func` && expr.name === `eq` && expr.args.length === 2) {
        const result = extractCorrelation(
          expr.args[0],
          expr.args[1],
          parentAliases,
          childAliases
        );
        if (result) {
          parentRef = result.parentRef;
          childRef = result.childRef;
          correlationWhereIndex = i;
          break;
        }
      }
      if (expr.type === `func` && expr.name === `and` && expr.args.length >= 2) {
        for (let j = 0; j < expr.args.length; j++) {
          const arg = expr.args[j];
          if (arg.type === `func` && arg.name === `eq` && arg.args.length === 2) {
            const result = extractCorrelation(
              arg.args[0],
              arg.args[1],
              parentAliases,
              childAliases
            );
            if (result) {
              parentRef = result.parentRef;
              childRef = result.childRef;
              correlationWhereIndex = i;
              correlationAndArgIndex = j;
              break;
            }
          }
        }
        if (parentRef) break;
      }
    }
  }
  if (!parentRef || !childRef || correlationWhereIndex === -1) {
    throw new Error(
      `Includes subquery for "${fieldName}" must have a WHERE clause with an eq() condition that correlates a parent field with a child field. Example: .where(({child}) => eq(child.parentId, parent.id))`
    );
  }
  const modifiedWhere = [...childQuery.where];
  if (correlationAndArgIndex >= 0) {
    const where = modifiedWhere[correlationWhereIndex];
    const expr = typeof where === `object` && `expression` in where ? where.expression : where;
    const remainingArgs = expr.args.filter(
      (_, idx) => idx !== correlationAndArgIndex
    );
    if (remainingArgs.length === 1) {
      const isResidual = typeof where === `object` && `expression` in where && where.residual;
      modifiedWhere[correlationWhereIndex] = isResidual ? { expression: remainingArgs[0], residual: true } : remainingArgs[0];
    } else {
      const newAnd = new ir.Func(`and`, remainingArgs);
      const isResidual = typeof where === `object` && `expression` in where && where.residual;
      modifiedWhere[correlationWhereIndex] = isResidual ? { expression: newAnd, residual: true } : newAnd;
    }
  } else {
    modifiedWhere.splice(correlationWhereIndex, 1);
  }
  const pureChildWhere = [];
  const parentFilters = [];
  for (const w of modifiedWhere) {
    if (referencesParent(w, parentAliases)) {
      parentFilters.push(w);
    } else {
      pureChildWhere.push(w);
    }
  }
  let parentProjection;
  if (parentFilters.length > 0) {
    const seen = /* @__PURE__ */ new Set();
    parentProjection = [];
    for (const w of parentFilters) {
      const expr = typeof w === `object` && `expression` in w ? w.expression : w;
      for (const ref of collectRefsFromExpression(expr)) {
        if (ref.path[0] != null && parentAliases.includes(ref.path[0]) && !seen.has(ref.path.join(`.`))) {
          seen.add(ref.path.join(`.`));
          parentProjection.push(ref);
        }
      }
    }
  }
  const modifiedQuery = {
    ...childQuery,
    where: pureChildWhere.length > 0 ? pureChildWhere : void 0
  };
  const rawChildSelect = modifiedQuery.select;
  const hasObjectSelect = rawChildSelect === void 0 || isPlainObject(rawChildSelect);
  let includesQuery = modifiedQuery;
  let scalarField;
  if (materialization === `concat`) {
    if (rawChildSelect === void 0 || hasObjectSelect) {
      throw new Error(
        `concat(toArray(...)) for "${fieldName}" requires the subquery to select a scalar value`
      );
    }
  }
  if (!hasObjectSelect) {
    if (materialization === `collection`) {
      throw new Error(
        `Includes subquery for "${fieldName}" must select an object when materializing as a Collection`
      );
    }
    scalarField = ir.INCLUDES_SCALAR_FIELD;
    includesQuery = {
      ...modifiedQuery,
      select: {
        [scalarField]: rawChildSelect
      }
    };
  }
  return new ir.IncludesSubquery(
    includesQuery,
    parentRef,
    childRef,
    fieldName,
    parentFilters.length > 0 ? parentFilters : void 0,
    parentProjection,
    materialization,
    scalarField
  );
}
function extractCorrelation(argA, argB, parentAliases, childAliases) {
  if (argA.type === `ref` && argB.type === `ref`) {
    const aAlias = argA.path[0];
    const bAlias = argB.path[0];
    if (aAlias && bAlias && parentAliases.includes(aAlias) && childAliases.includes(bAlias)) {
      return { parentRef: argA, childRef: argB };
    }
    if (aAlias && bAlias && parentAliases.includes(bAlias) && childAliases.includes(aAlias)) {
      return { parentRef: argB, childRef: argA };
    }
  }
  return void 0;
}
function buildQuery(fn) {
  const result = fn(new BaseQueryBuilder());
  return getQueryIR(result);
}
function getQueryIR(builder) {
  return builder._getQuery();
}
const Query = BaseQueryBuilder;
exports.BaseQueryBuilder = BaseQueryBuilder;
exports.Query = Query;
exports.buildQuery = buildQuery;
exports.getQueryIR = getQueryIR;
//# sourceMappingURL=index.cjs.map
