@mastra/clickhouse 1.15.2 → 1.16.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # @mastra/clickhouse
2
2
 
3
+ ## 1.16.0-alpha.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Fixed ClickHouse observability discovery refreshable materialized views failing with error 36 when their target tables use a Replicated engine inside a non-Replicated (Atomic) database. The discovery views now refresh in APPEND mode, and existing deployments with the old view definitions are migrated automatically on startup (only the views are recreated; discovery data is kept). Fixes https://github.com/mastra-ai/mastra/issues/21168 ([#22103](https://github.com/mastra-ai/mastra/pull/22103))
8
+
9
+ - Updated dependencies [[`2c85f42`](https://github.com/mastra-ai/mastra/commit/2c85f428e04ccd63ea31a7ec80b5b327afdad555), [`11bbeb9`](https://github.com/mastra-ai/mastra/commit/11bbeb9b108ef2264e05acefc6dafb9cbb342921), [`1a485f3`](https://github.com/mastra-ai/mastra/commit/1a485f3538f5ec64d58bd8b5e1e99de0c695c87b), [`0d37487`](https://github.com/mastra-ai/mastra/commit/0d37487d9f349388a3f1cef6a536cf9dcc4b6273), [`8661d7d`](https://github.com/mastra-ai/mastra/commit/8661d7d7179f0a024456aabdd8679bcecd09ac28), [`575e343`](https://github.com/mastra-ai/mastra/commit/575e343900451021d96110916497d334af7bc252), [`cacb839`](https://github.com/mastra-ai/mastra/commit/cacb8392d9e74189b56d857290b0615f98a2683d), [`b47b26e`](https://github.com/mastra-ai/mastra/commit/b47b26e6fe95cb8a3482be2c5e52de157fe59d0b), [`0d37487`](https://github.com/mastra-ai/mastra/commit/0d37487d9f349388a3f1cef6a536cf9dcc4b6273), [`c46eb09`](https://github.com/mastra-ai/mastra/commit/c46eb09ce4987509af57a0ac582c61241a6dd2f1), [`30ed33e`](https://github.com/mastra-ai/mastra/commit/30ed33ee14084a26019aba15fceadda6d6ddefaf), [`91ad69d`](https://github.com/mastra-ai/mastra/commit/91ad69d64994c89199b0c55399e64ed91c61df2f), [`8dc408d`](https://github.com/mastra-ai/mastra/commit/8dc408d34438f9e13297f792c11a5cfd6cf952e1), [`c92def1`](https://github.com/mastra-ai/mastra/commit/c92def10a13c822972c96f0a4ca6ffc1f4258aed), [`c5eaec5`](https://github.com/mastra-ai/mastra/commit/c5eaec5a860d80d0e3805e67db0414b87ac8cbed), [`e66b2ba`](https://github.com/mastra-ai/mastra/commit/e66b2ba100db63eaeab6e21e1ea34b113f2ec781)]:
10
+ - @mastra/core@1.62.0-alpha.3
11
+
12
+ ## 1.16.0-alpha.0
13
+
14
+ ### Minor Changes
15
+
16
+ - Added support for filtering scores by metadata key-value pairs in listScores. ([#22047](https://github.com/mastra-ai/mastra/pull/22047))
17
+
18
+ ```typescript
19
+ const result = await storage.listScores({
20
+ filters: { metadata: { env: 'prod' } },
21
+ });
22
+ ```
23
+
24
+ Each top-level metadata key is matched with exact equality against the stored value. Nested objects and arrays compare structurally (key order doesn't matter) with no partial/subset matching, and an empty metadata filter is a no-op.
25
+
26
+ ### Patch Changes
27
+
28
+ - Updated dependencies [[`e737014`](https://github.com/mastra-ai/mastra/commit/e737014e0fc7035759762bb5b48baef1d6c0f6a7), [`d6ce34a`](https://github.com/mastra-ai/mastra/commit/d6ce34aeceb06ddf3d595a1eed5cc74f481a46a1), [`e6f8450`](https://github.com/mastra-ai/mastra/commit/e6f845074d478527026b18d85031b23353e1d0a4)]:
29
+ - @mastra/core@1.62.0-alpha.2
30
+
3
31
  ## 1.15.2
4
32
 
5
33
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-clickhouse
3
3
  description: Documentation for @mastra/clickhouse. Use when working with @mastra/clickhouse APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/clickhouse"
6
- version: "1.15.2"
6
+ version: "1.16.0-alpha.1"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.15.2",
2
+ "version": "1.16.0-alpha.1",
3
3
  "package": "@mastra/clickhouse",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -3520,7 +3520,7 @@ function unionDistinctFromSignals(kind, key1Expr, valueExpr, extraJoin = "", ext
3520
3520
  }
3521
3521
  const DISCOVERY_VALUES_MV_DDL = `
3522
3522
  CREATE MATERIALIZED VIEW IF NOT EXISTS ${MV_DISCOVERY_VALUES}
3523
- REFRESH EVERY 1 MINUTE
3523
+ REFRESH EVERY 1 MINUTE APPEND
3524
3524
  TO ${TABLE_DISCOVERY_VALUES}
3525
3525
  AS
3526
3526
  SELECT DISTINCT kind, key1, value FROM (
@@ -3545,7 +3545,7 @@ SELECT DISTINCT kind, key1, value FROM (
3545
3545
  `;
3546
3546
  const DISCOVERY_PAIRS_MV_DDL = `
3547
3547
  CREATE MATERIALIZED VIEW IF NOT EXISTS ${MV_DISCOVERY_PAIRS}
3548
- REFRESH EVERY 5 MINUTE
3548
+ REFRESH EVERY 5 MINUTE APPEND
3549
3549
  TO ${TABLE_DISCOVERY_PAIRS}
3550
3550
  AS
3551
3551
  SELECT DISTINCT kind, key1, key2, value FROM (
@@ -4381,6 +4381,64 @@ function addStringMapFilters(column, values, keyPrefix, valuePrefix, out) {
4381
4381
  }
4382
4382
  }
4383
4383
  /**
4384
+ * Adds conditions asserting the JSON value at `pathRefs` inside `src` deep-equals `value`.
4385
+ *
4386
+ * Scalars (string/number/boolean/null) use type-checked typed extraction. Objects and
4387
+ * arrays are decomposed recursively into per-leaf comparisons plus length checks, so
4388
+ * nested values match structurally regardless of key serialization order while still
4389
+ * requiring complete (exact, not partial) equality — mirroring the in-memory
4390
+ * deep-equality semantics.
4391
+ */
4392
+ function addJsonValueConditions(src, pathRefs, value, valuePrefix, nextId, out) {
4393
+ const path = pathRefs.join(", ");
4394
+ if (value === null || value === void 0) out.conditions.push(`JSONHas(${src}, ${path}) AND JSONType(${src}, ${path}) = 'Null'`);
4395
+ else if (typeof value === "string") {
4396
+ const p = `${valuePrefix}_${nextId()}`;
4397
+ out.conditions.push(`JSONType(${src}, ${path}) = 'String' AND JSONExtractString(${src}, ${path}) = {${p}:String}`);
4398
+ out.params[p] = value;
4399
+ } else if (typeof value === "number") {
4400
+ const p = `${valuePrefix}_${nextId()}`;
4401
+ out.conditions.push(`JSONType(${src}, ${path}) IN ('Int64', 'UInt64', 'Double') AND JSONExtractFloat(${src}, ${path}) = {${p}:Float64}`);
4402
+ out.params[p] = value;
4403
+ } else if (typeof value === "boolean") {
4404
+ const p = `${valuePrefix}_${nextId()}`;
4405
+ out.conditions.push(`JSONType(${src}, ${path}) = 'Bool' AND JSONExtractBool(${src}, ${path}) = {${p}:UInt8}`);
4406
+ out.params[p] = value ? 1 : 0;
4407
+ } else if (Array.isArray(value)) {
4408
+ out.conditions.push(`JSONType(${src}, ${path}) = 'Array' AND JSONLength(${src}, ${path}) = ${value.length}`);
4409
+ value.forEach((item, idx) => {
4410
+ addJsonValueConditions(src, [...pathRefs, String(idx + 1)], item, valuePrefix, nextId, out);
4411
+ });
4412
+ } else {
4413
+ const entries = Object.entries(value).filter(([, v]) => v !== void 0);
4414
+ out.conditions.push(`JSONType(${src}, ${path}) = 'Object' AND JSONLength(${src}, ${path}) = ${entries.length}`);
4415
+ for (const [subKey, subValue] of entries) {
4416
+ const p = `${valuePrefix}_k_${nextId()}`;
4417
+ out.params[p] = subKey;
4418
+ addJsonValueConditions(src, [...pathRefs, `{${p}:String}`], subValue, valuePrefix, nextId, out);
4419
+ }
4420
+ }
4421
+ }
4422
+ /**
4423
+ * Adds key/value filters against a JSON-encoded string column (e.g. scores.metadata).
4424
+ * Emits predicates per requested key with exact-value deep equality for any JSON value,
4425
+ * mirroring the in-memory deep-equality semantics (structural, key-order independent).
4426
+ */
4427
+ function addJsonStringFilters(column, values, keyPrefix, valuePrefix, out) {
4428
+ if (values == null || typeof values !== "object") return;
4429
+ let id = 0;
4430
+ const nextId = () => id++;
4431
+ let i = 0;
4432
+ for (const [rawKey, rawValue] of Object.entries(values)) {
4433
+ if (rawValue === void 0) continue;
4434
+ const keyParam = `${keyPrefix}_${i}`;
4435
+ const src = `ifNull(${column}, '{}')`;
4436
+ out.params[keyParam] = rawKey;
4437
+ addJsonValueConditions(src, [`{${keyParam}:String}`], rawValue, valuePrefix, nextId, out);
4438
+ i++;
4439
+ }
4440
+ }
4441
+ /**
4384
4442
  * Adds shared context filter conditions (commonFilterFields) to the output.
4385
4443
  * Used by logs, metrics, scores, and feedback filter builders.
4386
4444
  */
@@ -4493,6 +4551,7 @@ function buildScoresFilterConditions(filters, tableAlias) {
4493
4551
  addEq(col("scoreSource"), filters.scoreSource, "scoreSource", "String", out);
4494
4552
  if (typeof filters.scorerId === "string") addEq(col("scorerId"), filters.scorerId, "scorerId", "String", out);
4495
4553
  else if (Array.isArray(filters.scorerId)) addIn(col("scorerId"), filters.scorerId, "scorerIds", out);
4554
+ addJsonStringFilters(col("metadata"), filters.metadata, "score_meta_k", "score_meta_v", out);
4496
4555
  return out;
4497
4556
  }
4498
4557
  function buildFeedbackFilterConditions(filters, tableAlias) {
@@ -7024,6 +7083,12 @@ async function filterAppliedRetention(client, entries) {
7024
7083
  * Init's subsequent `CREATE TABLE IF NOT EXISTS` and discovery MV bootstrap
7025
7084
  * recreate both with the current definitions.
7026
7085
  *
7086
+ * Separately, a refreshable MV whose stored definition lacks the `APPEND`
7087
+ * modifier (created by older releases) is dropped — keeping its target
7088
+ * table — so the MV bootstrap recreates it with the current APPEND
7089
+ * definition. Non-APPEND refreshes swap the target table atomically, which
7090
+ * fails when the target is Replicated inside a non-Replicated database.
7091
+ *
7027
7092
  * Silently returns if `system.tables` can't be queried — the rest of init
7028
7093
  * will still run and leave any existing tables untouched.
7029
7094
  */
@@ -7038,13 +7103,20 @@ async function assertExistingTablesCompatibleWithReplication(client, replication
7038
7103
  }
7039
7104
  async function reconcileDiscoveryTables(client, replication) {
7040
7105
  let engines;
7106
+ let mvCreateQueries;
7041
7107
  try {
7042
7108
  const rows = await (await client.query({
7043
- query: `SELECT name, engine FROM system.tables WHERE database = currentDatabase() AND name IN ({tables:Array(String)})`,
7044
- query_params: { tables: [TABLE_DISCOVERY_VALUES, TABLE_DISCOVERY_PAIRS] },
7109
+ query: `SELECT name, engine, create_table_query FROM system.tables WHERE database = currentDatabase() AND name IN ({tables:Array(String)})`,
7110
+ query_params: { tables: [
7111
+ TABLE_DISCOVERY_VALUES,
7112
+ TABLE_DISCOVERY_PAIRS,
7113
+ MV_DISCOVERY_VALUES,
7114
+ MV_DISCOVERY_PAIRS
7115
+ ] },
7045
7116
  format: "JSONEachRow"
7046
7117
  })).json();
7047
7118
  engines = new Map(rows.map((r) => [r.name, r.engine]));
7119
+ mvCreateQueries = new Map(rows.map((r) => [r.name, r.create_table_query]));
7048
7120
  } catch {
7049
7121
  return;
7050
7122
  }
@@ -7057,9 +7129,13 @@ async function reconcileDiscoveryTables(client, replication) {
7057
7129
  }];
7058
7130
  for (const { table, mv } of targets) {
7059
7131
  const engine = engines.get(table);
7060
- if (!engine || isReplacingMergeTreeEngine(engine)) continue;
7061
- await client.command({ query: addOnClusterToDDL(`DROP VIEW IF EXISTS ${mv}`, replication) });
7062
- await client.command({ query: addOnClusterToDDL(`DROP TABLE IF EXISTS ${table}`, replication) });
7132
+ if (engine && !isReplacingMergeTreeEngine(engine)) {
7133
+ await client.command({ query: addOnClusterToDDL(`DROP VIEW IF EXISTS ${mv}`, replication) });
7134
+ await client.command({ query: addOnClusterToDDL(`DROP TABLE IF EXISTS ${table}`, replication) });
7135
+ continue;
7136
+ }
7137
+ const createQuery = mvCreateQueries.get(mv);
7138
+ if (createQuery && /REFRESH EVERY/i.test(createQuery) && !/\bAPPEND\b/i.test(createQuery)) await client.command({ query: addOnClusterToDDL(`DROP VIEW IF EXISTS ${mv}`, replication) });
7063
7139
  }
7064
7140
  }
7065
7141
  async function queryNamesByTable(client, query, tables) {