Skip to content

Latest commit

 

History

History
1107 lines (765 loc) · 33.7 KB

README.md

File metadata and controls

1107 lines (765 loc) · 33.7 KB

Meilisearch-JavaScript

Meilisearch JavaScript

npm version Tests Codecov Prettier License Bors enabled

⚡ The Meilisearch API client written for JavaScript

Meilisearch JavaScript is the Meilisearch API client for JavaScript developers.

Meilisearch is an open-source search engine. Learn more about Meilisearch.

Table of Contents

📖 Documentation

This readme and Meilisearch JS documentation website contains all the information you need to start using this Meilisearch SDK.

For general information on how to use Meilisearch—such as our API reference, tutorials, guides, and in-depth articles—refer to our main documentation website.

⚡ Supercharge your Meilisearch experience

Say goodbye to server deployment and manual updates with Meilisearch Cloud. Get started with a 14-day free trial! No credit card required.

🔧 Installation

We recommend installing meilisearch-js in your project with your package manager of choice.

npm install meilisearch

meilisearch-js officially supports node versions >= 14 and <= 18.

Instead of using a package manager, you may also import the library directly into your HTML via a CDN.

Run Meilisearch

To use one of our SDKs, you must first have a running Meilisearch instance. Consult our documentation for instructions on how to download and launch Meilisearch.

Import

After installing meilisearch-js, you must import it into your application. There are many ways of doing that depending on your development environment.

import syntax

Usage in an ES module environment:

import { MeiliSearch } from 'meilisearch'

const client = new MeiliSearch({
  host: 'http://127.0.0.1:7700',
  apiKey: 'masterKey',
})

<script> tag

Usage in an HTML (or alike) file:

<script src='https://cdn.jsdelivr.net/npm/meilisearch@latest/dist/bundles/meilisearch.umd.js'></script>
<script>
  const client = new MeiliSearch({
    host: 'http://127.0.0.1:7700',
    apiKey: 'masterKey',
  })
</script>

require syntax

Usage in a back-end node.js or another environment supporting CommonJS modules:

const { MeiliSearch } = require('meilisearch')

const client = new MeiliSearch({
  host: 'http://127.0.0.1:7700',
  apiKey: 'masterKey',
})

React Native

To use meilisearch-js with React Native, you must also install react-native-url-polyfill.

Deno

Usage in a Deno environment:

import { MeiliSearch } from "https://esm.sh/meilisearch"

const client = new MeiliSearch({
  host: 'http://127.0.0.1:7700',
  apiKey: 'masterKey',
})

🎬 Getting started

Add documents

const { MeiliSearch } = require('meilisearch')
// Or if you are in a ES environment
import { MeiliSearch } from 'meilisearch'

;(async () => {
  const client = new MeiliSearch({
    host: 'http://127.0.0.1:7700',
    apiKey: 'masterKey',
  })

  // An index is where the documents are stored.
  const index = client.index('movies')

  const documents = [
      { id: 1, title: 'Carol', genres: ['Romance', 'Drama'] },
      { id: 2, title: 'Wonder Woman', genres: ['Action', 'Adventure'] },
      { id: 3, title: 'Life of Pi', genres: ['Adventure', 'Drama'] },
      { id: 4, title: 'Mad Max: Fury Road', genres: ['Adventure', 'Science Fiction'] },
      { id: 5, title: 'Moana', genres: ['Fantasy', 'Action']},
      { id: 6, title: 'Philadelphia', genres: ['Drama'] },
  ]

  // If the index 'movies' does not exist, Meilisearch creates it when you first add the documents.
  let response = await index.addDocuments(documents)

  console.log(response) // => { "uid": 0 }
})()

Tasks such as document addition always return a unique identifier. You can use this identifier taskUid to check the status (enqueued, canceled, processing, succeeded or failed) of a task.

Basic search

// Meilisearch is typo-tolerant:
const search = await index.search('philoudelphia')
console.log(search)

Output:

{
  "hits": [
    {
      "id": "6",
      "title": "Philadelphia",
      "genres": ["Drama"]
    }
  ],
  "offset": 0,
  "limit": 20,
  "estimatedTotalHits": 1,
  "processingTimeMs": 1,
  "query": "philoudelphia"
}

Using search parameters

meilisearch-js supports all search parameters described in our main documentation website.

await index.search(
  'wonder',
  {
    attributesToHighlight: ['*']
  }
)
{
  "hits": [
    {
      "id": 2,
      "title": "Wonder Woman",
      "genres": ["Action", "Adventure"],
      "_formatted": {
        "id": "2",
        "title": "<em>Wonder</em> Woman",
        "genres": ["Action", "Adventure"]
      }
    }
  ],
  "offset": 0,
  "limit": 20,
  "estimatedTotalHits": 1,
  "processingTimeMs": 0,
  "query": "wonder"
}

Custom search with filters

To enable filtering, you must first add your attributes to the filterableAttributes index setting.

await index.updateFilterableAttributes([
    'id',
    'genres'
  ])

You only need to perform this operation once per index.

Note that Meilisearch rebuilds your index whenever you update filterableAttributes. Depending on the size of your dataset, this might take considerable time. You can track the process using the tasks API).

After you configured filterableAttributes, you can use the filter search parameter to refine your search:

await index.search(
  'wonder',
  {
    filter: ['id > 1 AND genres = Action']
  }
)
{
  "hits": [
    {
      "id": 2,
      "title": "Wonder Woman",
      "genres": ["Action","Adventure"]
    }
  ],
  "offset": 0,
  "limit": 20,
  "estimatedTotalHits": 1,
  "processingTimeMs": 0,
  "query": "wonder"
}

Placeholder search

Placeholder search makes it possible to receive hits based on your parameters without having any query (q). For example, in a movies database you can run an empty query to receive all results filtered by genre.

await index.search(
  '',
  {
    filter: ['genres = fantasy'],
    facets: ['genres']
  }
)
{
  "hits": [
    {
      "id": 2,
      "title": "Wonder Woman",
      "genres": ["Action","Adventure"]
    },
    {
      "id": 5,
      "title": "Moana",
      "genres": ["Fantasy","Action"]
    }
  ],
  "offset": 0,
  "limit": 20,
  "estimatedTotalHits": 2,
  "processingTimeMs": 0,
  "query": "",
  "facetDistribution": {
    "genres": {
      "Action": 2,
      "Fantasy": 1,
      "Adventure": 1
    }
  }
}

Note that to enable faceted search on your dataset you need to add genres to the filterableAttributes index setting. For more information on filtering and faceting, consult our documentation settings.

Abortable search

You can abort a pending search request by providing an AbortSignal to the request.

const controller = new AbortController()

index
  .search('wonder', {}, {
    signal: controller.signal,
  })
  .then((response) => {
    /** ... */
  })
  .catch((e) => {
    /** Catch AbortError here. */
  })

controller.abort()

Using Meilisearch behind a proxy

Custom request config

You can provide a custom request configuration. for example, with custom headers.

const client: MeiliSearch = new MeiliSearch({
  host: 'http://localhost:3000/api/meilisearch/proxy',
  requestConfig: {
    headers: {
      Authorization: AUTH_TOKEN
    },
    // OR
    credentials: 'include'
  }
})

Custom http client

You can use your own HTTP client, for example, with axios.

const client: MeiliSearch = new MeiliSearch({
  host: 'http://localhost:3000/api/meilisearch/proxy',
  httpClient: async (url, opts) => {
    const response = await $axios.request({
      url,
      data: opts?.body,
      headers: opts?.headers,
      method: (opts?.method?.toLocaleUpperCase() as Method) ?? 'GET'
    })

    return response.data
  }
})

🤖 Compatibility with Meilisearch

This package guarantees compatibility with version v1.x of Meilisearch, but some features may not be present. Please check the issues for more info.

💡 Learn more

The following sections in our main documentation website may interest you:

This repository also contains more examples.

⚙️ Contributing

We welcome all contributions, big and small! If you want to know more about this SDK's development workflow or want to contribute to the repo, please visit our contributing guidelines for detailed instructions.

📜 API resources

Search

client.index<T>('xxx').search(query: string, options: SearchParams = {}, config?: Partial<Request>): Promise<SearchResponse<T>>
client.index<T>('xxx').searchGet(query: string, options: SearchParams = {}, config?: Partial<Request>): Promise<SearchResponse<T>>

Multi Search

client.multiSearch(queries?: MultiSearchParams, config?: Partial<Request>): Promise<Promise<MultiSearchResponse<T>>>

multiSearch uses the POST method when performing its request to Meilisearch.

Search For Facet Values

client.index<T>('myIndex').searchForFacetValues(params: SearchForFacetValuesParams, config?: Partial<Request>): Promise<SearchForFacetValuesResponse>

Documents

client.index('myIndex').addDocuments(documents: Document<T>[]): Promise<EnqueuedTask>
client.index('myIndex').addDocumentsFromString(documents: string, contentType: ContentType, queryParams: RawDocumentAdditionOptions): Promise<EnqueuedTask>
client.index('myIndex').addDocumentsInBatches(documents: Document<T>[], batchSize = 1000): Promise<EnqueuedTask[]>
client.index('myIndex').updateDocuments(documents: Array<Document<Partial<T>>>): Promise<EnqueuedTask>
client.index('myIndex').updateDocumentsFromString(documents: string, contentType: ContentType, queryParams: RawDocumentAdditionOptions): Promise<EnqueuedTask>
client.index('myIndex').updateDocumentsInBatches(documents: Array<Document<Partial<T>>>, batchSize = 1000): Promise<EnqueuedTask[]>
client.index.getDocuments(parameters: DocumentsQuery = {}): Promise<DocumentsResults<T>>>
client.index('myIndex').getDocument(documentId: string): Promise<Document<T>>
client.index('myIndex').deleteDocument(documentId: string | number): Promise<EnqueuedTask>
client.index('myIndex').deleteDocuments(params: DocumentsDeletionQuery | DocumentsIds): Promise<EnqueuedTask>
client.index('myIndex').deleteAllDocuments(): Promise<Types.EnqueuedTask>

Tasks

client.getTasks(parameters: TasksQuery): Promise<TasksResults>
client.getTask(uid: number): Promise<Task>
client.deleteTasks(parameters: DeleteTasksQuery = {}): Promise<EnqueuedTask>
client.cancelTasks(parameters: CancelTasksQuery = {}): Promise<EnqueuedTask>
client.index('myIndex').getTasks(parameters: TasksQuery): Promise<TasksResults>
client.index('myIndex').getTask(uid: number): Promise<Task>

Wait for one task

Using the client
client.waitForTask(uid: number, { timeOutMs?: number, intervalMs?: number }): Promise<Task>
Using the index
client.index('myIndex').waitForTask(uid: number, { timeOutMs?: number, intervalMs?: number }): Promise<Task>

Wait for multiple tasks

Using the client
client.waitForTasks(uids: number[], { timeOutMs?: number, intervalMs?: number }): Promise<Task[]>
Using the index
client.index('myIndex').waitForTasks(uids: number[], { timeOutMs?: number, intervalMs?: number }): Promise<Task[]>

Indexes

client.getIndexes(parameters: IndexesQuery): Promise<IndexesResults<Index[]>>
client.getRawIndexes(parameters: IndexesQuery): Promise<IndexesResults<IndexObject[]>>
client.createIndex<T>(uid: string, options?: IndexOptions): Promise<EnqueuedTask>

Create a local reference to an index

client.index<T>(uid: string): Index<T>
client.getIndex<T>(uid: string): Promise<Index<T>>
client.getRawIndex(uid: string): Promise<IndexObject>
client.index('myIndex').getRawInfo(): Promise<IndexObject>
Using the client
client.updateIndex(uid: string, options: IndexOptions): Promise<EnqueuedTask>
Using the index object
client.index('myIndex').update(data: IndexOptions): Promise<EnqueuedTask>
Using the client
client.deleteIndex(uid): Promise<void>
Using the index object
client.index('myIndex').delete(): Promise<void>
client.index('myIndex').getStats(): Promise<IndexStats>
Return Index instance with updated information
client.index('myIndex').fetchInfo(): Promise<Index>
Get Primary Key of an Index
client.index('myIndex').fetchPrimaryKey(): Promise<string | undefined>
Swap two indexes
client.swapIndexes(params: SwapIndexesParams): Promise<EnqueuedTask>

Settings

client.index('myIndex').getSettings(): Promise<Settings>
client.index('myIndex').updateSettings(settings: Settings): Promise<EnqueuedTask>
client.index('myIndex').resetSettings(): Promise<EnqueuedTask>

Pagination Settings

client.index('myIndex').getPagination(): Promise<PaginationSettings>
client.index('myIndex').updatePagination(pagination: PaginationSettings): Promise<EnqueuedTask>
client.index('myIndex').resetPagination(): Promise<EnqueuedTask>

Synonyms

client.index('myIndex').getSynonyms(): Promise<Synonyms>
client.index('myIndex').updateSynonyms(synonyms: Synonyms): Promise<EnqueuedTask>
client.index('myIndex').resetSynonyms(): Promise<EnqueuedTask>

Stop words

client.index('myIndex').getStopWords(): Promise<string[]>
client.index('myIndex').updateStopWords(stopWords: string[] | null ): Promise<EnqueuedTask>
client.index('myIndex').resetStopWords(): Promise<EnqueuedTask>

Ranking rules

client.index('myIndex').getRankingRules(): Promise<string[]>
client.index('myIndex').updateRankingRules(rankingRules: string[] | null): Promise<EnqueuedTask>
client.index('myIndex').resetRankingRules(): Promise<EnqueuedTask>

Distinct Attribute

client.index('myIndex').getDistinctAttribute(): Promise<string | void>
client.index('myIndex').updateDistinctAttribute(distinctAttribute: string | null): Promise<EnqueuedTask>
client.index('myIndex').resetDistinctAttribute(): Promise<EnqueuedTask>

Searchable attributes

client.index('myIndex').getSearchableAttributes(): Promise<string[]>
client.index('myIndex').updateSearchableAttributes(searchableAttributes: string[] | null): Promise<EnqueuedTask>
client.index('myIndex').resetSearchableAttributes(): Promise<EnqueuedTask>

Displayed attributes

client.index('myIndex').getDisplayedAttributes(): Promise<string[]>
client.index('myIndex').updateDisplayedAttributes(displayedAttributes: string[] | null): Promise<EnqueuedTask>
client.index('myIndex').resetDisplayedAttributes(): Promise<EnqueuedTask>

Filterable attributes

client.index('myIndex').getFilterableAttributes(): Promise<string[]>
client.index('myIndex').updateFilterableAttributes(filterableAttributes: string[] | null): Promise<EnqueuedTask>
client.index('myIndex').resetFilterableAttributes(): Promise<EnqueuedTask>

Sortable attributes

client.index('myIndex').getSortableAttributes(): Promise<string[]>
client.index('myIndex').updateSortableAttributes(sortableAttributes: string[] | null): Promise<EnqueuedTask>
client.index('myIndex').resetSortableAttributes(): Promise<EnqueuedTask>

Faceting

client.index('myIndex').getFaceting(): Promise<Faceting>
client.index('myIndex').updateFaceting(faceting: Faceting): Promise<EnqueuedTask>
client.index('myIndex').resetFaceting(): Promise<EnqueuedTask>

Typo tolerance

client.index('myIndex').getTypoTolerance(): Promise<TypoTolerance>
client.index('myIndex').updateTypoTolerance(typoTolerance: TypoTolerance | null): Promise<EnqueuedTask>
client.index('myIndex').resetTypoTolerance(): Promise<EnqueuedTask>

Separator tokens

client.index('myIndex').getSeparatorTokens(): Promise<SeparatorTokens>
client.index('myIndex').updateSeparatorTokens(separatorTokens: SeparatorTokens | null): Promise<EnqueuedTask>
client.index('myIndex').resetSeparatorTokens(): Promise<EnqueuedTask>

Non Separator tokens

client.index('myIndex').getNonSeparatorTokens(): Promise<NonSeparatorTokens>
client.index('myIndex').updateNonSeparatorTokens(nonSeparatorTokens: NonSeparatorTokens | null): Promise<EnqueuedTask>
client.index('myIndex').resetNonSeparatorTokens(): Promise<EnqueuedTask>

Dictionary

client.index('myIndex').getDictionary(): Promise<Dictionary>
client.index('myIndex').updateDictionary(dictionary: Dictionary | null): Promise<EnqueuedTask>
client.index('myIndex').resetDictionary(): Promise<EnqueuedTask>

Proximity Precision

client.index('myIndex').getProximityPrecision(): Promise<ProximityPrecision>
client.index('myIndex').updateProximityPrecision(proximityPrecision: ProximityPrecision): Promise<EnqueuedTask>
client.index('myIndex').resetProximityPrecision(): Promise<EnqueuedTask>

Embedders

⚠️ This feature is experimental. Activate the vectorStore experimental feature to use it

Get embedders

client.index('myIndex').getEmbedders(): Promise<Embedders>

Update embedders

client.index('myIndex').updateEmbedders(embedders: Embedders): Promise<EnqueuedTask>

Reset embedders

client.index('myIndex').resetEmbedders(): Promise<EnqueuedTask>

SearchCutoffMs

client.index('myIndex').getSearchCutoffMs(): Promise<SearchCutoffMs>
client.index('myIndex').updateSearchCutoffMs(searchCutoffMs: SearchCutoffMs): Promise<EnqueuedTask>
client.index('myIndex').resetSearchCutoffMs(): Promise<EnqueuedTask>

Keys

client.getKeys(parameters: KeysQuery): Promise<KeysResults>
client.getKey(keyOrUid: string): Promise<Key>
client.createKey(options: KeyCreation): Promise<Key>
client.updateKey(keyOrUid: string, options: KeyUpdate): Promise<Key>
client.deleteKey(keyOrUid: string): Promise<void>

isHealthy

client.isHealthy(): Promise<boolean>

Health

client.health(): Promise<Health>

Stats

client.getStats(): Promise<Stats>

Version

client.getVersion(): Promise<Version>

Dumps

client.createDump(): Promise<EnqueuedTask>

Snapshots

client.createSnapshot(): Promise<EnqueuedTask>

Meilisearch provides and maintains many SDKs and integration tools like this one. We want to provide everyone with an amazing search experience for any kind of project. For a full overview of everything we create and maintain, take a look at the integration-guides repository.