chub-dev 0.1.0 → 0.1.2-beta.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/README.md +55 -0
- package/bin/chub-mcp +2 -0
- package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
- package/dist/airtable/docs/database/python/DOC.md +1735 -0
- package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
- package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
- package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
- package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
- package/dist/asana/docs/tasks/DOC.md +1396 -0
- package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
- package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
- package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
- package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
- package/dist/auth0/docs/identity/python/DOC.md +1199 -0
- package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
- package/dist/aws/docs/s3/python/DOC.md +1807 -0
- package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
- package/dist/binance/docs/trading/python/DOC.md +1454 -0
- package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
- package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
- package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
- package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
- package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
- package/dist/clerk/docs/auth/python/DOC.md +274 -0
- package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
- package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
- package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
- package/dist/cohere/docs/llm/DOC.md +1335 -0
- package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
- package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
- package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
- package/dist/deepgram/docs/speech/python/DOC.md +685 -0
- package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
- package/dist/deepl/docs/translation/python/DOC.md +944 -0
- package/dist/deepseek/docs/llm/DOC.md +1220 -0
- package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
- package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
- package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
- package/dist/discord/docs/bot/python/DOC.md +1130 -0
- package/dist/elasticsearch/docs/search/DOC.md +1634 -0
- package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
- package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
- package/dist/firebase/docs/auth/DOC.md +1015 -0
- package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
- package/dist/gemini/docs/genai/python/DOC.md +555 -0
- package/dist/github/docs/octokit/DOC.md +1560 -0
- package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
- package/dist/google/docs/bigquery/python/DOC.md +1503 -0
- package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
- package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
- package/dist/huggingface/docs/transformers/DOC.md +948 -0
- package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
- package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
- package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
- package/dist/jira/docs/issues/python/DOC.md +1492 -0
- package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
- package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
- package/dist/landingai-ade/docs/api/DOC.md +620 -0
- package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
- package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
- package/dist/landingai-ade/skills/SKILL.md +489 -0
- package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
- package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
- package/dist/linear/docs/tracker/DOC.md +1554 -0
- package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
- package/dist/livekit/docs/realtime/python/DOC.md +163 -0
- package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
- package/dist/meilisearch/docs/search/DOC.md +1241 -0
- package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
- package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
- package/dist/mongodb/docs/atlas/DOC.md +2041 -0
- package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
- package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
- package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
- package/dist/okta/docs/identity/python/DOC.md +1401 -0
- package/dist/openai/docs/chat/javascript/DOC.md +407 -0
- package/dist/openai/docs/chat/python/DOC.md +568 -0
- package/dist/paypal/docs/checkout/DOC.md +278 -0
- package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
- package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
- package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
- package/dist/plaid/docs/banking/python/DOC.md +1203 -0
- package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
- package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
- package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
- package/dist/prisma/docs/orm/python/DOC.md +1317 -0
- package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
- package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
- package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
- package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
- package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
- package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
- package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
- package/dist/redis/docs/key-value/python/DOC.md +2054 -0
- package/dist/registry.json +2817 -0
- package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
- package/dist/resend/docs/email/DOC.md +1271 -0
- package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
- package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
- package/dist/search-index.json +1 -0
- package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
- package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
- package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
- package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
- package/dist/shopify/docs/storefront/DOC.md +457 -0
- package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
- package/dist/slack/docs/workspace/python/DOC.md +271 -0
- package/dist/square/docs/payments/javascript/DOC.md +1855 -0
- package/dist/square/docs/payments/python/DOC.md +1728 -0
- package/dist/stripe/docs/api/DOC.md +1727 -0
- package/dist/stripe/docs/payments/DOC.md +1726 -0
- package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
- package/dist/stytch/docs/auth/python/DOC.md +1962 -0
- package/dist/supabase/docs/client/DOC.md +1606 -0
- package/dist/twilio/docs/messaging/python/DOC.md +469 -0
- package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
- package/dist/vercel/docs/platform/DOC.md +1940 -0
- package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
- package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
- package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
- package/dist/zendesk/docs/support/python/DOC.md +2297 -0
- package/package.json +22 -6
- package/skills/get-api-docs/SKILL.md +84 -0
- package/src/commands/annotate.js +83 -0
- package/src/commands/build.js +12 -1
- package/src/commands/feedback.js +150 -0
- package/src/commands/get.js +83 -42
- package/src/commands/search.js +7 -0
- package/src/index.js +43 -17
- package/src/lib/analytics.js +90 -0
- package/src/lib/annotations.js +57 -0
- package/src/lib/bm25.js +170 -0
- package/src/lib/cache.js +69 -6
- package/src/lib/config.js +8 -3
- package/src/lib/identity.js +99 -0
- package/src/lib/registry.js +103 -20
- package/src/lib/telemetry.js +86 -0
- package/src/mcp/server.js +177 -0
- package/src/mcp/tools.js +251 -0
|
@@ -0,0 +1,1241 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: search
|
|
3
|
+
description: "Meilisearch JavaScript SDK coding guidelines for full-text search and indexing"
|
|
4
|
+
metadata:
|
|
5
|
+
languages: "javascript"
|
|
6
|
+
versions: "0.53.0"
|
|
7
|
+
updated-on: "2026-03-02"
|
|
8
|
+
source: maintainer
|
|
9
|
+
tags: "meilisearch,search,full-text,indexing,instant"
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Meilisearch JavaScript SDK Coding Guidelines
|
|
13
|
+
|
|
14
|
+
You are a Meilisearch API coding expert. Help me with writing code using the Meilisearch API calling the official libraries and SDKs.
|
|
15
|
+
|
|
16
|
+
You can find the official SDK documentation and code samples here:
|
|
17
|
+
https://meilisearch.github.io/meilisearch-js/
|
|
18
|
+
|
|
19
|
+
## Golden Rule: Use the Correct and Current SDK
|
|
20
|
+
|
|
21
|
+
Always use the Meilisearch JavaScript SDK to call Meilisearch, which is the standard library for all Meilisearch API interactions. Do not use legacy libraries or unofficial SDKs.
|
|
22
|
+
|
|
23
|
+
- **Library Name:** Meilisearch JavaScript SDK
|
|
24
|
+
- **NPM Package:** `meilisearch`
|
|
25
|
+
- **Legacy Libraries**: Other unofficial packages are not recommended
|
|
26
|
+
|
|
27
|
+
**Installation:**
|
|
28
|
+
|
|
29
|
+
- **Correct:** `npm install meilisearch`
|
|
30
|
+
|
|
31
|
+
**APIs and Usage:**
|
|
32
|
+
|
|
33
|
+
- **Correct:** `import { MeiliSearch } from 'meilisearch'`
|
|
34
|
+
- **Correct:** `const client = new MeiliSearch({ host, apiKey })`
|
|
35
|
+
- **Correct:** `await client.index('indexName').search('query')`
|
|
36
|
+
- **Correct:** `await client.index('indexName').addDocuments(documents)`
|
|
37
|
+
- **Incorrect:** `MeilisearchClient` or `MeiliSearchAPI`
|
|
38
|
+
- **Incorrect:** Legacy v0.x packages
|
|
39
|
+
|
|
40
|
+
## Installation
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
npm install meilisearch
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
For browser environments, you can use a CDN:
|
|
47
|
+
|
|
48
|
+
```html
|
|
49
|
+
<script src="https://cdn.jsdelivr.net/npm/meilisearch@latest/dist/bundles/meilisearch.umd.js"></script>
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Initialization
|
|
53
|
+
|
|
54
|
+
The `meilisearch` library requires creating a `MeiliSearch` instance for all API calls.
|
|
55
|
+
|
|
56
|
+
### Basic Initialization
|
|
57
|
+
|
|
58
|
+
```javascript
|
|
59
|
+
import { MeiliSearch } from 'meilisearch';
|
|
60
|
+
|
|
61
|
+
const client = new MeiliSearch({
|
|
62
|
+
host: 'http://127.0.0.1:7700',
|
|
63
|
+
apiKey: 'masterKey',
|
|
64
|
+
});
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Using Environment Variables
|
|
68
|
+
|
|
69
|
+
For production applications, always use environment variables to store API keys:
|
|
70
|
+
|
|
71
|
+
```javascript
|
|
72
|
+
import { MeiliSearch } from 'meilisearch';
|
|
73
|
+
|
|
74
|
+
const client = new MeiliSearch({
|
|
75
|
+
host: process.env.MEILISEARCH_HOST || 'http://127.0.0.1:7700',
|
|
76
|
+
apiKey: process.env.MEILISEARCH_API_KEY,
|
|
77
|
+
});
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### CommonJS Import
|
|
81
|
+
|
|
82
|
+
```javascript
|
|
83
|
+
const { MeiliSearch } = require('meilisearch');
|
|
84
|
+
|
|
85
|
+
const client = new MeiliSearch({
|
|
86
|
+
host: 'http://127.0.0.1:7700',
|
|
87
|
+
apiKey: 'masterKey',
|
|
88
|
+
});
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
### Browser Initialization
|
|
92
|
+
|
|
93
|
+
```javascript
|
|
94
|
+
const client = new window.MeiliSearch({
|
|
95
|
+
host: 'http://127.0.0.1:7700',
|
|
96
|
+
apiKey: 'searchOnlyApiKey', // Use search-only key for frontend
|
|
97
|
+
});
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## API Keys and Security
|
|
101
|
+
|
|
102
|
+
Meilisearch uses API keys for authentication. There are different types of API keys for different use cases.
|
|
103
|
+
|
|
104
|
+
### Master Key
|
|
105
|
+
|
|
106
|
+
The master key has full access to all endpoints and should only be used server-side:
|
|
107
|
+
|
|
108
|
+
```javascript
|
|
109
|
+
const adminClient = new MeiliSearch({
|
|
110
|
+
host: 'http://127.0.0.1:7700',
|
|
111
|
+
apiKey: process.env.MEILISEARCH_MASTER_KEY,
|
|
112
|
+
});
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
### Default Search API Key
|
|
116
|
+
|
|
117
|
+
Use the search-only key for frontend applications to limit access:
|
|
118
|
+
|
|
119
|
+
```javascript
|
|
120
|
+
const searchClient = new MeiliSearch({
|
|
121
|
+
host: 'http://127.0.0.1:7700',
|
|
122
|
+
apiKey: process.env.MEILISEARCH_SEARCH_KEY,
|
|
123
|
+
});
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
### Creating Custom API Keys
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
const key = await client.createKey({
|
|
130
|
+
description: 'Search key for products index',
|
|
131
|
+
actions: ['search'],
|
|
132
|
+
indexes: ['products'],
|
|
133
|
+
expiresAt: new Date('2025-12-31'),
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
console.log(key.key); // Use this key in your application
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
### Getting All API Keys
|
|
140
|
+
|
|
141
|
+
```javascript
|
|
142
|
+
const keys = await client.getKeys();
|
|
143
|
+
console.log(keys.results);
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
### Deleting an API Key
|
|
147
|
+
|
|
148
|
+
```javascript
|
|
149
|
+
await client.deleteKey('your-key-uid');
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
## Indexes
|
|
153
|
+
|
|
154
|
+
Indexes are where your documents are stored and searched.
|
|
155
|
+
|
|
156
|
+
### Creating an Index
|
|
157
|
+
|
|
158
|
+
```javascript
|
|
159
|
+
const index = await client.createIndex('movies', { primaryKey: 'id' });
|
|
160
|
+
console.log(index.uid); // 'movies'
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Getting an Index
|
|
164
|
+
|
|
165
|
+
```javascript
|
|
166
|
+
const index = client.index('movies');
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Listing All Indexes
|
|
170
|
+
|
|
171
|
+
```javascript
|
|
172
|
+
const indexes = await client.getIndexes();
|
|
173
|
+
console.log(indexes.results);
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### Updating an Index
|
|
177
|
+
|
|
178
|
+
```javascript
|
|
179
|
+
await client.index('movies').update({ primaryKey: 'movie_id' });
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### Deleting an Index
|
|
183
|
+
|
|
184
|
+
```javascript
|
|
185
|
+
await client.deleteIndex('movies');
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
### Getting Index Stats
|
|
189
|
+
|
|
190
|
+
```javascript
|
|
191
|
+
const stats = await client.index('movies').getStats();
|
|
192
|
+
console.log(stats.numberOfDocuments);
|
|
193
|
+
console.log(stats.isIndexing);
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
## Documents
|
|
197
|
+
|
|
198
|
+
Documents are the individual records in your index.
|
|
199
|
+
|
|
200
|
+
### Adding Documents
|
|
201
|
+
|
|
202
|
+
```javascript
|
|
203
|
+
const documents = [
|
|
204
|
+
{ id: 1, title: 'Inception', genre: 'Sci-Fi', year: 2010 },
|
|
205
|
+
{ id: 2, title: 'The Dark Knight', genre: 'Action', year: 2008 },
|
|
206
|
+
{ id: 3, title: 'Interstellar', genre: 'Sci-Fi', year: 2014 },
|
|
207
|
+
];
|
|
208
|
+
|
|
209
|
+
const response = await client.index('movies').addDocuments(documents);
|
|
210
|
+
console.log(response.taskUid); // Use this to check task status
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### Adding Documents with Auto-ID Generation
|
|
214
|
+
|
|
215
|
+
If documents don't have an ID, Meilisearch can generate them:
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
const documents = [
|
|
219
|
+
{ title: 'Movie without ID', genre: 'Drama' },
|
|
220
|
+
];
|
|
221
|
+
|
|
222
|
+
await client.index('movies').addDocuments(documents);
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### Adding Documents with Primary Key
|
|
226
|
+
|
|
227
|
+
Specify a custom primary key field:
|
|
228
|
+
|
|
229
|
+
```javascript
|
|
230
|
+
const documents = [
|
|
231
|
+
{ movie_id: 'mv001', title: 'Inception' },
|
|
232
|
+
{ movie_id: 'mv002', title: 'The Matrix' },
|
|
233
|
+
];
|
|
234
|
+
|
|
235
|
+
await client.index('movies').addDocuments(documents, { primaryKey: 'movie_id' });
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
### Updating Documents
|
|
239
|
+
|
|
240
|
+
Update existing documents (replaces entire document):
|
|
241
|
+
|
|
242
|
+
```javascript
|
|
243
|
+
const documents = [
|
|
244
|
+
{ id: 1, title: 'Inception', genre: 'Sci-Fi', year: 2010, rating: 8.8 },
|
|
245
|
+
];
|
|
246
|
+
|
|
247
|
+
await client.index('movies').updateDocuments(documents);
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
### Getting a Document
|
|
251
|
+
|
|
252
|
+
```javascript
|
|
253
|
+
const document = await client.index('movies').getDocument(1);
|
|
254
|
+
console.log(document.title);
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Getting Documents with Pagination
|
|
258
|
+
|
|
259
|
+
```javascript
|
|
260
|
+
const documents = await client.index('movies').getDocuments({
|
|
261
|
+
offset: 0,
|
|
262
|
+
limit: 20,
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
console.log(documents.results);
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### Getting Documents with Field Selection
|
|
269
|
+
|
|
270
|
+
```javascript
|
|
271
|
+
const documents = await client.index('movies').getDocuments({
|
|
272
|
+
fields: ['id', 'title', 'year'],
|
|
273
|
+
limit: 10,
|
|
274
|
+
});
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Deleting a Single Document
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
await client.index('movies').deleteDocument(1);
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Deleting Multiple Documents
|
|
284
|
+
|
|
285
|
+
```javascript
|
|
286
|
+
await client.index('movies').deleteDocuments([1, 2, 3]);
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### Deleting Documents by Filter
|
|
290
|
+
|
|
291
|
+
```javascript
|
|
292
|
+
await client.index('movies').deleteDocuments({
|
|
293
|
+
filter: 'year < 2000',
|
|
294
|
+
});
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
### Deleting All Documents
|
|
298
|
+
|
|
299
|
+
```javascript
|
|
300
|
+
await client.index('movies').deleteAllDocuments();
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
## Search
|
|
304
|
+
|
|
305
|
+
The core functionality of Meilisearch is search.
|
|
306
|
+
|
|
307
|
+
### Basic Search
|
|
308
|
+
|
|
309
|
+
```javascript
|
|
310
|
+
const results = await client.index('movies').search('inception');
|
|
311
|
+
|
|
312
|
+
console.log(results.hits); // Array of matching documents
|
|
313
|
+
console.log(results.query); // The search query
|
|
314
|
+
console.log(results.processingTimeMs); // Search duration
|
|
315
|
+
console.log(results.estimatedTotalHits); // Number of matches
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
### Search with Options
|
|
319
|
+
|
|
320
|
+
```javascript
|
|
321
|
+
const results = await client.index('movies').search('batman', {
|
|
322
|
+
limit: 10,
|
|
323
|
+
offset: 0,
|
|
324
|
+
attributesToRetrieve: ['id', 'title', 'year'],
|
|
325
|
+
attributesToHighlight: ['title'],
|
|
326
|
+
attributesToCrop: ['overview'],
|
|
327
|
+
cropLength: 20,
|
|
328
|
+
filter: 'year > 2000',
|
|
329
|
+
sort: ['year:desc'],
|
|
330
|
+
matchingStrategy: 'last',
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
console.log(results.hits);
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
### Filtering
|
|
337
|
+
|
|
338
|
+
Configure filterable attributes first:
|
|
339
|
+
|
|
340
|
+
```javascript
|
|
341
|
+
await client.index('movies').updateFilterableAttributes([
|
|
342
|
+
'genre',
|
|
343
|
+
'year',
|
|
344
|
+
'rating',
|
|
345
|
+
]);
|
|
346
|
+
```
|
|
347
|
+
|
|
348
|
+
Then use filters in search:
|
|
349
|
+
|
|
350
|
+
```javascript
|
|
351
|
+
// Exact match
|
|
352
|
+
const sciFi = await client.index('movies').search('space', {
|
|
353
|
+
filter: 'genre = "Sci-Fi"',
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
// Numeric comparison
|
|
357
|
+
const recent = await client.index('movies').search('', {
|
|
358
|
+
filter: 'year > 2015',
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
// Range filter
|
|
362
|
+
const highRated = await client.index('movies').search('', {
|
|
363
|
+
filter: 'rating 8 TO 10',
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
// Multiple filters with AND
|
|
367
|
+
const filtered = await client.index('movies').search('', {
|
|
368
|
+
filter: 'genre = "Action" AND year > 2010',
|
|
369
|
+
});
|
|
370
|
+
|
|
371
|
+
// Multiple filters with OR
|
|
372
|
+
const multiGenre = await client.index('movies').search('', {
|
|
373
|
+
filter: 'genre = "Action" OR genre = "Sci-Fi"',
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// Array syntax for AND
|
|
377
|
+
const arrayFilter = await client.index('movies').search('', {
|
|
378
|
+
filter: [
|
|
379
|
+
'genre = "Action"',
|
|
380
|
+
'year > 2010',
|
|
381
|
+
],
|
|
382
|
+
});
|
|
383
|
+
|
|
384
|
+
// Nested array syntax for OR within AND
|
|
385
|
+
const complexFilter = await client.index('movies').search('', {
|
|
386
|
+
filter: [
|
|
387
|
+
['genre = "Action"', 'genre = "Sci-Fi"'],
|
|
388
|
+
'year > 2010',
|
|
389
|
+
],
|
|
390
|
+
});
|
|
391
|
+
```
|
|
392
|
+
|
|
393
|
+
### Sorting
|
|
394
|
+
|
|
395
|
+
Configure sortable attributes first:
|
|
396
|
+
|
|
397
|
+
```javascript
|
|
398
|
+
await client.index('movies').updateSortableAttributes([
|
|
399
|
+
'year',
|
|
400
|
+
'rating',
|
|
401
|
+
'title',
|
|
402
|
+
]);
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
Then use sort in search:
|
|
406
|
+
|
|
407
|
+
```javascript
|
|
408
|
+
// Sort by single attribute
|
|
409
|
+
const byYear = await client.index('movies').search('', {
|
|
410
|
+
sort: ['year:desc'],
|
|
411
|
+
});
|
|
412
|
+
|
|
413
|
+
// Sort by multiple attributes
|
|
414
|
+
const multiSort = await client.index('movies').search('', {
|
|
415
|
+
sort: ['rating:desc', 'year:desc'],
|
|
416
|
+
});
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Pagination
|
|
420
|
+
|
|
421
|
+
```javascript
|
|
422
|
+
const page1 = await client.index('movies').search('action', {
|
|
423
|
+
limit: 20,
|
|
424
|
+
offset: 0,
|
|
425
|
+
});
|
|
426
|
+
|
|
427
|
+
const page2 = await client.index('movies').search('action', {
|
|
428
|
+
limit: 20,
|
|
429
|
+
offset: 20,
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
// Using hitsPerPage and page (alternative pagination)
|
|
433
|
+
const results = await client.index('movies').search('action', {
|
|
434
|
+
hitsPerPage: 20,
|
|
435
|
+
page: 1,
|
|
436
|
+
});
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
### Faceted Search
|
|
440
|
+
|
|
441
|
+
Configure facets:
|
|
442
|
+
|
|
443
|
+
```javascript
|
|
444
|
+
await client.index('movies').updateFilterableAttributes([
|
|
445
|
+
'genre',
|
|
446
|
+
'year',
|
|
447
|
+
'director',
|
|
448
|
+
]);
|
|
449
|
+
```
|
|
450
|
+
|
|
451
|
+
Search with facets:
|
|
452
|
+
|
|
453
|
+
```javascript
|
|
454
|
+
const results = await client.index('movies').search('classic', {
|
|
455
|
+
facets: ['genre', 'year', 'director'],
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
console.log(results.facetDistribution);
|
|
459
|
+
// {
|
|
460
|
+
// genre: { 'Sci-Fi': 42, 'Action': 38, 'Drama': 25 },
|
|
461
|
+
// year: { '2020': 10, '2019': 15, '2018': 12 },
|
|
462
|
+
// director: { 'Nolan': 5, 'Spielberg': 8 }
|
|
463
|
+
// }
|
|
464
|
+
|
|
465
|
+
console.log(results.facetStats);
|
|
466
|
+
// { year: { min: 1980, max: 2024 } }
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### Facet Search
|
|
470
|
+
|
|
471
|
+
Search within facet values:
|
|
472
|
+
|
|
473
|
+
```javascript
|
|
474
|
+
const results = await client.index('movies').searchForFacetValues({
|
|
475
|
+
facetName: 'genre',
|
|
476
|
+
facetQuery: 'sci',
|
|
477
|
+
filter: 'year > 2010',
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
console.log(results.facetHits);
|
|
481
|
+
// [{ value: 'Sci-Fi', count: 42 }]
|
|
482
|
+
```
|
|
483
|
+
|
|
484
|
+
### Highlighting and Cropping
|
|
485
|
+
|
|
486
|
+
```javascript
|
|
487
|
+
const results = await client.index('movies').search('space exploration', {
|
|
488
|
+
attributesToHighlight: ['title', 'overview'],
|
|
489
|
+
attributesToCrop: ['overview'],
|
|
490
|
+
cropLength: 30,
|
|
491
|
+
cropMarker: '...',
|
|
492
|
+
highlightPreTag: '<mark>',
|
|
493
|
+
highlightPostTag: '</mark>',
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
console.log(results.hits[0]._formatted.title);
|
|
497
|
+
// "The <mark>Space</mark> Between Us"
|
|
498
|
+
console.log(results.hits[0]._formatted.overview);
|
|
499
|
+
// "...astronaut on a mission of <mark>space</mark> <mark>exploration</mark>..."
|
|
500
|
+
```
|
|
501
|
+
|
|
502
|
+
### Geosearch
|
|
503
|
+
|
|
504
|
+
Add documents with `_geo` field:
|
|
505
|
+
|
|
506
|
+
```javascript
|
|
507
|
+
const restaurants = [
|
|
508
|
+
{
|
|
509
|
+
id: 1,
|
|
510
|
+
name: "Joe's Pizza",
|
|
511
|
+
_geo: { lat: 40.7484, lng: -73.9857 },
|
|
512
|
+
},
|
|
513
|
+
{
|
|
514
|
+
id: 2,
|
|
515
|
+
name: "Pasta Palace",
|
|
516
|
+
_geo: { lat: 40.7589, lng: -73.9851 },
|
|
517
|
+
},
|
|
518
|
+
];
|
|
519
|
+
|
|
520
|
+
await client.index('restaurants').addDocuments(restaurants);
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
Configure `_geo` as filterable:
|
|
524
|
+
|
|
525
|
+
```javascript
|
|
526
|
+
await client.index('restaurants').updateFilterableAttributes(['_geo']);
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
Search by geo radius:
|
|
530
|
+
|
|
531
|
+
```javascript
|
|
532
|
+
const results = await client.index('restaurants').search('pizza', {
|
|
533
|
+
filter: '_geoRadius(40.7484, -73.9857, 5000)', // 5km radius
|
|
534
|
+
});
|
|
535
|
+
```
|
|
536
|
+
|
|
537
|
+
Search by geo bounding box:
|
|
538
|
+
|
|
539
|
+
```javascript
|
|
540
|
+
const results = await client.index('restaurants').search('', {
|
|
541
|
+
filter: '_geoBoundingBox([40.7590, -73.9850], [40.7480, -73.9860])',
|
|
542
|
+
});
|
|
543
|
+
```
|
|
544
|
+
|
|
545
|
+
Sort by distance from a point:
|
|
546
|
+
|
|
547
|
+
```javascript
|
|
548
|
+
await client.index('restaurants').updateSortableAttributes(['_geo']);
|
|
549
|
+
|
|
550
|
+
const results = await client.index('restaurants').search('', {
|
|
551
|
+
sort: ['_geoPoint(40.7484, -73.9857):asc'],
|
|
552
|
+
});
|
|
553
|
+
|
|
554
|
+
console.log(results.hits[0]._geoDistance); // Distance in meters
|
|
555
|
+
```
|
|
556
|
+
|
|
557
|
+
## Multi-Search
|
|
558
|
+
|
|
559
|
+
Perform multiple searches in a single request:
|
|
560
|
+
|
|
561
|
+
```javascript
|
|
562
|
+
const results = await client.multiSearch({
|
|
563
|
+
queries: [
|
|
564
|
+
{
|
|
565
|
+
indexUid: 'movies',
|
|
566
|
+
q: 'batman',
|
|
567
|
+
filter: 'year > 2000',
|
|
568
|
+
},
|
|
569
|
+
{
|
|
570
|
+
indexUid: 'movies',
|
|
571
|
+
q: 'superman',
|
|
572
|
+
limit: 5,
|
|
573
|
+
},
|
|
574
|
+
{
|
|
575
|
+
indexUid: 'books',
|
|
576
|
+
q: 'javascript',
|
|
577
|
+
},
|
|
578
|
+
],
|
|
579
|
+
});
|
|
580
|
+
|
|
581
|
+
console.log(results.results[0].hits); // Batman search results
|
|
582
|
+
console.log(results.results[1].hits); // Superman search results
|
|
583
|
+
console.log(results.results[2].hits); // JavaScript books results
|
|
584
|
+
```
|
|
585
|
+
|
|
586
|
+
### Federated Search
|
|
587
|
+
|
|
588
|
+
Merge results from multiple queries into a single list:
|
|
589
|
+
|
|
590
|
+
```javascript
|
|
591
|
+
const results = await client.multiSearch({
|
|
592
|
+
federation: {
|
|
593
|
+
limit: 20,
|
|
594
|
+
offset: 0,
|
|
595
|
+
},
|
|
596
|
+
queries: [
|
|
597
|
+
{
|
|
598
|
+
indexUid: 'movies',
|
|
599
|
+
q: 'batman',
|
|
600
|
+
},
|
|
601
|
+
{
|
|
602
|
+
indexUid: 'comics',
|
|
603
|
+
q: 'batman',
|
|
604
|
+
},
|
|
605
|
+
{
|
|
606
|
+
indexUid: 'books',
|
|
607
|
+
q: 'batman',
|
|
608
|
+
},
|
|
609
|
+
],
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
console.log(results.hits); // Merged results from all three indexes
|
|
613
|
+
console.log(results.facetsByIndex); // Facets grouped by index
|
|
614
|
+
```
|
|
615
|
+
|
|
616
|
+
## Index Settings
|
|
617
|
+
|
|
618
|
+
Settings control how Meilisearch processes and ranks search results.
|
|
619
|
+
|
|
620
|
+
### Get All Settings
|
|
621
|
+
|
|
622
|
+
```javascript
|
|
623
|
+
const settings = await client.index('movies').getSettings();
|
|
624
|
+
console.log(settings);
|
|
625
|
+
```
|
|
626
|
+
|
|
627
|
+
### Update All Settings
|
|
628
|
+
|
|
629
|
+
```javascript
|
|
630
|
+
await client.index('movies').updateSettings({
|
|
631
|
+
searchableAttributes: ['title', 'overview', 'genre'],
|
|
632
|
+
filterableAttributes: ['genre', 'year', 'rating'],
|
|
633
|
+
sortableAttributes: ['year', 'rating'],
|
|
634
|
+
rankingRules: [
|
|
635
|
+
'words',
|
|
636
|
+
'typo',
|
|
637
|
+
'proximity',
|
|
638
|
+
'attribute',
|
|
639
|
+
'sort',
|
|
640
|
+
'exactness',
|
|
641
|
+
],
|
|
642
|
+
stopWords: ['the', 'a', 'an'],
|
|
643
|
+
synonyms: {
|
|
644
|
+
'wolverine': ['logan', 'weapon x'],
|
|
645
|
+
'batman': ['dark knight', 'caped crusader'],
|
|
646
|
+
},
|
|
647
|
+
distinctAttribute: 'movie_id',
|
|
648
|
+
typoTolerance: {
|
|
649
|
+
enabled: true,
|
|
650
|
+
minWordSizeForTypos: {
|
|
651
|
+
oneTypo: 5,
|
|
652
|
+
twoTypos: 9,
|
|
653
|
+
},
|
|
654
|
+
},
|
|
655
|
+
pagination: {
|
|
656
|
+
maxTotalHits: 1000,
|
|
657
|
+
},
|
|
658
|
+
});
|
|
659
|
+
```
|
|
660
|
+
|
|
661
|
+
### Reset All Settings
|
|
662
|
+
|
|
663
|
+
```javascript
|
|
664
|
+
await client.index('movies').resetSettings();
|
|
665
|
+
```
|
|
666
|
+
|
|
667
|
+
### Searchable Attributes
|
|
668
|
+
|
|
669
|
+
Defines which fields are searchable and their ranking order:
|
|
670
|
+
|
|
671
|
+
```javascript
|
|
672
|
+
await client.index('movies').updateSearchableAttributes([
|
|
673
|
+
'title',
|
|
674
|
+
'overview',
|
|
675
|
+
'genre',
|
|
676
|
+
]);
|
|
677
|
+
```
|
|
678
|
+
|
|
679
|
+
Get searchable attributes:
|
|
680
|
+
|
|
681
|
+
```javascript
|
|
682
|
+
const attrs = await client.index('movies').getSearchableAttributes();
|
|
683
|
+
```
|
|
684
|
+
|
|
685
|
+
Reset to default (all attributes):
|
|
686
|
+
|
|
687
|
+
```javascript
|
|
688
|
+
await client.index('movies').resetSearchableAttributes();
|
|
689
|
+
```
|
|
690
|
+
|
|
691
|
+
### Filterable Attributes
|
|
692
|
+
|
|
693
|
+
Defines which fields can be used in filters and facets:
|
|
694
|
+
|
|
695
|
+
```javascript
|
|
696
|
+
await client.index('movies').updateFilterableAttributes([
|
|
697
|
+
'genre',
|
|
698
|
+
'year',
|
|
699
|
+
'rating',
|
|
700
|
+
'director',
|
|
701
|
+
]);
|
|
702
|
+
```
|
|
703
|
+
|
|
704
|
+
### Sortable Attributes
|
|
705
|
+
|
|
706
|
+
Defines which fields can be used for sorting:
|
|
707
|
+
|
|
708
|
+
```javascript
|
|
709
|
+
await client.index('movies').updateSortableAttributes([
|
|
710
|
+
'year',
|
|
711
|
+
'rating',
|
|
712
|
+
'title',
|
|
713
|
+
]);
|
|
714
|
+
```
|
|
715
|
+
|
|
716
|
+
### Ranking Rules
|
|
717
|
+
|
|
718
|
+
Controls how results are ranked:
|
|
719
|
+
|
|
720
|
+
```javascript
|
|
721
|
+
await client.index('movies').updateRankingRules([
|
|
722
|
+
'words',
|
|
723
|
+
'typo',
|
|
724
|
+
'proximity',
|
|
725
|
+
'attribute',
|
|
726
|
+
'sort',
|
|
727
|
+
'exactness',
|
|
728
|
+
'rating:desc', // Custom ranking rule
|
|
729
|
+
]);
|
|
730
|
+
```
|
|
731
|
+
|
|
732
|
+
Get ranking rules:
|
|
733
|
+
|
|
734
|
+
```javascript
|
|
735
|
+
const rules = await client.index('movies').getRankingRules();
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
Reset to default:
|
|
739
|
+
|
|
740
|
+
```javascript
|
|
741
|
+
await client.index('movies').resetRankingRules();
|
|
742
|
+
```
|
|
743
|
+
|
|
744
|
+
### Stop Words
|
|
745
|
+
|
|
746
|
+
Words ignored during search:
|
|
747
|
+
|
|
748
|
+
```javascript
|
|
749
|
+
await client.index('movies').updateStopWords([
|
|
750
|
+
'the',
|
|
751
|
+
'a',
|
|
752
|
+
'an',
|
|
753
|
+
'and',
|
|
754
|
+
'or',
|
|
755
|
+
'but',
|
|
756
|
+
]);
|
|
757
|
+
```
|
|
758
|
+
|
|
759
|
+
### Synonyms
|
|
760
|
+
|
|
761
|
+
Configure word equivalences:
|
|
762
|
+
|
|
763
|
+
```javascript
|
|
764
|
+
await client.index('movies').updateSynonyms({
|
|
765
|
+
'wolverine': ['logan', 'weapon x'],
|
|
766
|
+
'batman': ['dark knight', 'caped crusader'],
|
|
767
|
+
'sw': ['star wars'],
|
|
768
|
+
});
|
|
769
|
+
```
|
|
770
|
+
|
|
771
|
+
### Distinct Attribute
|
|
772
|
+
|
|
773
|
+
Return only one document per distinct value:
|
|
774
|
+
|
|
775
|
+
```javascript
|
|
776
|
+
await client.index('movies').updateDistinctAttribute('movie_id');
|
|
777
|
+
```
|
|
778
|
+
|
|
779
|
+
### Typo Tolerance
|
|
780
|
+
|
|
781
|
+
Configure typo tolerance settings:
|
|
782
|
+
|
|
783
|
+
```javascript
|
|
784
|
+
await client.index('movies').updateTypoTolerance({
|
|
785
|
+
enabled: true,
|
|
786
|
+
minWordSizeForTypos: {
|
|
787
|
+
oneTypo: 5,
|
|
788
|
+
twoTypos: 9,
|
|
789
|
+
},
|
|
790
|
+
disableOnWords: ['spiderman', 'batman'],
|
|
791
|
+
disableOnAttributes: ['title'],
|
|
792
|
+
});
|
|
793
|
+
```
|
|
794
|
+
|
|
795
|
+
Get typo tolerance settings:
|
|
796
|
+
|
|
797
|
+
```javascript
|
|
798
|
+
const settings = await client.index('movies').getTypoTolerance();
|
|
799
|
+
```
|
|
800
|
+
|
|
801
|
+
### Faceting Settings
|
|
802
|
+
|
|
803
|
+
Configure faceting behavior:
|
|
804
|
+
|
|
805
|
+
```javascript
|
|
806
|
+
await client.index('movies').updateFaceting({
|
|
807
|
+
maxValuesPerFacet: 100,
|
|
808
|
+
sortFacetValuesBy: {
|
|
809
|
+
'*': 'alpha',
|
|
810
|
+
'genre': 'count',
|
|
811
|
+
},
|
|
812
|
+
});
|
|
813
|
+
```
|
|
814
|
+
|
|
815
|
+
### Pagination Settings
|
|
816
|
+
|
|
817
|
+
Configure maximum search results:
|
|
818
|
+
|
|
819
|
+
```javascript
|
|
820
|
+
await client.index('movies').updatePagination({
|
|
821
|
+
maxTotalHits: 1000,
|
|
822
|
+
});
|
|
823
|
+
```
|
|
824
|
+
|
|
825
|
+
### Displayed Attributes
|
|
826
|
+
|
|
827
|
+
Control which fields are returned in search results:
|
|
828
|
+
|
|
829
|
+
```javascript
|
|
830
|
+
await client.index('movies').updateDisplayedAttributes([
|
|
831
|
+
'id',
|
|
832
|
+
'title',
|
|
833
|
+
'year',
|
|
834
|
+
'genre',
|
|
835
|
+
'rating',
|
|
836
|
+
]);
|
|
837
|
+
```
|
|
838
|
+
|
|
839
|
+
## Tasks
|
|
840
|
+
|
|
841
|
+
All operations in Meilisearch are asynchronous and return a task.
|
|
842
|
+
|
|
843
|
+
### Get Task by UID
|
|
844
|
+
|
|
845
|
+
```javascript
|
|
846
|
+
const task = await client.getTask(12);
|
|
847
|
+
|
|
848
|
+
console.log(task.status); // 'enqueued', 'processing', 'succeeded', 'failed', 'canceled'
|
|
849
|
+
console.log(task.type); // 'documentAdditionOrUpdate', 'settingsUpdate', etc.
|
|
850
|
+
console.log(task.details);
|
|
851
|
+
console.log(task.duration);
|
|
852
|
+
```
|
|
853
|
+
|
|
854
|
+
### Get Tasks
|
|
855
|
+
|
|
856
|
+
```javascript
|
|
857
|
+
const tasks = await client.getTasks({
|
|
858
|
+
limit: 20,
|
|
859
|
+
from: 0,
|
|
860
|
+
});
|
|
861
|
+
|
|
862
|
+
console.log(tasks.results);
|
|
863
|
+
```
|
|
864
|
+
|
|
865
|
+
### Get Tasks with Filters
|
|
866
|
+
|
|
867
|
+
```javascript
|
|
868
|
+
const tasks = await client.getTasks({
|
|
869
|
+
indexUids: ['movies'],
|
|
870
|
+
statuses: ['succeeded', 'failed'],
|
|
871
|
+
types: ['documentAdditionOrUpdate'],
|
|
872
|
+
limit: 50,
|
|
873
|
+
});
|
|
874
|
+
```
|
|
875
|
+
|
|
876
|
+
### Wait for Task
|
|
877
|
+
|
|
878
|
+
```javascript
|
|
879
|
+
const response = await client.index('movies').addDocuments(documents);
|
|
880
|
+
|
|
881
|
+
// Wait for the task to complete
|
|
882
|
+
const task = await client.waitForTask(response.taskUid);
|
|
883
|
+
|
|
884
|
+
if (task.status === 'succeeded') {
|
|
885
|
+
console.log('Documents added successfully');
|
|
886
|
+
} else if (task.status === 'failed') {
|
|
887
|
+
console.error('Task failed:', task.error);
|
|
888
|
+
}
|
|
889
|
+
```
|
|
890
|
+
|
|
891
|
+
### Wait for Multiple Tasks
|
|
892
|
+
|
|
893
|
+
```javascript
|
|
894
|
+
await client.waitForTasks([taskUid1, taskUid2, taskUid3]);
|
|
895
|
+
```
|
|
896
|
+
|
|
897
|
+
### Cancel Tasks
|
|
898
|
+
|
|
899
|
+
```javascript
|
|
900
|
+
await client.cancelTasks({
|
|
901
|
+
uids: [12, 13, 14],
|
|
902
|
+
});
|
|
903
|
+
|
|
904
|
+
// Cancel by filter
|
|
905
|
+
await client.cancelTasks({
|
|
906
|
+
statuses: ['enqueued'],
|
|
907
|
+
indexUids: ['movies'],
|
|
908
|
+
});
|
|
909
|
+
```
|
|
910
|
+
|
|
911
|
+
### Delete Tasks
|
|
912
|
+
|
|
913
|
+
```javascript
|
|
914
|
+
await client.deleteTasks({
|
|
915
|
+
uids: [12, 13, 14],
|
|
916
|
+
});
|
|
917
|
+
|
|
918
|
+
// Delete by filter
|
|
919
|
+
await client.deleteTasks({
|
|
920
|
+
statuses: ['succeeded', 'failed'],
|
|
921
|
+
beforeEnqueuedAt: new Date('2024-01-01'),
|
|
922
|
+
});
|
|
923
|
+
```
|
|
924
|
+
|
|
925
|
+
## Tenant Tokens
|
|
926
|
+
|
|
927
|
+
Tenant tokens provide secure, scoped access for multi-tenant applications.
|
|
928
|
+
|
|
929
|
+
### Generating Tenant Tokens
|
|
930
|
+
|
|
931
|
+
```javascript
|
|
932
|
+
import { MeiliSearch } from 'meilisearch';
|
|
933
|
+
|
|
934
|
+
// Server-side code
|
|
935
|
+
const client = new MeiliSearch({
|
|
936
|
+
host: 'http://127.0.0.1:7700',
|
|
937
|
+
apiKey: 'YOUR_MASTER_KEY',
|
|
938
|
+
});
|
|
939
|
+
|
|
940
|
+
const apiKey = await client.getKey('YOUR_API_KEY_UID');
|
|
941
|
+
|
|
942
|
+
const searchRules = {
|
|
943
|
+
'patient_medical_records': {
|
|
944
|
+
filter: 'user_id = 42',
|
|
945
|
+
},
|
|
946
|
+
};
|
|
947
|
+
|
|
948
|
+
const token = await client.generateTenantToken(
|
|
949
|
+
apiKey.uid,
|
|
950
|
+
searchRules,
|
|
951
|
+
{
|
|
952
|
+
apiKey: apiKey.key,
|
|
953
|
+
expiresAt: new Date('2025-12-31'),
|
|
954
|
+
}
|
|
955
|
+
);
|
|
956
|
+
|
|
957
|
+
// Send this token to the frontend
|
|
958
|
+
```
|
|
959
|
+
|
|
960
|
+
### Using Tenant Tokens
|
|
961
|
+
|
|
962
|
+
```javascript
|
|
963
|
+
// Frontend code
|
|
964
|
+
const frontendClient = new MeiliSearch({
|
|
965
|
+
host: 'http://127.0.0.1:7700',
|
|
966
|
+
apiKey: tenantToken, // Token from backend
|
|
967
|
+
});
|
|
968
|
+
|
|
969
|
+
const results = await frontendClient.index('patient_medical_records').search('blood test');
|
|
970
|
+
// Only returns documents where user_id = 42
|
|
971
|
+
```
|
|
972
|
+
|
|
973
|
+
### Multi-Index Tenant Tokens
|
|
974
|
+
|
|
975
|
+
```javascript
|
|
976
|
+
const searchRules = {
|
|
977
|
+
'products': {
|
|
978
|
+
filter: 'company_id = 123',
|
|
979
|
+
},
|
|
980
|
+
'orders': {
|
|
981
|
+
filter: 'company_id = 123',
|
|
982
|
+
},
|
|
983
|
+
'invoices': {
|
|
984
|
+
filter: 'company_id = 123',
|
|
985
|
+
},
|
|
986
|
+
};
|
|
987
|
+
|
|
988
|
+
const token = await client.generateTenantToken(
|
|
989
|
+
apiKeyUid,
|
|
990
|
+
searchRules,
|
|
991
|
+
{ apiKey: apiKey.key }
|
|
992
|
+
);
|
|
993
|
+
```
|
|
994
|
+
|
|
995
|
+
### Wildcard Tenant Tokens
|
|
996
|
+
|
|
997
|
+
```javascript
|
|
998
|
+
const searchRules = {
|
|
999
|
+
'*': {
|
|
1000
|
+
filter: 'tenant_id = 456',
|
|
1001
|
+
},
|
|
1002
|
+
};
|
|
1003
|
+
|
|
1004
|
+
const token = await client.generateTenantToken(
|
|
1005
|
+
apiKeyUid,
|
|
1006
|
+
searchRules,
|
|
1007
|
+
{ apiKey: apiKey.key }
|
|
1008
|
+
);
|
|
1009
|
+
```
|
|
1010
|
+
|
|
1011
|
+
## Health and Stats
|
|
1012
|
+
|
|
1013
|
+
### Check Health
|
|
1014
|
+
|
|
1015
|
+
```javascript
|
|
1016
|
+
const health = await client.health();
|
|
1017
|
+
console.log(health.status); // 'available'
|
|
1018
|
+
```
|
|
1019
|
+
|
|
1020
|
+
### Get Version
|
|
1021
|
+
|
|
1022
|
+
```javascript
|
|
1023
|
+
const version = await client.getVersion();
|
|
1024
|
+
console.log(version.pkgVersion); // '1.12.0'
|
|
1025
|
+
```
|
|
1026
|
+
|
|
1027
|
+
### Get Database Stats
|
|
1028
|
+
|
|
1029
|
+
```javascript
|
|
1030
|
+
const stats = await client.getStats();
|
|
1031
|
+
console.log(stats.databaseSize);
|
|
1032
|
+
console.log(stats.lastUpdate);
|
|
1033
|
+
console.log(stats.indexes);
|
|
1034
|
+
```
|
|
1035
|
+
|
|
1036
|
+
## Error Handling
|
|
1037
|
+
|
|
1038
|
+
```javascript
|
|
1039
|
+
try {
|
|
1040
|
+
const results = await client.index('movies').search('batman');
|
|
1041
|
+
console.log(results.hits);
|
|
1042
|
+
} catch (error) {
|
|
1043
|
+
if (error.message.includes('index_not_found')) {
|
|
1044
|
+
console.error('Index does not exist');
|
|
1045
|
+
} else if (error.message.includes('invalid_api_key')) {
|
|
1046
|
+
console.error('Invalid API key');
|
|
1047
|
+
} else {
|
|
1048
|
+
console.error('Search error:', error.message);
|
|
1049
|
+
}
|
|
1050
|
+
}
|
|
1051
|
+
```
|
|
1052
|
+
|
|
1053
|
+
### Handling Task Failures
|
|
1054
|
+
|
|
1055
|
+
```javascript
|
|
1056
|
+
const response = await client.index('movies').addDocuments(documents);
|
|
1057
|
+
const task = await client.waitForTask(response.taskUid);
|
|
1058
|
+
|
|
1059
|
+
if (task.status === 'failed') {
|
|
1060
|
+
console.error('Error code:', task.error.code);
|
|
1061
|
+
console.error('Error type:', task.error.type);
|
|
1062
|
+
console.error('Error message:', task.error.message);
|
|
1063
|
+
console.error('Error link:', task.error.link);
|
|
1064
|
+
}
|
|
1065
|
+
```
|
|
1066
|
+
|
|
1067
|
+
## Advanced Examples
|
|
1068
|
+
|
|
1069
|
+
### Full-Text Search with Filters and Sorting
|
|
1070
|
+
|
|
1071
|
+
```javascript
|
|
1072
|
+
await client.index('movies').updateSettings({
|
|
1073
|
+
searchableAttributes: ['title', 'overview', 'genre'],
|
|
1074
|
+
filterableAttributes: ['genre', 'year', 'rating', 'director'],
|
|
1075
|
+
sortableAttributes: ['year', 'rating'],
|
|
1076
|
+
rankingRules: ['words', 'typo', 'proximity', 'attribute', 'sort', 'exactness'],
|
|
1077
|
+
});
|
|
1078
|
+
|
|
1079
|
+
const results = await client.index('movies').search('space adventure', {
|
|
1080
|
+
filter: [
|
|
1081
|
+
['genre = "Sci-Fi"', 'genre = "Adventure"'],
|
|
1082
|
+
'year > 2010',
|
|
1083
|
+
'rating >= 7.0',
|
|
1084
|
+
],
|
|
1085
|
+
sort: ['rating:desc', 'year:desc'],
|
|
1086
|
+
attributesToRetrieve: ['title', 'year', 'rating', 'overview'],
|
|
1087
|
+
attributesToHighlight: ['title', 'overview'],
|
|
1088
|
+
attributesToCrop: ['overview'],
|
|
1089
|
+
cropLength: 50,
|
|
1090
|
+
limit: 20,
|
|
1091
|
+
});
|
|
1092
|
+
```
|
|
1093
|
+
|
|
1094
|
+
### E-commerce Product Search
|
|
1095
|
+
|
|
1096
|
+
```javascript
|
|
1097
|
+
const products = [
|
|
1098
|
+
{
|
|
1099
|
+
id: 1,
|
|
1100
|
+
name: 'Laptop Pro 15',
|
|
1101
|
+
category: 'Electronics',
|
|
1102
|
+
brand: 'TechCorp',
|
|
1103
|
+
price: 1299.99,
|
|
1104
|
+
rating: 4.5,
|
|
1105
|
+
inStock: true,
|
|
1106
|
+
},
|
|
1107
|
+
{
|
|
1108
|
+
id: 2,
|
|
1109
|
+
name: 'Wireless Mouse',
|
|
1110
|
+
category: 'Electronics',
|
|
1111
|
+
brand: 'TechCorp',
|
|
1112
|
+
price: 29.99,
|
|
1113
|
+
rating: 4.8,
|
|
1114
|
+
inStock: true,
|
|
1115
|
+
},
|
|
1116
|
+
];
|
|
1117
|
+
|
|
1118
|
+
await client.index('products').addDocuments(products);
|
|
1119
|
+
|
|
1120
|
+
await client.index('products').updateSettings({
|
|
1121
|
+
filterableAttributes: ['category', 'brand', 'price', 'rating', 'inStock'],
|
|
1122
|
+
sortableAttributes: ['price', 'rating'],
|
|
1123
|
+
rankingRules: ['words', 'typo', 'proximity', 'attribute', 'sort', 'exactness'],
|
|
1124
|
+
});
|
|
1125
|
+
|
|
1126
|
+
const results = await client.index('products').search('laptop', {
|
|
1127
|
+
filter: [
|
|
1128
|
+
'category = "Electronics"',
|
|
1129
|
+
'inStock = true',
|
|
1130
|
+
'price 0 TO 1500',
|
|
1131
|
+
],
|
|
1132
|
+
sort: ['rating:desc'],
|
|
1133
|
+
facets: ['brand', 'category'],
|
|
1134
|
+
limit: 20,
|
|
1135
|
+
});
|
|
1136
|
+
```
|
|
1137
|
+
|
|
1138
|
+
### Location-Based Restaurant Search
|
|
1139
|
+
|
|
1140
|
+
```javascript
|
|
1141
|
+
const restaurants = [
|
|
1142
|
+
{
|
|
1143
|
+
id: 1,
|
|
1144
|
+
name: 'Italian Bistro',
|
|
1145
|
+
cuisine: 'Italian',
|
|
1146
|
+
rating: 4.5,
|
|
1147
|
+
priceRange: '$$',
|
|
1148
|
+
_geo: { lat: 40.7589, lng: -73.9851 },
|
|
1149
|
+
},
|
|
1150
|
+
{
|
|
1151
|
+
id: 2,
|
|
1152
|
+
name: 'Sushi Palace',
|
|
1153
|
+
cuisine: 'Japanese',
|
|
1154
|
+
rating: 4.8,
|
|
1155
|
+
priceRange: '$$$',
|
|
1156
|
+
_geo: { lat: 40.7484, lng: -73.9857 },
|
|
1157
|
+
},
|
|
1158
|
+
];
|
|
1159
|
+
|
|
1160
|
+
await client.index('restaurants').addDocuments(restaurants);
|
|
1161
|
+
|
|
1162
|
+
await client.index('restaurants').updateSettings({
|
|
1163
|
+
filterableAttributes: ['cuisine', 'rating', 'priceRange', '_geo'],
|
|
1164
|
+
sortableAttributes: ['rating', '_geo'],
|
|
1165
|
+
});
|
|
1166
|
+
|
|
1167
|
+
const results = await client.index('restaurants').search('', {
|
|
1168
|
+
filter: [
|
|
1169
|
+
'_geoRadius(40.7580, -73.9855, 2000)', // 2km radius
|
|
1170
|
+
'rating >= 4.0',
|
|
1171
|
+
],
|
|
1172
|
+
sort: ['_geoPoint(40.7580, -73.9855):asc'],
|
|
1173
|
+
limit: 10,
|
|
1174
|
+
});
|
|
1175
|
+
|
|
1176
|
+
results.hits.forEach(hit => {
|
|
1177
|
+
console.log(`${hit.name} - ${hit._geoDistance}m away`);
|
|
1178
|
+
});
|
|
1179
|
+
```
|
|
1180
|
+
|
|
1181
|
+
### Real-Time Search with Autocomplete
|
|
1182
|
+
|
|
1183
|
+
```javascript
|
|
1184
|
+
let timeoutId;
|
|
1185
|
+
|
|
1186
|
+
function handleSearchInput(query) {
|
|
1187
|
+
clearTimeout(timeoutId);
|
|
1188
|
+
|
|
1189
|
+
timeoutId = setTimeout(async () => {
|
|
1190
|
+
const results = await client.index('movies').search(query, {
|
|
1191
|
+
limit: 5,
|
|
1192
|
+
attributesToRetrieve: ['id', 'title', 'year'],
|
|
1193
|
+
attributesToHighlight: ['title'],
|
|
1194
|
+
});
|
|
1195
|
+
|
|
1196
|
+
displayResults(results.hits);
|
|
1197
|
+
}, 300); // Debounce for 300ms
|
|
1198
|
+
}
|
|
1199
|
+
```
|
|
1200
|
+
|
|
1201
|
+
### Bulk Document Operations
|
|
1202
|
+
|
|
1203
|
+
```javascript
|
|
1204
|
+
// Process large datasets in batches
|
|
1205
|
+
const batchSize = 1000;
|
|
1206
|
+
const allDocuments = [...]; // Your large dataset
|
|
1207
|
+
|
|
1208
|
+
for (let i = 0; i < allDocuments.length; i += batchSize) {
|
|
1209
|
+
const batch = allDocuments.slice(i, i + batchSize);
|
|
1210
|
+
const response = await client.index('movies').addDocuments(batch);
|
|
1211
|
+
await client.waitForTask(response.taskUid);
|
|
1212
|
+
console.log(`Processed ${i + batch.length} documents`);
|
|
1213
|
+
}
|
|
1214
|
+
```
|
|
1215
|
+
|
|
1216
|
+
### Search with Custom Ranking
|
|
1217
|
+
|
|
1218
|
+
```javascript
|
|
1219
|
+
await client.index('movies').updateSettings({
|
|
1220
|
+
rankingRules: [
|
|
1221
|
+
'words',
|
|
1222
|
+
'typo',
|
|
1223
|
+
'proximity',
|
|
1224
|
+
'attribute',
|
|
1225
|
+
'sort',
|
|
1226
|
+
'exactness',
|
|
1227
|
+
'rating:desc', // Boost higher-rated content
|
|
1228
|
+
'year:desc', // Prefer newer content
|
|
1229
|
+
],
|
|
1230
|
+
});
|
|
1231
|
+
|
|
1232
|
+
const results = await client.index('movies').search('action');
|
|
1233
|
+
```
|
|
1234
|
+
|
|
1235
|
+
## Useful Links
|
|
1236
|
+
|
|
1237
|
+
- Official Documentation: https://www.meilisearch.com/docs
|
|
1238
|
+
- JavaScript SDK Documentation: https://meilisearch.github.io/meilisearch-js/
|
|
1239
|
+
- API Reference: https://www.meilisearch.com/docs/reference/api/overview
|
|
1240
|
+
- GitHub Repository: https://github.com/meilisearch/meilisearch-js
|
|
1241
|
+
- Cloud Hosting: https://www.meilisearch.com/cloud
|