@mastra/spanner 1.3.1 → 1.4.0-alpha.0

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,30 @@
1
1
  # @mastra/spanner
2
2
 
3
+ ## 1.4.0-alpha.0
4
+
5
+ ### Minor Changes
6
+
7
+ - Added exact metadata filtering to message history queries across Memory APIs and supported storage providers. ([#19991](https://github.com/mastra-ai/mastra/pull/19991))
8
+
9
+ ```ts
10
+ const messages = await memory.recall({
11
+ threadId: 'thread-1',
12
+ filter: {
13
+ metadata: {
14
+ status: 'done',
15
+ priority: 'high',
16
+ },
17
+ },
18
+ });
19
+ ```
20
+
21
+ Multiple fields use AND semantics. Supported values are strings, finite numbers, booleans, and `null`.
22
+
23
+ ### Patch Changes
24
+
25
+ - Updated dependencies [[`0dca9d0`](https://github.com/mastra-ai/mastra/commit/0dca9d0b1356024a53b72ea6f040db528b126caa)]:
26
+ - @mastra/core@1.54.0-alpha.0
27
+
3
28
  ## 1.3.1
4
29
 
5
30
  ### Patch Changes
@@ -3,7 +3,7 @@ name: mastra-spanner
3
3
  description: Documentation for @mastra/spanner. Use when working with @mastra/spanner APIs, configuration, or implementation.
4
4
  metadata:
5
5
  package: "@mastra/spanner"
6
- version: "1.3.1"
6
+ version: "1.4.0-alpha.0"
7
7
  ---
8
8
 
9
9
  ## When to use
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "1.3.1",
2
+ "version": "1.4.0-alpha.0",
3
3
  "package": "@mastra/spanner",
4
4
  "exports": {},
5
5
  "modules": {}
package/dist/index.cjs CHANGED
@@ -6369,6 +6369,35 @@ var MCPServersSpanner = class _MCPServersSpanner extends storage.MCPServersStora
6369
6369
  }
6370
6370
  }
6371
6371
  };
6372
+ function buildSpannerMessageMetadataFilter(metadataFilter) {
6373
+ if (!metadataFilter) return { clauses: [], params: {} };
6374
+ const clauses = [];
6375
+ const params = {};
6376
+ Object.entries(metadataFilter).forEach(([key, value], index) => {
6377
+ const path = `$.metadata.${key}`;
6378
+ const valueExpr = `JSON_VALUE(SAFE.PARSE_JSON(content), '${path}')`;
6379
+ const queryExpr = `JSON_QUERY(SAFE.PARSE_JSON(content), '${path}')`;
6380
+ const typeExpr = `JSON_TYPE(${queryExpr})`;
6381
+ if (value === null) {
6382
+ clauses.push(`${queryExpr} = 'null'`);
6383
+ return;
6384
+ }
6385
+ const paramName = `metadataValue${index}`;
6386
+ if (typeof value === "string") {
6387
+ params[paramName] = value;
6388
+ clauses.push(`${typeExpr} = 'string' AND ${valueExpr} = @${paramName}`);
6389
+ return;
6390
+ }
6391
+ if (typeof value === "number") {
6392
+ params[paramName] = String(value);
6393
+ clauses.push(`${typeExpr} = 'number' AND ${valueExpr} = @${paramName}`);
6394
+ return;
6395
+ }
6396
+ params[paramName] = value ? "true" : "false";
6397
+ clauses.push(`${typeExpr} = 'boolean' AND ${valueExpr} = @${paramName}`);
6398
+ });
6399
+ return { clauses, params };
6400
+ }
6372
6401
  var MemorySpanner = class _MemorySpanner extends storage.MemoryStorage {
6373
6402
  database;
6374
6403
  db;
@@ -6847,6 +6876,7 @@ var MemorySpanner = class _MemorySpanner extends storage.MemoryStorage {
6847
6876
  /** Paginated message listing for a thread, with optional pinned-include and date-range filters. */
6848
6877
  async listMessages(args) {
6849
6878
  const { threadId, resourceId, include, filter, perPage: perPageInput, page = 0, orderBy } = args;
6879
+ const metadataFilter = storage.validateStorageMetadataFilter(filter?.metadata);
6850
6880
  const threadIds = Array.isArray(threadId) ? threadId : [threadId];
6851
6881
  if (threadIds.length === 0 || threadIds.some((id) => !id || !id.trim())) {
6852
6882
  throw new error.MastraError(
@@ -6872,10 +6902,16 @@ var MemorySpanner = class _MemorySpanner extends storage.MemoryStorage {
6872
6902
  ...buildDateRangeFilter(filter?.dateRange, "createdAt")
6873
6903
  };
6874
6904
  const {
6875
- sql: whereSql,
6905
+ sql: preparedWhereSql,
6876
6906
  params: whereParams,
6877
6907
  types: whereTypes
6878
6908
  } = this.db.prepareWhereClause(filters, storage.TABLE_MESSAGES);
6909
+ const metadataWhere = buildSpannerMessageMetadataFilter(metadataFilter);
6910
+ const whereSql = [preparedWhereSql, ...metadataWhere.clauses].reduce((sql, clause) => {
6911
+ if (!clause) return sql;
6912
+ return sql ? `${sql} AND ${clause}` : ` WHERE ${clause}`;
6913
+ }, "");
6914
+ Object.assign(whereParams, metadataWhere.params);
6879
6915
  if (perPage === 0 && (!include || include.length === 0)) {
6880
6916
  return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
6881
6917
  }
@@ -6930,6 +6966,7 @@ var MemorySpanner = class _MemorySpanner extends storage.MemoryStorage {
6930
6966
  const messages = baseRows.map(
6931
6967
  (r) => transformFromSpannerRow({ tableName: storage.TABLE_MESSAGES, row: r })
6932
6968
  );
6969
+ const primaryPageCount = messages.length;
6933
6970
  if (total === 0 && messages.length === 0 && (!include || include.length === 0)) {
6934
6971
  return { messages: [], total: 0, page, perPage: perPageForResponse, hasMore: false };
6935
6972
  }
@@ -6960,7 +6997,7 @@ var MemorySpanner = class _MemorySpanner extends storage.MemoryStorage {
6960
6997
  finalMessages.filter((m) => m.threadId && threadIdSet.has(m.threadId)).map((m) => m.id)
6961
6998
  );
6962
6999
  const allThreadMessagesReturned = returnedThreadMessageIds.size >= total;
6963
- const hasMore = perPageInput !== false && !allThreadMessagesReturned && offset + perPage < total;
7000
+ const hasMore = metadataFilter ? perPageInput !== false && offset + primaryPageCount < total : perPageInput !== false && !allThreadMessagesReturned && offset + perPage < total;
6964
7001
  return {
6965
7002
  messages: finalMessages,
6966
7003
  total,