montycat 1.1.7 → 1.1.8
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/README.md +26 -3
- package/dist/classes/generic.d.ts +68 -0
- package/dist/classes/generic.js +64 -3
- package/dist/functions/storeGenericFunctions.js +7 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -15,7 +15,7 @@ The official Node.js & TypeScript SDK for [Montycat](https://montygovernance.com
|
|
|
15
15
|
// Search your data by MEANING — no external APIs, no separate vector database.
|
|
16
16
|
// (already ON by default in the montycat-semantic server edition)
|
|
17
17
|
const hits = await Sales.semanticSearchGetValues({ query: 'Show all Bluetooth devices', limitOutput: { start: 0, stop: 5 } });
|
|
18
|
-
// → [{ __key__
|
|
18
|
+
// → [{ __key__: 123..., __score__: 0.78, __value__: { name: 'Wireless Headphones' }}]
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
> ### 🧩 All-in-one. AI-native. **Zero external dependencies.**
|
|
@@ -93,7 +93,7 @@ docker run -d --name montycat \
|
|
|
93
93
|
-p 21210:21210 -p 21211:21211 \
|
|
94
94
|
-e MONTYCAT_SUPEROWNER="admin" \
|
|
95
95
|
-e MONTYCAT_PASSWORD="change-me" \
|
|
96
|
-
-v montycat_data:/
|
|
96
|
+
-v montycat_data:/var/lib/.montycat \
|
|
97
97
|
montygovernance/montycat:semantic
|
|
98
98
|
```
|
|
99
99
|
|
|
@@ -345,6 +345,29 @@ await engine.enableSemanticSearch({ model: 'bge-base' });
|
|
|
345
345
|
await engine.disableSemanticSearch();
|
|
346
346
|
```
|
|
347
347
|
|
|
348
|
+
### Hybrid semantic search
|
|
349
|
+
|
|
350
|
+
Use semantic ranking with a structured metadata constraint. The filter is a
|
|
351
|
+
hard AND pre-filter using the same criteria shape as `lookupKeysWhere`; it does
|
|
352
|
+
not boost cosine scores.
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
const matchingKeys = await Sales.semanticSearchGetKeysWhere({
|
|
356
|
+
query: 'astronomy and outer space',
|
|
357
|
+
filters: { category: 'space' },
|
|
358
|
+
limitOutput: { start: 0, stop: 5 },
|
|
359
|
+
minScore: 0.35,
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
const matchingValues = await Sales.semanticSearchGetValuesWhere({
|
|
363
|
+
query: 'astronomy and outer space',
|
|
364
|
+
filters: { category: 'space' },
|
|
365
|
+
limitOutput: { start: 0, stop: 5 },
|
|
366
|
+
});
|
|
367
|
+
// key hits: { __key__, __score__ }
|
|
368
|
+
// value hits: { __key__, __score__, __value__ }
|
|
369
|
+
```
|
|
370
|
+
|
|
348
371
|
## 🔗 Links
|
|
349
372
|
|
|
350
373
|
- 🌐 **Website & Docs** — https://montygovernance.com
|
|
@@ -357,4 +380,4 @@ await engine.disableSemanticSearch();
|
|
|
357
380
|
- **Is Montycat a vector database or a NoSQL database?** Both — one engine. Store records and query them by *meaning* (vector / semantic search) or by key/schema, without running two systems.
|
|
358
381
|
- **Do I need OpenAI or an embedding API?** No. Embeddings run on-device in the `montycat-semantic` server. No API keys, no per-query bill, no data egress.
|
|
359
382
|
- **Is it a Pinecone / Weaviate / Chroma / Qdrant alternative?** Yes — self-hosted and open-source, with a NoSQL store built in.
|
|
360
|
-
- **TypeScript support?** First-class — the package ships its own type definitions. Works with Node.js, Deno, Bun, Express, Fastify, and Next.js.
|
|
383
|
+
- **TypeScript support?** First-class — the package ships its own type definitions. Works with Node.js, Deno, Bun, Express, Fastify, and Next.js.
|
|
@@ -183,6 +183,36 @@ declare class GenericKV {
|
|
|
183
183
|
};
|
|
184
184
|
minScore?: number | null;
|
|
185
185
|
}): Promise<any>;
|
|
186
|
+
/**
|
|
187
|
+
* Hybrid semantic search returning ranked keys only, restricted by a
|
|
188
|
+
* metadata filter.
|
|
189
|
+
*
|
|
190
|
+
* Same ranking as `semanticSearchGetKeys`, but only items matching
|
|
191
|
+
* `filters` are considered — a hard AND constraint through the same
|
|
192
|
+
* criteria stack as `lookupKeysWhere` (indexed fields, Timestamp,
|
|
193
|
+
* Pointer). Scores stay pure cosine; the filter never boosts, it only
|
|
194
|
+
* restricts. A filter matching nothing resolves with `[]`.
|
|
195
|
+
*
|
|
196
|
+
* A separate method (not a parameter on `semanticSearchGetKeys`) so
|
|
197
|
+
* existing integrations keep their exact signature.
|
|
198
|
+
*
|
|
199
|
+
* @param query - The natural-language query text to embed and search for.
|
|
200
|
+
* @param filters - Metadata criteria, same shape as `lookupKeysWhere`.
|
|
201
|
+
* @param limitOutput - Start/stop over the ranked hits; `{start: 0, stop: 0}`
|
|
202
|
+
* (the default) lets the server apply its default top-k (10).
|
|
203
|
+
* @param minScore - Drop hits whose cosine similarity (in [-1, 1]) is below
|
|
204
|
+
* this value. Default null (no score filter).
|
|
205
|
+
* @return A promise resolving with ranked hits, each `{__key__, __score__}`.
|
|
206
|
+
*/
|
|
207
|
+
static semanticSearchGetKeysWhere({ query, filters, limitOutput, minScore }: {
|
|
208
|
+
query: string;
|
|
209
|
+
filters: object;
|
|
210
|
+
limitOutput?: {
|
|
211
|
+
start: number;
|
|
212
|
+
stop: number;
|
|
213
|
+
};
|
|
214
|
+
minScore?: number | null;
|
|
215
|
+
}): Promise<any>;
|
|
186
216
|
/**
|
|
187
217
|
* Semantic (vector similarity) search returning ranked hits with their values.
|
|
188
218
|
*
|
|
@@ -218,6 +248,44 @@ declare class GenericKV {
|
|
|
218
248
|
withPointers?: boolean;
|
|
219
249
|
pointersMetadata?: boolean;
|
|
220
250
|
}): Promise<any>;
|
|
251
|
+
/**
|
|
252
|
+
* Hybrid semantic search returning ranked hits with their values,
|
|
253
|
+
* restricted by a metadata filter.
|
|
254
|
+
*
|
|
255
|
+
* Same ranking as `semanticSearchGetValues`, but only items matching
|
|
256
|
+
* `filters` are considered — a hard AND constraint through the same
|
|
257
|
+
* criteria stack as `lookupKeysWhere` (indexed fields, Timestamp,
|
|
258
|
+
* Pointer). Scores stay pure cosine; the filter never boosts, it only
|
|
259
|
+
* restricts. A filter matching nothing resolves with `[]`.
|
|
260
|
+
*
|
|
261
|
+
* A separate method (not a parameter on `semanticSearchGetValues`) so
|
|
262
|
+
* existing integrations keep their exact signature.
|
|
263
|
+
*
|
|
264
|
+
* @param query - The natural-language query text to embed and search for.
|
|
265
|
+
* @param filters - Metadata criteria, same shape as `lookupKeysWhere`.
|
|
266
|
+
* @param limitOutput - Start/stop over the ranked hits; `{start: 0, stop: 0}`
|
|
267
|
+
* (the default) lets the server apply its default top-k (10).
|
|
268
|
+
* @param minScore - Drop hits whose cosine similarity (in [-1, 1]) is below
|
|
269
|
+
* this value. Default null (no score filter).
|
|
270
|
+
* @param withPointers - Whether to include pointers (foreign values) in each
|
|
271
|
+
* returned value.
|
|
272
|
+
* @param pointersMetadata - Whether to include pointer metadata in each
|
|
273
|
+
* returned value.
|
|
274
|
+
* @return A promise resolving with ranked hits, each
|
|
275
|
+
* `{__key__, __score__, __value__}` — the same dunder envelope
|
|
276
|
+
* `lookupValuesWhere` returns with `keyIncluded: true`, plus the score.
|
|
277
|
+
*/
|
|
278
|
+
static semanticSearchGetValuesWhere({ query, filters, limitOutput, minScore, withPointers, pointersMetadata }: {
|
|
279
|
+
query: string;
|
|
280
|
+
filters: object;
|
|
281
|
+
limitOutput?: {
|
|
282
|
+
start: number;
|
|
283
|
+
stop: number;
|
|
284
|
+
};
|
|
285
|
+
minScore?: number | null;
|
|
286
|
+
withPointers?: boolean;
|
|
287
|
+
pointersMetadata?: boolean;
|
|
288
|
+
}): Promise<any>;
|
|
221
289
|
/**
|
|
222
290
|
* Gets the length of the keyspace.
|
|
223
291
|
* @returns A promise that resolves with the length of the keyspace.
|
package/dist/classes/generic.js
CHANGED
|
@@ -221,7 +221,7 @@ class GenericKV {
|
|
|
221
221
|
* public methods differ only in which value-inclusion flags they pass,
|
|
222
222
|
* so the wire call lives here once.
|
|
223
223
|
*/
|
|
224
|
-
static async semanticSearchCore(query, limitOutput, minScore, withPointers, keyIncluded, pointersMetadata) {
|
|
224
|
+
static async semanticSearchCore(query, limitOutput, minScore, filters, withPointers, keyIncluded, pointersMetadata) {
|
|
225
225
|
if (!query || !query.trim()) {
|
|
226
226
|
throw new Error("No query text provided for semantic search.");
|
|
227
227
|
}
|
|
@@ -230,6 +230,7 @@ class GenericKV {
|
|
|
230
230
|
semanticQuery: query,
|
|
231
231
|
limitOutput,
|
|
232
232
|
minScore,
|
|
233
|
+
semanticFilter: filters,
|
|
233
234
|
withPointers,
|
|
234
235
|
keyIncluded,
|
|
235
236
|
pointersMetadata,
|
|
@@ -257,7 +258,34 @@ class GenericKV {
|
|
|
257
258
|
* @return A promise resolving with ranked hits, each `{__key__, __score__}`.
|
|
258
259
|
*/
|
|
259
260
|
static async semanticSearchGetKeys({ query, limitOutput = { start: 0, stop: 0 }, minScore = null }) {
|
|
260
|
-
return this.semanticSearchCore(query, limitOutput, minScore, false, false, false);
|
|
261
|
+
return this.semanticSearchCore(query, limitOutput, minScore, null, false, false, false);
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Hybrid semantic search returning ranked keys only, restricted by a
|
|
265
|
+
* metadata filter.
|
|
266
|
+
*
|
|
267
|
+
* Same ranking as `semanticSearchGetKeys`, but only items matching
|
|
268
|
+
* `filters` are considered — a hard AND constraint through the same
|
|
269
|
+
* criteria stack as `lookupKeysWhere` (indexed fields, Timestamp,
|
|
270
|
+
* Pointer). Scores stay pure cosine; the filter never boosts, it only
|
|
271
|
+
* restricts. A filter matching nothing resolves with `[]`.
|
|
272
|
+
*
|
|
273
|
+
* A separate method (not a parameter on `semanticSearchGetKeys`) so
|
|
274
|
+
* existing integrations keep their exact signature.
|
|
275
|
+
*
|
|
276
|
+
* @param query - The natural-language query text to embed and search for.
|
|
277
|
+
* @param filters - Metadata criteria, same shape as `lookupKeysWhere`.
|
|
278
|
+
* @param limitOutput - Start/stop over the ranked hits; `{start: 0, stop: 0}`
|
|
279
|
+
* (the default) lets the server apply its default top-k (10).
|
|
280
|
+
* @param minScore - Drop hits whose cosine similarity (in [-1, 1]) is below
|
|
281
|
+
* this value. Default null (no score filter).
|
|
282
|
+
* @return A promise resolving with ranked hits, each `{__key__, __score__}`.
|
|
283
|
+
*/
|
|
284
|
+
static async semanticSearchGetKeysWhere({ query, filters, limitOutput = { start: 0, stop: 0 }, minScore = null }) {
|
|
285
|
+
if (!filters || Object.keys(filters).length === 0) {
|
|
286
|
+
throw new Error("No filters provided for hybrid semantic search.");
|
|
287
|
+
}
|
|
288
|
+
return this.semanticSearchCore(query, limitOutput, minScore, filters, false, false, false);
|
|
261
289
|
}
|
|
262
290
|
/**
|
|
263
291
|
* Semantic (vector similarity) search returning ranked hits with their values.
|
|
@@ -285,7 +313,40 @@ class GenericKV {
|
|
|
285
313
|
* `lookupValuesWhere` returns with `keyIncluded: true`, plus the score.
|
|
286
314
|
*/
|
|
287
315
|
static async semanticSearchGetValues({ query, limitOutput = { start: 0, stop: 0 }, minScore = null, withPointers = false, pointersMetadata = false }) {
|
|
288
|
-
return this.semanticSearchCore(query, limitOutput, minScore, withPointers, true, pointersMetadata);
|
|
316
|
+
return this.semanticSearchCore(query, limitOutput, minScore, null, withPointers, true, pointersMetadata);
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* Hybrid semantic search returning ranked hits with their values,
|
|
320
|
+
* restricted by a metadata filter.
|
|
321
|
+
*
|
|
322
|
+
* Same ranking as `semanticSearchGetValues`, but only items matching
|
|
323
|
+
* `filters` are considered — a hard AND constraint through the same
|
|
324
|
+
* criteria stack as `lookupKeysWhere` (indexed fields, Timestamp,
|
|
325
|
+
* Pointer). Scores stay pure cosine; the filter never boosts, it only
|
|
326
|
+
* restricts. A filter matching nothing resolves with `[]`.
|
|
327
|
+
*
|
|
328
|
+
* A separate method (not a parameter on `semanticSearchGetValues`) so
|
|
329
|
+
* existing integrations keep their exact signature.
|
|
330
|
+
*
|
|
331
|
+
* @param query - The natural-language query text to embed and search for.
|
|
332
|
+
* @param filters - Metadata criteria, same shape as `lookupKeysWhere`.
|
|
333
|
+
* @param limitOutput - Start/stop over the ranked hits; `{start: 0, stop: 0}`
|
|
334
|
+
* (the default) lets the server apply its default top-k (10).
|
|
335
|
+
* @param minScore - Drop hits whose cosine similarity (in [-1, 1]) is below
|
|
336
|
+
* this value. Default null (no score filter).
|
|
337
|
+
* @param withPointers - Whether to include pointers (foreign values) in each
|
|
338
|
+
* returned value.
|
|
339
|
+
* @param pointersMetadata - Whether to include pointer metadata in each
|
|
340
|
+
* returned value.
|
|
341
|
+
* @return A promise resolving with ranked hits, each
|
|
342
|
+
* `{__key__, __score__, __value__}` — the same dunder envelope
|
|
343
|
+
* `lookupValuesWhere` returns with `keyIncluded: true`, plus the score.
|
|
344
|
+
*/
|
|
345
|
+
static async semanticSearchGetValuesWhere({ query, filters, limitOutput = { start: 0, stop: 0 }, minScore = null, withPointers = false, pointersMetadata = false }) {
|
|
346
|
+
if (!filters || Object.keys(filters).length === 0) {
|
|
347
|
+
throw new Error("No filters provided for hybrid semantic search.");
|
|
348
|
+
}
|
|
349
|
+
return this.semanticSearchCore(query, limitOutput, minScore, filters, withPointers, true, pointersMetadata);
|
|
289
350
|
}
|
|
290
351
|
/**
|
|
291
352
|
* Gets the length of the keyspace.
|
|
@@ -58,7 +58,7 @@ function processBulkKeysValues(bulkKeysValues) {
|
|
|
58
58
|
* @returns A JSON string representing the binary query.
|
|
59
59
|
* */
|
|
60
60
|
function convertToBinaryQuery(cls, options = {}) {
|
|
61
|
-
const { key = null, value = {}, expireSec = 0, bulkValues = [], bulkKeys = [], bulkKeysValues = {}, searchCriteria = {}, limitOutput = { start: 0, stop: 0 }, withPointers = false, schema = null, keyIncluded = false, pointersMetadata = false, volumes = [], latestVolume = false, semanticQuery = null, minScore = null, waitForIndex = null, } = options;
|
|
61
|
+
const { key = null, value = {}, expireSec = 0, bulkValues = [], bulkKeys = [], bulkKeysValues = {}, searchCriteria = {}, limitOutput = { start: 0, stop: 0 }, withPointers = false, schema = null, keyIncluded = false, pointersMetadata = false, volumes = [], latestVolume = false, semanticQuery = null, minScore = null, semanticFilter = null, waitForIndex = null, } = options;
|
|
62
62
|
const { processedValue, foundSchema } = processValue(value);
|
|
63
63
|
const { processedBulkValues, uniqueSchema } = processBulkValues(bulkValues);
|
|
64
64
|
const bulkKeysValuesProcessed = processBulkKeysValues(bulkKeysValues);
|
|
@@ -94,6 +94,12 @@ function convertToBinaryQuery(cls, options = {}) {
|
|
|
94
94
|
if (minScore !== null) {
|
|
95
95
|
queryDict.min_score = minScore;
|
|
96
96
|
}
|
|
97
|
+
// Hybrid metadata pre-filter for `semantic_search` (hard AND constraint,
|
|
98
|
+
// same criteria shape as lookupKeysWhere — Timestamp/Pointer supported).
|
|
99
|
+
// Omit when null so the wire is unchanged for existing commands.
|
|
100
|
+
if (semanticFilter !== null) {
|
|
101
|
+
queryDict.semantic_filter = JSON.stringify(processSearchCriteria(semanticFilter));
|
|
102
|
+
}
|
|
97
103
|
// Per-request wait_for_index override for persistent writes; omit when null
|
|
98
104
|
// so the server falls back to its DB-wide default (existing wire unchanged).
|
|
99
105
|
if (waitForIndex !== null) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "montycat",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.8",
|
|
4
4
|
"description": "Self-hosted vector database + NoSQL with built-in AI semantic search — the Node.js & TypeScript client for Montycat. A Rust-powered, AI-native Pinecone / Weaviate / Chroma alternative for RAG, AI agents & LLM memory.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|