• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rogerpadilla / uql / 30230650503

27 Jul 2026 01:47AM UTC coverage: 95.017% (-0.02%) from 95.039%
30230650503

push

github

rogerpadilla
feat(orm): $set/$pull JSON operators, typed comparisons, vector index guard

- Rename $merge to $set (shallow key assignment, matching MongoDB's own
  $set); add $pull to remove array elements; unify $push to always create
  a missing array (MariaDB previously nulled the whole column).
- Map $set/$unset/$push/$pull onto MongoDB's native update operators
  instead of writing the operator object into the document as literal
  data; falls back to an aggregation pipeline when a path is touched by
  more than one operator group, in the same $pull->$set->$push->$unset
  order the SQL dialects apply.
- Require `distance` on vector index types (hnsw/ivfflat/vector) and
  reject it on every other type - omitting/adding it silently changed
  generated DDL.
- Fix JSON dot-path and $elemMatch bugs: MySQL dot-paths never worked at
  runtime, MariaDB array operators emitted invalid SQL, SQLite $all never
  matched string elements, typed scalar comparisons (numbers/booleans)
  were inconsistent across engines, and $elemMatch's plain-equality form
  skipped the numeric cast and turned `null` into `= NULL`.
- Fix $includes rendering as ILIKE instead of LIKE on PostgreSQL/
  CockroachDB, and PostgreSQL full-text search failing on multi-word
  queries (to_tsquery -> websearch_to_tsquery).
- Simplify JSON dialect internals: unify jsonCast/jsonScalarParam, extract
  pure builders into dialect/jsonSql.ts, add a single jsonCompareMode
  classifier. Share the JSON-update test matrix and MySQL/MariaDB spec
  bodies across dialect specs. Unify vector types into type/vector.ts and
  close a type hole in IndexTypeOptions.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

3396 of 3751 branches covered (90.54%)

Branch coverage included in aggregate %.

208 of 214 new or added lines in 14 files covered. (97.2%)

5775 of 5901 relevant lines covered (97.86%)

721.91 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

94.12
/packages/uql-orm/src/dialect/vectorSqlDialect.ts
1
import { resolveVectorCast, type VectorCast } from '../schema/canonicalType.js';
2
import type {
3
  EntityMeta,
4
  FieldKey,
5
  FieldOptions,
6
  QueryContext,
7
  QueryVectorSearch,
8
  VectorDistance,
9
} from '../type/index.js';
10
import { AbstractDialect } from './abstractDialect.js';
11

12
/**
13
 * Vector similarity search for SQL dialects: the `ORDER BY <distance>` expression, its projection as
14
 * a named score, and the index metadata the schema generator reads.
15
 *
16
 * A layer of its own because it is nearly self-contained - it needs only `escapeId` from the SQL
17
 * dialect above it - unlike the JSON operators, which are woven into the generic comparison
18
 * machinery (`neExpr`, `numericCast`, `formatIn`, ...) and belong with it.
19
 *
20
 * Two shapes exist across dialects: an operator (`"col" <=> $1`, Postgres/CockroachDB) or a function
21
 * call (`VEC_DISTANCE_COSINE(col, ?)`, MariaDB/SQLite). Dialects pick one by either overriding
22
 * {@link appendVectorSort} or filling {@link vectorDistanceFns}.
23
 */
24
export abstract class VectorSqlDialect extends AbstractDialect {
25
  /** Vector index operator classes, keyed by distance metric. Partial: not every dialect supports every metric. */
26
  readonly vectorOpsClass: ReadonlyMap<VectorDistance, string> | undefined = undefined;
427✔
27

28
  readonly vectorExtension: string | undefined = undefined;
427✔
29

30
  /**
31
   * Mapping of UQL vector distance metrics to native SQL functions.
32
   * Override in dialects that use function-call syntax (e.g. SQLite, MariaDB).
33
   * Dialects with operator-based syntax (e.g. Postgres) leave this empty and override `appendVectorSort` directly.
34
   */
35
  protected readonly vectorDistanceFns: ReadonlyMap<VectorDistance, string> = new Map();
427✔
36

37
  /** Quotes an identifier; supplied by the SQL dialect built on top of this layer. */
38
  abstract escapeId(val: string, forbidQualified?: boolean, addDot?: boolean): string;
39

40
  /**
41
   * Resolve common parameters for a vector similarity ORDER BY expression.
42
   * Shared by all dialect overrides of `appendVectorSort`.
43
   */
44
  protected resolveVectorSortParams<E>(
45
    meta: EntityMeta<E>,
46
    key: string,
47
    search: QueryVectorSearch,
48
  ): { colName: string; distance: VectorDistance; field: FieldOptions | undefined; vectorCast: VectorCast } {
49
    const field = meta.fields[key as FieldKey<E>];
44✔
50
    const colName = this.resolveColumnName(key, field);
44✔
51
    const distance = search.$distance ?? field?.distance ?? 'cosine';
44✔
52
    const vectorCast = resolveVectorCast(field);
44✔
53
    return { colName, distance, field, vectorCast };
44✔
54
  }
55

56
  /**
57
   * Append a vector similarity function call: `fn(col, ?)`.
58
   * Used by dialects that express vector distance via SQL functions (SQLite, MariaDB).
59
   */
60
  protected appendFunctionVectorSort<E>(
61
    ctx: QueryContext,
62
    meta: EntityMeta<E>,
63
    key: string,
64
    search: QueryVectorSearch,
65
    dialectName: string,
66
  ): void {
67
    const { colName, distance, vectorCast } = this.resolveVectorSortParams(meta, key, search);
14✔
68
    const fn = this.vectorDistanceFns.get(distance);
14✔
69
    if (!fn) {
14✔
70
      throw Error(`${dialectName} does not support vector distance metric: ${distance}`);
4✔
71
    }
72
    ctx.append(`${fn}(${this.escapeId(colName)}, `);
10✔
73
    ctx.addValue(`[${search.$vector.join(',')}]`);
10✔
74
    if (vectorCast && dialectName === 'PostgreSQL') {
10!
NEW
75
      ctx.append(`::${vectorCast}`);
×
76
    }
77
    ctx.append(')');
10✔
78
  }
79

80
  /**
81
   * Append a vector distance projection.
82
   * Delegates to `appendVectorSort` so each dialect's distance syntax is written once.
83
   */
84
  protected appendVectorProjection<E>(
85
    ctx: QueryContext,
86
    meta: EntityMeta<E>,
87
    key: string,
88
    search: QueryVectorSearch,
89
  ): void {
90
    this.appendVectorSort(ctx, meta, key, search);
9✔
91
    ctx.append(` AS ${this.escapeId(search.$project!)}`);
9✔
92
  }
93

94
  /**
95
   * Append a vector similarity expression for `ORDER BY`.
96
   * Default: auto-delegates to `appendFunctionVectorSort` when `vectorDistanceFns` has entries.
97
   */
98
  protected appendVectorSort<E>(ctx: QueryContext, meta: EntityMeta<E>, key: string, search: QueryVectorSearch): void {
99
    if (this.vectorDistanceFns.size > 0) {
15✔
100
      this.appendFunctionVectorSort(ctx, meta, key, search, this.dialectName);
14✔
101
      return;
14✔
102
    }
103
    throw new TypeError('Vector similarity sort is not supported by this dialect. Use raw() for vector queries.');
1✔
104
  }
105
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE TRIAL · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc