* 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({ po
(callback: WhereCallback<TContext>)
| 502 | * ``` |
| 503 | */ |
| 504 | having(callback: WhereCallback<TContext>): QueryBuilder<TContext> { |
| 505 | const aliases = this._getCurrentAliases() |
| 506 | // Add $selected namespace if SELECT clause exists (either regular or functional) |
| 507 | const refProxy = ( |
| 508 | this.query.select || this.query.fnSelect |
| 509 | ? createRefProxyWithSelected(aliases) |
| 510 | : createRefProxy(aliases) |
| 511 | ) as RefsForContext<TContext> |
| 512 | const rawExpression = callback(refProxy) |
| 513 | |
| 514 | // Allow bare boolean column references like `.having(({ $selected }) => $selected.isActive)` |
| 515 | // by converting ref proxies to PropRef expressions, the same way helper |
| 516 | // functions like `not()` and `eq()` do via `toExpression()`. |
| 517 | const expression = isRefProxy(rawExpression) |
| 518 | ? toExpression(rawExpression) |
| 519 | : rawExpression |
| 520 | |
| 521 | // Validate that the callback returned a valid expression |
| 522 | // This catches common mistakes like using JavaScript comparison operators (===, !==, etc.) |
| 523 | // which return boolean primitives instead of expression objects |
| 524 | if (!isExpressionLike(expression)) { |
| 525 | throw new InvalidWhereExpressionError(getValueTypeName(expression)) |
| 526 | } |
| 527 | |
| 528 | const existingHaving = this.query.having || [] |
| 529 | |
| 530 | return new BaseQueryBuilder({ |
| 531 | ...this.query, |
| 532 | having: [...existingHaving, expression], |
| 533 | }) as any |
| 534 | } |
| 535 | |
| 536 | /** |
| 537 | * Select specific columns or computed values from the query |