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.
Files changed (139) hide show
  1. package/README.md +55 -0
  2. package/bin/chub-mcp +2 -0
  3. package/dist/airtable/docs/database/javascript/DOC.md +1437 -0
  4. package/dist/airtable/docs/database/python/DOC.md +1735 -0
  5. package/dist/amplitude/docs/analytics/javascript/DOC.md +1282 -0
  6. package/dist/amplitude/docs/analytics/python/DOC.md +1199 -0
  7. package/dist/anthropic/docs/claude-api/javascript/DOC.md +503 -0
  8. package/dist/anthropic/docs/claude-api/python/DOC.md +389 -0
  9. package/dist/asana/docs/tasks/DOC.md +1396 -0
  10. package/dist/assemblyai/docs/transcription/DOC.md +1043 -0
  11. package/dist/atlassian/docs/confluence/javascript/DOC.md +1347 -0
  12. package/dist/atlassian/docs/confluence/python/DOC.md +1604 -0
  13. package/dist/auth0/docs/identity/javascript/DOC.md +968 -0
  14. package/dist/auth0/docs/identity/python/DOC.md +1199 -0
  15. package/dist/aws/docs/s3/javascript/DOC.md +1773 -0
  16. package/dist/aws/docs/s3/python/DOC.md +1807 -0
  17. package/dist/binance/docs/trading/javascript/DOC.md +1315 -0
  18. package/dist/binance/docs/trading/python/DOC.md +1454 -0
  19. package/dist/braintree/docs/gateway/javascript/DOC.md +1278 -0
  20. package/dist/braintree/docs/gateway/python/DOC.md +1179 -0
  21. package/dist/chromadb/docs/embeddings-db/javascript/DOC.md +1263 -0
  22. package/dist/chromadb/docs/embeddings-db/python/DOC.md +1707 -0
  23. package/dist/clerk/docs/auth/javascript/DOC.md +1220 -0
  24. package/dist/clerk/docs/auth/python/DOC.md +274 -0
  25. package/dist/cloudflare/docs/workers/javascript/DOC.md +918 -0
  26. package/dist/cloudflare/docs/workers/python/DOC.md +994 -0
  27. package/dist/cockroachdb/docs/distributed-db/DOC.md +1500 -0
  28. package/dist/cohere/docs/llm/DOC.md +1335 -0
  29. package/dist/datadog/docs/monitoring/javascript/DOC.md +1740 -0
  30. package/dist/datadog/docs/monitoring/python/DOC.md +1815 -0
  31. package/dist/deepgram/docs/speech/javascript/DOC.md +885 -0
  32. package/dist/deepgram/docs/speech/python/DOC.md +685 -0
  33. package/dist/deepl/docs/translation/javascript/DOC.md +887 -0
  34. package/dist/deepl/docs/translation/python/DOC.md +944 -0
  35. package/dist/deepseek/docs/llm/DOC.md +1220 -0
  36. package/dist/directus/docs/headless-cms/javascript/DOC.md +1128 -0
  37. package/dist/directus/docs/headless-cms/python/DOC.md +1276 -0
  38. package/dist/discord/docs/bot/javascript/DOC.md +1090 -0
  39. package/dist/discord/docs/bot/python/DOC.md +1130 -0
  40. package/dist/elasticsearch/docs/search/DOC.md +1634 -0
  41. package/dist/elevenlabs/docs/text-to-speech/javascript/DOC.md +336 -0
  42. package/dist/elevenlabs/docs/text-to-speech/python/DOC.md +552 -0
  43. package/dist/firebase/docs/auth/DOC.md +1015 -0
  44. package/dist/gemini/docs/genai/javascript/DOC.md +691 -0
  45. package/dist/gemini/docs/genai/python/DOC.md +555 -0
  46. package/dist/github/docs/octokit/DOC.md +1560 -0
  47. package/dist/google/docs/bigquery/javascript/DOC.md +1688 -0
  48. package/dist/google/docs/bigquery/python/DOC.md +1503 -0
  49. package/dist/hubspot/docs/crm/javascript/DOC.md +1805 -0
  50. package/dist/hubspot/docs/crm/python/DOC.md +2033 -0
  51. package/dist/huggingface/docs/transformers/DOC.md +948 -0
  52. package/dist/intercom/docs/messaging/javascript/DOC.md +1844 -0
  53. package/dist/intercom/docs/messaging/python/DOC.md +1797 -0
  54. package/dist/jira/docs/issues/javascript/DOC.md +1420 -0
  55. package/dist/jira/docs/issues/python/DOC.md +1492 -0
  56. package/dist/kafka/docs/streaming/javascript/DOC.md +1671 -0
  57. package/dist/kafka/docs/streaming/python/DOC.md +1464 -0
  58. package/dist/landingai-ade/docs/api/DOC.md +620 -0
  59. package/dist/landingai-ade/docs/sdk/python/DOC.md +489 -0
  60. package/dist/landingai-ade/docs/sdk/typescript/DOC.md +542 -0
  61. package/dist/landingai-ade/skills/SKILL.md +489 -0
  62. package/dist/launchdarkly/docs/feature-flags/javascript/DOC.md +1191 -0
  63. package/dist/launchdarkly/docs/feature-flags/python/DOC.md +1671 -0
  64. package/dist/linear/docs/tracker/DOC.md +1554 -0
  65. package/dist/livekit/docs/realtime/javascript/DOC.md +303 -0
  66. package/dist/livekit/docs/realtime/python/DOC.md +163 -0
  67. package/dist/mailchimp/docs/marketing/DOC.md +1420 -0
  68. package/dist/meilisearch/docs/search/DOC.md +1241 -0
  69. package/dist/microsoft/docs/onedrive/javascript/DOC.md +1421 -0
  70. package/dist/microsoft/docs/onedrive/python/DOC.md +1549 -0
  71. package/dist/mongodb/docs/atlas/DOC.md +2041 -0
  72. package/dist/notion/docs/workspace-api/javascript/DOC.md +1435 -0
  73. package/dist/notion/docs/workspace-api/python/DOC.md +1400 -0
  74. package/dist/okta/docs/identity/javascript/DOC.md +1171 -0
  75. package/dist/okta/docs/identity/python/DOC.md +1401 -0
  76. package/dist/openai/docs/chat/javascript/DOC.md +407 -0
  77. package/dist/openai/docs/chat/python/DOC.md +568 -0
  78. package/dist/paypal/docs/checkout/DOC.md +278 -0
  79. package/dist/pinecone/docs/sdk/javascript/DOC.md +984 -0
  80. package/dist/pinecone/docs/sdk/python/DOC.md +1395 -0
  81. package/dist/plaid/docs/banking/javascript/DOC.md +1163 -0
  82. package/dist/plaid/docs/banking/python/DOC.md +1203 -0
  83. package/dist/playwright-community/skills/login-flows/SKILL.md +108 -0
  84. package/dist/postmark/docs/transactional-email/DOC.md +1168 -0
  85. package/dist/prisma/docs/orm/javascript/DOC.md +1419 -0
  86. package/dist/prisma/docs/orm/python/DOC.md +1317 -0
  87. package/dist/qdrant/docs/vector-search/javascript/DOC.md +1221 -0
  88. package/dist/qdrant/docs/vector-search/python/DOC.md +1653 -0
  89. package/dist/rabbitmq/docs/message-queue/javascript/DOC.md +1193 -0
  90. package/dist/rabbitmq/docs/message-queue/python/DOC.md +1243 -0
  91. package/dist/razorpay/docs/payments/javascript/DOC.md +1219 -0
  92. package/dist/razorpay/docs/payments/python/DOC.md +1330 -0
  93. package/dist/redis/docs/key-value/javascript/DOC.md +1851 -0
  94. package/dist/redis/docs/key-value/python/DOC.md +2054 -0
  95. package/dist/registry.json +2817 -0
  96. package/dist/replicate/docs/model-hosting/DOC.md +1318 -0
  97. package/dist/resend/docs/email/DOC.md +1271 -0
  98. package/dist/salesforce/docs/crm/javascript/DOC.md +1241 -0
  99. package/dist/salesforce/docs/crm/python/DOC.md +1183 -0
  100. package/dist/search-index.json +1 -0
  101. package/dist/sendgrid/docs/email-api/javascript/DOC.md +371 -0
  102. package/dist/sendgrid/docs/email-api/python/DOC.md +656 -0
  103. package/dist/sentry/docs/error-tracking/javascript/DOC.md +1073 -0
  104. package/dist/sentry/docs/error-tracking/python/DOC.md +1309 -0
  105. package/dist/shopify/docs/storefront/DOC.md +457 -0
  106. package/dist/slack/docs/workspace/javascript/DOC.md +933 -0
  107. package/dist/slack/docs/workspace/python/DOC.md +271 -0
  108. package/dist/square/docs/payments/javascript/DOC.md +1855 -0
  109. package/dist/square/docs/payments/python/DOC.md +1728 -0
  110. package/dist/stripe/docs/api/DOC.md +1727 -0
  111. package/dist/stripe/docs/payments/DOC.md +1726 -0
  112. package/dist/stytch/docs/auth/javascript/DOC.md +1813 -0
  113. package/dist/stytch/docs/auth/python/DOC.md +1962 -0
  114. package/dist/supabase/docs/client/DOC.md +1606 -0
  115. package/dist/twilio/docs/messaging/python/DOC.md +469 -0
  116. package/dist/twilio/docs/messaging/typescript/DOC.md +946 -0
  117. package/dist/vercel/docs/platform/DOC.md +1940 -0
  118. package/dist/weaviate/docs/vector-db/javascript/DOC.md +1268 -0
  119. package/dist/weaviate/docs/vector-db/python/DOC.md +1388 -0
  120. package/dist/zendesk/docs/support/javascript/DOC.md +2150 -0
  121. package/dist/zendesk/docs/support/python/DOC.md +2297 -0
  122. package/package.json +22 -6
  123. package/skills/get-api-docs/SKILL.md +84 -0
  124. package/src/commands/annotate.js +83 -0
  125. package/src/commands/build.js +12 -1
  126. package/src/commands/feedback.js +150 -0
  127. package/src/commands/get.js +83 -42
  128. package/src/commands/search.js +7 -0
  129. package/src/index.js +43 -17
  130. package/src/lib/analytics.js +90 -0
  131. package/src/lib/annotations.js +57 -0
  132. package/src/lib/bm25.js +170 -0
  133. package/src/lib/cache.js +69 -6
  134. package/src/lib/config.js +8 -3
  135. package/src/lib/identity.js +99 -0
  136. package/src/lib/registry.js +103 -20
  137. package/src/lib/telemetry.js +86 -0
  138. package/src/mcp/server.js +177 -0
  139. 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