@mastra/pg 1.34.0-alpha.1 → 1.34.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/dist/docs/SKILL.md +1 -1
- package/dist/docs/assets/SOURCE_MAP.json +1 -1
- package/dist/docs/references/docs-memory-semantic-recall.md +2 -0
- package/dist/docs/references/reference-rag-retrieval.md +5 -1
- package/dist/index.cjs +24 -23
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +24 -23
- package/dist/index.js.map +1 -1
- package/dist/vector/index.d.ts.map +1 -1
- package/package.json +6 -6
package/dist/docs/SKILL.md
CHANGED
|
@@ -435,6 +435,8 @@ When using PostgreSQL as your vector store, you can optimize semantic recall per
|
|
|
435
435
|
|
|
436
436
|
PostgreSQL supports both IVFFlat and HNSW indexes. By default, Mastra creates an IVFFlat index, but HNSW indexes typically provide better performance, especially with OpenAI embeddings which use inner product distance.
|
|
437
437
|
|
|
438
|
+
Semantic recall always filters by thread or resource. PgVector uses the vector index for these searches only with HNSW on pgvector 0.8.0 or newer. With IVFFlat, each search compares the query against every message in the thread or resource.
|
|
439
|
+
|
|
438
440
|
```typescript
|
|
439
441
|
import { Memory } from '@mastra/memory'
|
|
440
442
|
import { PostgresStore, PgVector } from '@mastra/pg'
|
|
@@ -251,7 +251,11 @@ const results = await pgVector.query({
|
|
|
251
251
|
})
|
|
252
252
|
```
|
|
253
253
|
|
|
254
|
-
|
|
254
|
+
With an HNSW index on pgvector 0.8.0 or newer, PostgreSQL searches the index and skips rows that don't match the filter. When the index search can't fill `topK`, pgVector reruns the query as an exact search.
|
|
255
|
+
|
|
256
|
+
With IVFFlat, or HNSW on older pgvector versions, pgVector compares the query vector against every matching row. Results are exact, but the work grows with the number of matching rows.
|
|
257
|
+
|
|
258
|
+
Indexing the metadata fields you filter on speeds up both cases and gives PostgreSQL the row counts it needs to choose between them.
|
|
255
259
|
|
|
256
260
|
### Vector Query Tool
|
|
257
261
|
|
package/dist/index.cjs
CHANGED
|
@@ -1103,42 +1103,43 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
|
|
|
1103
1103
|
const scoreExpr = ops.scoreExpr(distanceExpr);
|
|
1104
1104
|
const filterClause = filterQuery.trim().replace(/^WHERE\s+/i, "");
|
|
1105
1105
|
const hasFilter = filterClause.length > 0;
|
|
1106
|
-
const useIndexedOrder = indexInfo.type === "hnsw" && !hasFilter && minScore <= 0;
|
|
1107
1106
|
const namespaceParam = filterValues.length + 1;
|
|
1108
|
-
const
|
|
1107
|
+
const queryValues = [...filterValues, namespace];
|
|
1108
|
+
const selectColumns = `vector_id as id, ${scoreExpr} as score, metadata${includeVector ? ", embedding" : ""}`;
|
|
1109
|
+
const whereClause = `WHERE namespace = $${namespaceParam}${filterClause ? ` AND (${filterClause})` : ""}`;
|
|
1110
|
+
const useIndexedOrder = indexInfo.type === "hnsw" && (!hasFilter || this.supportsIterativeScan());
|
|
1111
|
+
let rows;
|
|
1112
|
+
if (useIndexedOrder) {
|
|
1113
|
+
const indexedQuery = `
|
|
1109
1114
|
WITH vector_scores AS (
|
|
1110
|
-
SELECT
|
|
1111
|
-
vector_id as id,
|
|
1112
|
-
${scoreExpr} as score,
|
|
1113
|
-
metadata
|
|
1114
|
-
${includeVector ? ", embedding" : ""}
|
|
1115
|
+
SELECT ${selectColumns}
|
|
1115
1116
|
FROM ${tableName}
|
|
1116
|
-
|
|
1117
|
+
${whereClause}
|
|
1117
1118
|
ORDER BY ${distanceExpr}
|
|
1118
1119
|
LIMIT $2
|
|
1119
1120
|
)
|
|
1120
|
-
SELECT
|
|
1121
|
+
SELECT *, score > $1 AS above_min_score
|
|
1121
1122
|
FROM vector_scores
|
|
1122
|
-
|
|
1123
|
-
|
|
1123
|
+
ORDER BY score DESC`;
|
|
1124
|
+
const candidates = (await client.query(indexedQuery, queryValues)).rows;
|
|
1125
|
+
if (candidates.length >= topK) rows = candidates.filter((row) => row.above_min_score);
|
|
1126
|
+
}
|
|
1127
|
+
if (!rows) {
|
|
1128
|
+
const exactQuery = `
|
|
1124
1129
|
WITH vector_scores AS (
|
|
1125
|
-
SELECT
|
|
1126
|
-
vector_id as id,
|
|
1127
|
-
${scoreExpr} as score,
|
|
1128
|
-
metadata
|
|
1129
|
-
${includeVector ? ", embedding" : ""}
|
|
1130
|
+
SELECT ${selectColumns}
|
|
1130
1131
|
FROM ${tableName}
|
|
1131
|
-
|
|
1132
|
-
${filterClause ? `AND (${filterClause})` : ""}
|
|
1132
|
+
${whereClause}
|
|
1133
1133
|
)
|
|
1134
1134
|
SELECT *
|
|
1135
1135
|
FROM vector_scores
|
|
1136
1136
|
WHERE score > $1
|
|
1137
1137
|
ORDER BY score DESC
|
|
1138
1138
|
LIMIT $2`;
|
|
1139
|
-
|
|
1139
|
+
rows = (await client.query(exactQuery, queryValues)).rows;
|
|
1140
|
+
}
|
|
1140
1141
|
await client.query("COMMIT");
|
|
1141
|
-
return
|
|
1142
|
+
return rows.map(({ id, score, metadata, embedding }) => ({
|
|
1142
1143
|
id,
|
|
1143
1144
|
score,
|
|
1144
1145
|
metadata,
|
|
@@ -1405,7 +1406,7 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
|
|
|
1405
1406
|
indexConfig,
|
|
1406
1407
|
vectorType
|
|
1407
1408
|
}, client);
|
|
1408
|
-
if (metadataIndexes?.length) await this.createMetadataIndexes(tableName, indexName, metadataIndexes);
|
|
1409
|
+
if (metadataIndexes?.length) await this.createMetadataIndexes(client, tableName, indexName, metadataIndexes);
|
|
1409
1410
|
} catch (error) {
|
|
1410
1411
|
this.createdIndexes.delete(indexName);
|
|
1411
1412
|
this.namespaceReadyIndexes.delete(indexName);
|
|
@@ -1555,13 +1556,13 @@ var PgVector = class extends _mastra_core_vector.MastraVector {
|
|
|
1555
1556
|
await client.query(indexSQL);
|
|
1556
1557
|
});
|
|
1557
1558
|
}
|
|
1558
|
-
async createMetadataIndexes(tableName, indexName, metadataFields) {
|
|
1559
|
+
async createMetadataIndexes(client, tableName, indexName, metadataFields) {
|
|
1559
1560
|
const hasher = await this.hasher;
|
|
1560
1561
|
for (const field of metadataFields) {
|
|
1561
1562
|
const fieldHash = hasher.h32(field).toString(16);
|
|
1562
1563
|
const metadataIdxName = `"${indexName.slice(0, 55 - fieldHash.length)}_md_${fieldHash}_idx"`;
|
|
1563
1564
|
const escapedField = field.replace(/'/g, "''");
|
|
1564
|
-
await
|
|
1565
|
+
await client.query(`
|
|
1565
1566
|
CREATE INDEX CONCURRENTLY IF NOT EXISTS ${metadataIdxName}
|
|
1566
1567
|
ON ${tableName} ((metadata->>'${escapedField}'))
|
|
1567
1568
|
`);
|