diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index f14e4a27b..70b9d81bc 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -1,422 +1,410 @@ -# This code-samples file is used by the Meilisearch documentation +# This code-samples file is used by the Meilisearch documentation. # Every example written here will be automatically fetched by -# the documentation on build -# You can read more on https://github.com/meilisearch/documentation/tree/master/.vuepress/code-samples +# the documentation on build. +# You can read more at https://github.com/meilisearch/documentation +# This file is generated, read more in CONTRIBUTING.md "Tests and Linter" section. --- synonyms_guide_1: |- - client.index('movies').updateSynonyms({ - 'great': ['fantastic'], - 'fantastic': ['great'] - }) + await client.index("movies").updateSynonyms({ + great: ["fantastic"], + fantastic: ["great"], + }); date_guide_index_1: |- - const games = require('./games.json') - client.index('games').addDocuments(games).then((res) => console.log(res)) + import games from "./games.json" with { type: "json" }; + await client.index("games").addDocuments(games); date_guide_filterable_attributes_1: |- - client.index('games').updateFilterableAttributes(['release_timestamp']) + await client.index("games").updateFilterableAttributes(["release_timestamp"]); date_guide_filter_1: |- - client.index('games').search('', { - filter: 'release_timestamp >= 1514761200 AND release_timestamp < 1672527600' - }) + await client.index("games").search("", { + filter: "release_timestamp >= 1514761200 AND release_timestamp < 1672527600", + }); date_guide_sortable_attributes_1: |- - client.index('games').updateSortableAttributes(['release_timestamp']) + await client.index("games").updateSortableAttributes(["release_timestamp"]); date_guide_sort_1: |- - client.index('games').search('', { - sort: ['release_timestamp:desc'], - }) + await client.index("games").search("", { + sort: ["release_timestamp:desc"], + }); get_one_index_1: |- - client.index('movies').getRawInfo() + await client.index("movies").getRawInfo(); list_all_indexes_1: |- - client.getIndexes({ limit: 3 }) + await client.getIndexes({ limit: 3 }); create_an_index_1: |- - client.createIndex('movies', { primaryKey: 'id' }) + await client.createIndex("movies", { primaryKey: "id" }); update_an_index_1: |- - client.updateIndex('movies', { primaryKey: 'id' }) + await client.updateIndex("movies", { primaryKey: "id" }); delete_an_index_1: |- - client.deleteIndex('movies') + await client.deleteIndex("movies"); swap_indexes_1: |- - client.swapIndexes([ - { 'indexes': ['indexA', 'indexB'] }, - { 'indexes': ['indexX', 'indexY'] } - ]) + await client.swapIndexes([ + { indexes: ["indexA", "indexB"] }, + { indexes: ["indexX", "indexY"] }, + ]); get_one_document_1: |- - client - .index('movies') - .getDocument(25684, { fields: ['id', 'title', 'poster', 'release_date'] }) + await client + .index("movies") + .getDocument(25684, { fields: ["id", "title", "poster", "release_date"] }); get_documents_1: |- - client.index('movies').getDocuments({ + await client.index("movies").getDocuments({ limit: 2, - filter: 'genres = action' - }) + filter: "genres = action", + }); get_documents_post_1: |- - client.index('books').getDocuments({ - filter: '(rating > 3 AND (genres = Adventure OR genres = Fiction)) AND language = English', - fields: ['title', 'genres', 'rating', 'language'], - limit: 3 - }) + await client.index("books").getDocuments({ + filter: + "(rating > 3 AND (genres = Adventure OR genres = Fiction)) AND language = English", + fields: ["title", "genres", "rating", "language"], + limit: 3, + }); add_or_replace_documents_1: |- - client.index('movies').addDocuments([{ + await client.index("movies").addDocuments([ + { id: 287947, - title: 'Shazam', - poster: 'https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg', - overview: 'A boy is given the ability to become an adult superhero in times of need with a single magic word.', - release_date: '2019-03-23' - }]) + title: "Shazam", + poster: "https://image.tmdb.org/t/p/w1280/xnopI5Xtky18MPhK40cZAGAOVeV.jpg", + overview: + "A boy is given the ability to become an adult superhero in times of need with a single magic word.", + release_date: "2019-03-23", + }, + ]); add_or_update_documents_1: |- - client.index('movies').updateDocuments([{ + await client.index("movies").updateDocuments([ + { id: 287947, - title: 'Shazam ⚡️', - genres: 'comedy' - }]) + title: "Shazam ⚡️", + genres: "comedy", + }, + ]); delete_all_documents_1: |- - client.index('movies').deleteAllDocuments() + await client.index("movies").deleteAllDocuments(); delete_one_document_1: |- - client.index('movies').deleteDocument(25684) + await client.index("movies").deleteDocument(25684); delete_documents_by_batch_1: |- - client.index('movies').deleteDocuments([23488, 153738, 437035, 363869]) + await client.index("movies").deleteDocuments([23488, 153738, 437035, 363869]); delete_documents_by_filter_1: |- - client.index('movies').deleteDocuments({ - filter: 'genres = action OR genres = adventure' - }) + await client.index("movies").deleteDocuments({ + filter: "genres = action OR genres = adventure", + }); search_post_1: |- - client.index('movies').search('American ninja') + await client.index("movies").search("American ninja"); search_get_1: |- - client.index('movies').searchGet('American ninja') + await client.index("movies").searchGet("American ninja"); multi_search_1: |- - client.multiSearch({ queries: [ - { - indexUid: 'movies', - q: 'pooh', - limit: 5, - }, - { - indexUid: 'movies', - q: 'nemo', - limit: 5, - }, - { - indexUid: 'movie_ratings', - q: 'us', - }, - ]}) + await client.multiSearch({ + queries: [ + { + indexUid: "movies", + q: "pooh", + limit: 5, + }, + { + indexUid: "movies", + q: "nemo", + limit: 5, + }, + { + indexUid: "movie_ratings", + q: "us", + }, + ], + }); get_all_tasks_1: |- - client.tasks.getTasks() + await client.tasks.getTasks(); get_task_1: |- - client.tasks.getTask(1) + await client.tasks.getTask(1); async_guide_filter_by_date_1: |- - client.tasks.getTasks({ afterEnqueuedAt: '2020-10-11T11:49:53.000Z' }) + await client.tasks.getTasks({ + afterEnqueuedAt: "2020-10-11T11:49:53.000Z", + }); async_guide_multiple_filters_1: |- - client.tasks.getTasks({ - indexUids: ['movies'], - types: ['documentAdditionOrUpdate','documentDeletion'], - statuses: ['processing'] - }) + await client.tasks.getTasks({ + indexUids: ["movies"], + types: ["documentAdditionOrUpdate", "documentDeletion"], + statuses: ["processing"], + }); async_guide_filter_by_ids_1: |- - client.tasks.getTasks({ uids: [5, 10, 13] }) + await client.tasks.getTasks({ uids: [5, 10, 13] }); async_guide_filter_by_statuses_1: |- - client.tasks.getTasks({ statuses: ['failed', 'canceled'] }) + await client.tasks.getTasks({ + statuses: ["failed", "canceled"], + }); async_guide_filter_by_types_1: |- - client.tasks.getTasks({ types: ['dumpCreation', 'indexSwap'] }) + await client.tasks.getTasks({ + types: ["dumpCreation", "indexSwap"], + }); async_guide_filter_by_index_uids_1: |- - client.tasks.getTasks({ indexUids: ['movies'] }) + await client.tasks.getTasks({ indexUids: ["movies"] }); get_all_tasks_paginating_1: |- - client.tasks.getTasks({ limit: 2, from: 10 }) + await client.tasks.getTasks({ limit: 2, from: 10 }); get_all_tasks_paginating_2: |- - client.tasks.getTasks({ limit: 2, from: 8 }) + await client.tasks.getTasks({ limit: 2, from: 8 }); async_guide_canceled_by_1: |- - client.tasks.getTasks({ canceledBy: [9, 15] }) + await client.tasks.getTasks({ canceledBy: [9, 15] }); delete_tasks_1: |- - client.tasks.deleteTasks({ uids: [1, 2] }) + await client.tasks.deleteTasks({ uids: [1, 2] }); cancel_tasks_1: |- - client.tasks.cancelTasks({ uids: [1, 2] }) + await client.tasks.cancelTasks({ uids: [1, 2] }); get_one_key_1: |- - client.getKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d') + await client.getKey("6062abda-a5aa-4414-ac91-ecd7944c0f8d"); get_all_keys_1: |- - client.getKeys({ limit: 3 }) + await client.getKeys({ limit: 3 }); create_a_key_1: |- - client.createKey({ - description: 'Add documents: Products API key', - actions: ['documents.add'], - indexes: ['products'], - expiresAt: '2021-11-13T00:00:00Z' - }) + await client.createKey({ + description: "Add documents: Products API key", + actions: ["documents.add"], + indexes: ["products"], + expiresAt: new Date("2021-11-13T00:00:00Z"), + }); update_a_key_1: |- - client.updateKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d', { - name: 'Products/Reviews API key', - description: 'Manage documents: Products/Reviews API key', - }) + await client.updateKey("6062abda-a5aa-4414-ac91-ecd7944c0f8d", { + name: "Products/Reviews API key", + description: "Manage documents: Products/Reviews API key", + }); delete_a_key_1: |- - client.deleteKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d') + await client.deleteKey("6062abda-a5aa-4414-ac91-ecd7944c0f8d"); get_settings_1: |- - client.index('movies').getSettings() + await client.index("movies").getSettings(); update_settings_1: |- - client.index('movies').updateSettings({ - rankingRules: [ - 'words', - 'typo', - 'proximity', - 'attribute', - 'sort', - 'exactness', - 'release_date:desc', - 'rank:desc' - ], - distinctAttribute: 'movie_id', - searchableAttributes: [ - 'title', - 'overview', - 'genres' - ], - displayedAttributes: [ - 'title', - 'overview', - 'genres', - 'release_date' - ], - stopWords: [ - 'the', - 'a', - 'an' - ], - sortableAttributes: [ - 'title', - 'release_date' - ], - synonyms: { - 'wolverine': ['xmen', 'logan'], - 'logan': ['wolverine'] - }, - typoTolerance: { - 'minWordSizeForTypos': { - 'oneTypo': 8, - 'twoTypos': 10 - }, - 'disableOnAttributes': [ - 'title' - ] - }, - pagination: { - maxTotalHits: 5000 - }, - faceting: { - maxValuesPerFacet: 200 + await client.index("movies").updateSettings({ + rankingRules: [ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + "release_date:desc", + "rank:desc", + ], + distinctAttribute: "movie_id", + searchableAttributes: ["title", "overview", "genres"], + displayedAttributes: ["title", "overview", "genres", "release_date"], + stopWords: ["the", "a", "an"], + sortableAttributes: ["title", "release_date"], + synonyms: { + wolverine: ["xmen", "logan"], + logan: ["wolverine"], + }, + typoTolerance: { + minWordSizeForTypos: { + oneTypo: 8, + twoTypos: 10, }, - searchCutoffMs: 150 - }) + disableOnAttributes: ["title"], + }, + pagination: { + maxTotalHits: 5000, + }, + faceting: { + maxValuesPerFacet: 200, + }, + searchCutoffMs: 150, + }); reset_settings_1: |- - client.index('movies').resetSettings() + await client.index("movies").resetSettings(); get_synonyms_1: |- - client.index('movies').getSynonyms() + await client.index("movies").getSynonyms(); update_synonyms_1: |- - client.index('movies').updateSynonyms({ - wolverine: ['xmen', 'logan'], - logan: ['wolverine', 'xmen'], - wow: ['world of warcraft'] - }) + await client.index("movies").updateSynonyms({ + wolverine: ["xmen", "logan"], + logan: ["wolverine", "xmen"], + wow: ["world of warcraft"], + }); reset_synonyms_1: |- - client.index('movies').resetSynonyms() + await client.index("movies").resetSynonyms(); get_stop_words_1: |- - client.index('movies').getStopWords() + await client.index("movies").getStopWords(); update_stop_words_1: |- - client.index('movies').updateStopWords(['of', 'the', 'to']) + await client.index("movies").updateStopWords(["of", "the", "to"]); reset_stop_words_1: |- - client.index('movies').resetStopWords() + await client.index("movies").resetStopWords(); get_ranking_rules_1: |- - client.index('movies').getRankingRules() + await client.index("movies").getRankingRules(); update_ranking_rules_1: |- - client.index('movies').updateRankingRules([ - 'words', - 'typo', - 'proximity', - 'attribute', - 'sort', - 'exactness', - 'release_date:asc', - 'rank:desc' - ]) + await client + .index("movies") + .updateRankingRules([ + "words", + "typo", + "proximity", + "attribute", + "sort", + "exactness", + "release_date:asc", + "rank:desc", + ]); reset_ranking_rules_1: |- - client.index('movies').resetRankingRules() + await client.index("movies").resetRankingRules(); get_distinct_attribute_1: |- - client.index('shoes').getDistinctAttribute() + await client.index("shoes").getDistinctAttribute(); update_distinct_attribute_1: |- - client.index('shoes').updateDistinctAttribute('skuid') + await client.index("shoes").updateDistinctAttribute("skuid"); reset_distinct_attribute_1: |- - client.index('shoes').resetDistinctAttribute() + await client.index("shoes").resetDistinctAttribute(); get_searchable_attributes_1: |- - client.index('movies').getSearchableAttributes() + await client.index("movies").getSearchableAttributes(); update_searchable_attributes_1: |- - client.index('movies').updateSearchableAttributes([ - 'title', - 'overview', - 'genres' - ]) + await client + .index("movies") + .updateSearchableAttributes(["title", "overview", "genres"]); reset_searchable_attributes_1: |- - client.index('movies').resetSearchableAttributes() + await client.index("movies").resetSearchableAttributes(); get_displayed_attributes_1: |- - client.index('movies').getDisplayedAttributes() + await client.index("movies").getDisplayedAttributes(); update_displayed_attributes_1: |- - client.index('movies').updateDisplayedAttributes([ - 'title', - 'overview', - 'genres', - 'release_date' - ]) + await client + .index("movies") + .updateDisplayedAttributes(["title", "overview", "genres", "release_date"]); reset_displayed_attributes_1: |- - client.index('movies').resetDisplayedAttributes() + await client.index("movies").resetDisplayedAttributes(); get_typo_tolerance_1: |- - client.index('books').getTypoTolerance() + await client.index("books").getTypoTolerance(); update_typo_tolerance_1: |- - client.index('books').updateTypoTolerance({ + await client.index("books").updateTypoTolerance({ minWordSizeForTypos: { - oneTypo: 4, - twoTypos: 10 - }, - disableOnAttributes: [ - 'title' - ] - }) + oneTypo: 4, + twoTypos: 10, + }, + disableOnAttributes: ["title"], + }); reset_typo_tolerance_1: |- - client.index('books').resetTypoTolerance() + await client.index("books").resetTypoTolerance(); get_index_stats_1: |- - client.index('movies').getStats() + await client.index("movies").getStats(); get_indexes_stats_1: |- - client.getStats() + await client.getStats(); get_health_1: |- - client.health() + await client.health(); get_version_1: |- - client.getVersion() + await client.getVersion(); distinct_attribute_guide_1: |- - client.index('jackets').updateDistinctAttribute('product_id') + await client.index("jackets").updateDistinctAttribute("product_id"); field_properties_guide_searchable_1: |- - client.index('movies').updateSearchableAttributes([ - 'title', - 'overview', - 'genres', - ] - ) + await client + .index("movies") + .updateSearchableAttributes(["title", "overview", "genres"]); field_properties_guide_displayed_1: |- - client.index('movies').updateDisplayedAttributes([ - 'title', - 'overview', - 'genres', - 'release_date', - ] - ) + await client + .index("movies") + .updateDisplayedAttributes(["title", "overview", "genres", "release_date"]); filtering_guide_1: |- - client.index('movie_ratings').search('Avengers', { - filter: 'release_date > 795484800' - }) + await client.index("movie_ratings").search("Avengers", { + filter: "release_date > 795484800", + }); filtering_guide_2: |- - client.index('movie_ratings').search('Batman', { - filter: 'release_date > 795484800 AND (director = "Tim Burton" OR director = "Christopher Nolan")' - }) + await client.index("movie_ratings").search("Batman", { + filter: + 'release_date > 795484800 AND (director = "Tim Burton" OR director = "Christopher Nolan")', + }); filtering_guide_3: |- - client.index('movie_ratings').search('Planet of the Apes', { - filter: "release_date > 1577884550 AND (NOT director = \"Tim Burton\")" - }) + await client.index("movie_ratings").search("Planet of the Apes", { + filter: 'release_date > 1577884550 AND (NOT director = "Tim Burton")', + }); filtering_guide_nested_1: |- - client.index('movie_ratings').search('thriller', { - filter: 'rating.users >= 90' - }) + await client.index("movie_ratings").search("thriller", { + filter: "rating.users >= 90", + }); search_parameter_guide_query_1: |- - client.index('movies').search('shifu') + await client.index("movies").search("shifu"); search_parameter_guide_offset_1: |- - client.index('movies').search('shifu', { - offset: 1 - }) + await client.index("movies").search("shifu", { + offset: 1, + }); search_parameter_guide_limit_1: |- - client.index('movies').search('shifu', { - limit: 2 - }) + await client.index("movies").search("shifu", { + limit: 2, + }); search_parameter_guide_retrieve_1: |- - client.index('movies').search('shifu', { - attributesToRetrieve: ['overview', 'title'] - }) + await client.index("movies").search("shifu", { + attributesToRetrieve: ["overview", "title"], + }); search_parameter_guide_crop_1: |- - client.index('movies').search('shifu', { - attributesToCrop: ['overview'], - cropLength: 5 - }) + await client.index("movies").search("shifu", { + attributesToCrop: ["overview"], + cropLength: 5, + }); search_parameter_guide_crop_marker_1: |- - client.index('movies').search('shifu', { - attributesToCrop: ['overview'], - cropMarker: '[…]' - }) + await client.index("movies").search("shifu", { + attributesToCrop: ["overview"], + cropMarker: "[…]", + }); search_parameter_guide_highlight_1: |- - client.index('movies').search('winter feast', { - attributesToHighlight: ['overview'] - }) + await client.index("movies").search("winter feast", { + attributesToHighlight: ["overview"], + }); search_parameter_guide_highlight_tag_1: |- - client.index('movies').search('winter feast', { - attributesToHighlight: ['overview'], + await client.index("movies").search("winter feast", { + attributesToHighlight: ["overview"], highlightPreTag: '', - highlightPostTag: '' - }) + highlightPostTag: "", + }); search_parameter_guide_show_matches_position_1: |- - client.index('movies').search('winter feast', { - showMatchesPosition: true - }) + await client.index("movies").search("winter feast", { + showMatchesPosition: true, + }); search_parameter_guide_matching_strategy_1: |- - client.index('movies').search('big fat liar', { - matchingStrategy: 'last' - }) + await client.index("movies").search("big fat liar", { + matchingStrategy: "last", + }); search_parameter_guide_matching_strategy_2: |- - client.index('movies').search('big fat liar', { - matchingStrategy: 'all' - }) + await client.index("movies").search("big fat liar", { + matchingStrategy: "all", + }); search_parameter_guide_hitsperpage_1: |- - client.index('movies').search('', { - hitsPerPage: 15 - }) + await client.index("movies").search("", { + hitsPerPage: 15, + }); search_parameter_guide_page_1: |- - client.index('movies').search('', { - page: 2 - }) + await client.index("movies").search("", { + page: 2, + }); search_parameter_guide_show_ranking_score_1: |- - client.index('movies').search('dragon', { - showRankingScore: true - }) + await client.index("movies").search("dragon", { + showRankingScore: true, + }); search_parameter_guide_attributes_to_search_on_1: |- - client.index('movies').search('adventure', { - attributesToSearchOn: ['overview'] - }) + await client.index("movies").search("adventure", { + attributesToSearchOn: ["overview"], + }); typo_tolerance_guide_1: |- - client.index('movies').updateTypoTolerance({ - enabled: false - }) + await client.index("movies").updateTypoTolerance({ + enabled: false, + }); typo_tolerance_guide_2: |- - client.index('movies').updateTypoTolerance({ - disableOnAttributes: ['title'] - }) + await client.index("movies").updateTypoTolerance({ + disableOnAttributes: ["title"], + }); typo_tolerance_guide_3: |- - client.index('movies').updateTypoTolerance({ - disableOnWords: ['shrek'] - }) + await client.index("movies").updateTypoTolerance({ + disableOnWords: ["shrek"], + }); typo_tolerance_guide_4: |- - client.index('movies').updateTypoTolerance({ + await client.index("movies").updateTypoTolerance({ minWordSizeForTypos: { oneTypo: 4, - twoTypos: 10 - } - }) + twoTypos: 10, + }, + }); add_movies_json_1: |- - const movies = require('./movies.json') - client.index('movies').addDocuments(movies).then((res) => console.log(res)) + import movies from "./movies.json" with { type: "json" }; + const task = await client.index("movies").addDocuments(movies); + console.log(task); primary_field_guide_update_document_primary_key: |- - client.updateIndex('books', { - primaryKey: 'title' - }) + await client.updateIndex("books", { primaryKey: "title" }); primary_field_guide_create_index_primary_key: |- - client.createIndex('books', { primaryKey: 'reference_number' }) + await client.createIndex("books", { primaryKey: "reference_number" }); primary_field_guide_add_document_primary_key: |- - client.index('books').addDocuments([ - { - reference_number: 287947, - title: 'Diary of a Wimpy Kid', - author: 'Jeff Kinney', - genres: ['comedy','humor'], - price: 5.00 - } - ], { primaryKey: 'reference_number' }) + await client.index("books").addDocuments( + [ + { + reference_number: 287947, + title: "Diary of a Wimpy Kid", + author: "Jeff Kinney", + genres: ["comedy", "humor"], + price: 5.0, + }, + ], + { primaryKey: "reference_number" }, + ); getting_started_add_documents: |- // With npm: // npm install meilisearch @@ -425,397 +413,427 @@ getting_started_add_documents: |- // yarn add meilisearch // In your .js file: + import { MeiliSearch } from "meilisearch"; + import movies from "./movies.json" with { type: "json" }; + // With the `require` syntax: - const { MeiliSearch } = require('meilisearch') - const movies = require('./movies.json') - // With the `import` syntax: - import { MeiliSearch } from 'meilisearch' - import movies from './movies.json' + // const { MeiliSearch } = require("meilisearch"); + // const movies = require("./movies.json"); const client = new MeiliSearch({ - host: 'http://localhost:7700', - apiKey: 'aSampleMasterKey' - }) - client.index('movies').addDocuments(movies) - .then((res) => console.log(res)) + host: "http://localhost:7700", + apiKey: "aSampleMasterKey", + }); + + const task = await client.index("movies").addDocuments(movies); + console.log(task); getting_started_search: |- - client.index('movies').search('botman').then((res) => console.log(res)) + const response = await client.index("movies").search("botman"); + + console.log(response); getting_started_update_ranking_rules: |- - client.index('movies').updateRankingRules([ - 'exactness', - 'words', - 'typo', - 'proximity', - 'attribute', - 'sort', - 'release_date:asc', - 'rank:desc' - ]) + await client + .index("movies") + .updateRankingRules([ + "exactness", + "words", + "typo", + "proximity", + "attribute", + "sort", + "release_date:asc", + "rank:desc", + ]); getting_started_update_searchable_attributes: |- - client.index('movies').updateSearchableAttributes([ - 'title' - ]) + await client.index("movies").updateSearchableAttributes(["title"]); getting_started_update_stop_words: |- - client.index('movies').updateStopWords(['the']) + await client.index("movies").updateStopWords(["the"]); getting_started_check_task_status: |- - client.tasks.getTask(0) + await client.tasks.getTask(0); getting_started_synonyms: |- - client.index('movies').updateSynonyms({ - winnie: ['piglet'], - piglet: ['winnie'] - }) + await client.index("movies").updateSynonyms({ + winnie: ["piglet"], + piglet: ["winnie"], + }); getting_started_update_displayed_attributes: |- - client.index('movies').updateDisplayedAttributes([ - 'title', - 'overview', - 'poster' - ]) + await client + .index("movies") + .updateDisplayedAttributes(["title", "overview", "poster"]); getting_started_add_meteorites: |- - const meteorites = require('./meteorites.json') + import meteorites from "./meteorites.json" with { type: "json" }; - client.index('meteorites').addDocuments(meteorites) + await client.index("meteorites").addDocuments(meteorites); getting_started_configure_settings: |- - client.index('meteorites').updateSettings({ - filterableAttributes: ['mass', '_geo'], - sortableAttributes: ['mass', '_geo'] - }) + await client.index("meteorites").updateSettings({ + filterableAttributes: ["mass", "_geo"], + sortableAttributes: ["mass", "_geo"], + }); getting_started_geo_radius: |- - client.index('meteorites').search('', { filter: '_geoRadius(46.9480, 7.4474, 210000)' }) + await client + .index("meteorites") + .search("", { filter: "_geoRadius(46.9480, 7.4474, 210000)" }); getting_started_geo_point: |- - client.index('meteorites').search('', { sort: ['_geoPoint(48.8583701, 2.2922926):asc'] }) + await client + .index("meteorites") + .search("", { sort: ["_geoPoint(48.8583701, 2.2922926):asc"] }); getting_started_sorting: |- - client.index('meteorites').search('', { - sort: ['mass:asc'], - filter: 'mass < 200' - }) + await client.index("meteorites").search("", { + sort: ["mass:asc"], + filter: "mass < 200", + }); getting_started_faceting: |- - client.index('movies').updateFaceting({ + await client.index("movies").updateFaceting({ maxValuesPerFacet: 2, sortFacetValuesBy: { - '*': 'count' - } - }) + "*": "count", + }, + }); getting_started_typo_tolerance: |- - client.index('movies').updateTypoTolerance({ + await client.index("movies").updateTypoTolerance({ minWordSizeForTypos: { - oneTypo: 4 - } - }) + oneTypo: 4, + }, + }); getting_started_filtering: |- - client.index('meteorites').search('', { filter: 'mass < 200' }) + await client.index("meteorites").search("", { filter: "mass < 200" }); getting_started_pagination: |- - client.index('movies').updatePagination({ maxTotalHits: 500 }) + await client.index("movies").updatePagination({ maxTotalHits: 500 }); get_filterable_attributes_1: |- - client.index('movies').getFilterableAttributes() + await client.index("movies").getFilterableAttributes(); update_filterable_attributes_1: |- - client.index('movies') - .updateFilterableAttributes([ - "genres", - { - attributePatterns: ["genre"], - features: { - facetSearch: true, - filter: { equality: true, comparison: false }, - }, - } - ]) + await client.index("movies").updateFilterableAttributes([ + "genres", + { + attributePatterns: ["genre"], + features: { + facetSearch: true, + filter: { equality: true, comparison: false }, + }, + }, + ]); reset_filterable_attributes_1: |- - client.index('movies').resetFilterableAttributes() + await client.index("movies").resetFilterableAttributes(); filtering_update_settings_1: |- - client.index('movies') - .updateFilterableAttributes([ - 'director', - 'genres' - ]) + await client.index("movies").updateFilterableAttributes(["director", "genres"]); faceted_search_walkthrough_filter_1: |- - client.index('movies') - .search('thriller', { - filter: [['genres = Horror', 'genres = Mystery'], 'director = "Jordan Peele"'] - }) + await client.index("movies").search("thriller", { + filter: [ + ["genres = Horror", "genres = Mystery"], + 'director = "Jordan Peele"', + ], + }); faceted_search_update_settings_1: |- - client.index('movie_ratings').updateFilterableAttributes(['genres', 'rating', 'language']) + await client + .index("movie_ratings") + .updateFilterableAttributes(["genres", "rating", "language"]); faceted_search_1: |- - client.index('books').search('classic', { facets: ['genres', 'rating', 'language'] }) + await client + .index("books") + .search("classic", { facets: ["genres", "rating", "language"] }); post_dump_1: |- - client.createDump() + await client.createDump(); create_snapshot_1: |- - client.createSnapshot() + await client.createSnapshot(); phrase_search_1: |- - client.index('movies') - .search('"african american" horror') + await client.index("movies").search('"african american" horror'); sorting_guide_update_sortable_attributes_1: |- - client.index('books').updateSortableAttributes([ - 'author', - 'price' - ]) + await client.index("books").updateSortableAttributes(["author", "price"]); sorting_guide_update_ranking_rules_1: |- - client.index('books').updateRankingRules([ - 'words', - 'sort', - 'typo', - 'proximity', - 'attribute', - 'exactness' - ]) + await client + .index("books") + .updateRankingRules([ + "words", + "sort", + "typo", + "proximity", + "attribute", + "exactness", + ]); sorting_guide_sort_parameter_1: |- - client.index('books').search('science fiction', { - sort: ['price:asc'], - }) + await client.index("books").search("science fiction", { + sort: ["price:asc"], + }); sorting_guide_sort_parameter_2: |- - client.index('books').search('butler', { - sort: ['author:desc'], - }) + await client.index("books").search("butler", { + sort: ["author:desc"], + }); sorting_guide_sort_nested_1: |- - client.index('books').search('science fiction', { - 'sort': ['rating.users:asc'], - }) + await client.index("books").search("science fiction", { + sort: ["rating.users:asc"], + }); get_sortable_attributes_1: |- - client.index('books').getSortableAttributes() + await client.index("books").getSortableAttributes(); update_sortable_attributes_1: |- - client.index('books') - .updateSortableAttributes([ - 'price', - 'author' - ]) + await client.index("books").updateSortableAttributes(["price", "author"]); reset_sortable_attributes_1: |- - client.index('books').resetSortableAttributes() + await client.index("books").resetSortableAttributes(); get_pagination_settings_1: |- - client.index('books').getPagination() + await client.index("books").getPagination(); update_pagination_settings_1: |- - client.index('books').updateSettings({ pagination: { maxTotalHits: 100 }}) + await client + .index("books") + .updateSettings({ pagination: { maxTotalHits: 100 } }); reset_pagination_settings_1: |- - client.index('books').resetPagination() + await client.index("books").resetPagination(); get_faceting_settings_1: |- - client.index('books').getFaceting() + await client.index("books").getFaceting(); update_faceting_settings_1: |- - client.index('books').updateFaceting({ - maxValuesPerFacet: 2 + await client.index("books").updateFaceting({ + maxValuesPerFacet: 2, sortFacetValuesBy: { - '*': 'alpha', - genres: 'count' - } - }) + "*": "alpha", + genres: "count", + }, + }); reset_faceting_settings_1: |- - client.index('books').resetFaceting() + await client.index("books").resetFaceting(); get_dictionary_1: |- - client.index('books').getDictionary() + await client.index("books").getDictionary(); update_dictionary_1: |- - client.index('books').updateDictionary(['J. R. R.', 'W. E. B.']) + await client.index("books").updateDictionary(["J. R. R.", "W. E. B."]); reset_dictionary_1: |- - client.index('books').resetDictionary() + await client.index("books").resetDictionary(); search_parameter_guide_sort_1: |- - client.index('books').search('science fiction', { - sort: ['price:asc'], - }) + await client.index("books").search("science fiction", { + sort: ["price:asc"], + }); get_separator_tokens_1: |- - client.index('books').getSeparatorTokens() + await client.index("books").getSeparatorTokens(); update_separator_tokens_1: |- - client.index('books').updateSeparatorTokens(['|', '…']) + await client.index("books").updateSeparatorTokens(["|", "…"]); reset_separator_tokens_1: |- - client.index('books').resetSeparatorTokens() + await client.index("books").resetSeparatorTokens(); get_non_separator_tokens_1: |- - client.index('books').getNonSeparatorTokens() + await client.index("books").getNonSeparatorTokens(); update_non_separator_tokens_1: |- - client.index('books').updateNonSeparatorTokens(['@', '#']) + await client.index("books").updateNonSeparatorTokens(["@", "#"]); reset_non_separator_tokens_1: |- - client.index('books').resetNonSeparatorTokens() + await client.index("books").resetNonSeparatorTokens(); get_proximity_precision_settings_1: |- - client.index('books').getProximityPrecision() + await client.index("books").getProximityPrecision(); update_proximity_precision_settings_1: |- - client.index('books').updateProximityPrecision('byAttribute') + await client.index("books").updateProximityPrecision("byAttribute"); reset_proximity_precision_settings_1: |- - client.index('books').resetProximityPrecision() + await client.index("books").resetProximityPrecision(); get_search_cutoff_1: |- - client.index('movies').getSearchCutoffMs() + await client.index("movies").getSearchCutoffMs(); update_search_cutoff_1: |- - client.index('movies').updateSearchCutoffMs(150) + await client.index("movies").updateSearchCutoffMs(150); reset_search_cutoff_1: |- - client.index('movies').resetSearchCutoffMs() + await client.index("movies").resetSearchCutoffMs(); search_parameter_guide_facet_stats_1: |- - client.index('movie_ratings').search('Batman', { facets: ['genres', 'rating'] }) + await client + .index("movie_ratings") + .search("Batman", { facets: ["genres", "rating"] }); geosearch_guide_filter_settings_1: |- - client.index('restaurants') - .updateFilterableAttributes([ - '_geo' - ]) + await client.index("restaurants").updateFilterableAttributes(["_geo"]); geosearch_guide_filter_usage_1: |- - client.index('restaurants').search('', { - filter: ['_geoRadius(45.472735, 9.184019, 2000)'], - }) + await client.index("restaurants").search("", { + filter: ["_geoRadius(45.472735, 9.184019, 2000)"], + }); geosearch_guide_filter_usage_2: |- - client.index('restaurants').search('', { - filter: ['_geoRadius(45.472735, 9.184019, 2000) AND type = pizza'], - }) + await client.index("restaurants").search("", { + filter: ["_geoRadius(45.472735, 9.184019, 2000) AND type = pizza"], + }); geosearch_guide_filter_usage_3: |- - client.index('restaurants').search('', { - filter: ['_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])'], - }) + await client.index("restaurants").search("", { + filter: ["_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])"], + }); geosearch_guide_sort_settings_1: |- - client.index('restaurants').updateSortableAttributes([ - '_geo' - ]) + await client.index("restaurants").updateSortableAttributes(["_geo"]); geosearch_guide_sort_usage_1: |- - client.index('restaurants').search('', { - sort: ['_geoPoint(48.8561446, 2.2978204):asc'], - }) + await client.index("restaurants").search("", { + sort: ["_geoPoint(48.8561446, 2.2978204):asc"], + }); geosearch_guide_sort_usage_2: |- - client.index('restaurants').search('', { - sort: ['_geoPoint(48.8561446, 2.2978204):asc', 'rating:desc'], - }) + await client.index("restaurants").search("", { + sort: ["_geoPoint(48.8561446, 2.2978204):asc", "rating:desc"], + }); security_guide_search_key_1: |- - const client = new MeiliSearch({ host: 'http://localhost:7700', apiKey: 'apiKey' }) - client.index('patient_medical_records').search() + const client = new MeiliSearch({ + host: "http://localhost:7700", + apiKey: "apiKey", + }); + + await client.index("patient_medical_records").search(); security_guide_update_key_1: |- - const client = new MeiliSearch({ host: 'http://localhost:7700', apiKey: 'masterKey' }) - client.updateKey('74c9c733-3368-4738-bbe5-1d18a5fecb37', { - description: 'Default Search API Key' - }) + const client = new MeiliSearch({ + host: "http://localhost:7700", + apiKey: "masterKey", + }); + + await client.updateKey("74c9c733-3368-4738-bbe5-1d18a5fecb37", { + description: "Default Search API Key", + }); security_guide_create_key_1: |- - const client = new MeiliSearch({ host: 'http://localhost:7700', apiKey: 'masterKey' }) - client.createKey({ - description: 'Search patient records key', - actions: ['search'], - indexes: ['patient_medical_records'], - expiresAt: '2023-01-01T00:00:00Z' - }) + const client = new MeiliSearch({ + host: "http://localhost:7700", + apiKey: "masterKey", + }); + + await client.createKey({ + description: "Search patient records key", + actions: ["search"], + indexes: ["patient_medical_records"], + expiresAt: new Date("2023-01-01T00:00:00Z"), + }); security_guide_list_keys_1: |- - const client = new MeiliSearch({ host: 'http://localhost:7700', apiKey: 'masterKey' }) - client.getKeys() + await client.getKeys(); security_guide_delete_key_1: |- - const client = new MeiliSearch({ host: 'http://localhost:7700', apiKey: 'masterKey' }) - client.deleteKey('ac5cd97d-5a4b-4226-a868-2d0eb6d197ab') + const client = new MeiliSearch({ + host: "http://localhost:7700", + apiKey: "masterKey", + }); + + await client.deleteKey("ac5cd97d-5a4b-4226-a868-2d0eb6d197ab"); authorization_header_1: |- - const client = new MeiliSearch({ host: 'http://localhost:7700', apiKey: 'masterKey' }) - client.getKeys() + await client.getKeys(); tenant_token_guide_generate_sdk_1: |- - import { generateTenantToken } from 'meilisearch/token' - - const searchRules = { - patient_medical_records: { - filter: 'user_id = 1' - } - } - const apiKey = 'B5KdX2MY2jV6EXfUs6scSfmC...' - const apiKeyUid = '85c3c2f9-bdd6-41f1-abd8-11fcf80e0f76' - const expiresAt = new Date('2025-12-20') // optional + import { generateTenantToken } from "meilisearch/token"; - const token = await generateTenantToken({ apiKey, apiKeyUid, searchRules, expiresAt }) + await generateTenantToken({ + apiKey: "B5KdX2MY2jV6EXfUs6scSfmC...", + apiKeyUid: "85c3c2f9-bdd6-41f1-abd8-11fcf80e0f76", + searchRules: { patient_medical_records: { filter: "user_id = 1" } }, + expiresAt: new Date("2025-12-20"), // optional + }); tenant_token_guide_search_sdk_1: |- - const frontEndClient = new MeiliSearch({ host: 'http://localhost:7700', apiKey: token }) - frontEndClient.index('patient_medical_records').search('blood test') + const frontEndClient = new MeiliSearch({ + host: "http://localhost:7700", + apiKey: "", + }); + + await frontEndClient.index("patient_medical_records").search("blood test"); landing_getting_started_1: |- - const client = new MeiliSearch('http://localhost:7700', 'masterKey') + const client = new MeiliSearch({ + host: "http://localhost:7700", + apiKey: "masterKey", + }); - await client.index('movies').addDocuments([ - { 'id': 1, 'title': 'Carol' }, - { 'id': 2, 'title': 'Wonder Woman' }, - { 'id': 3, 'title': 'Life of Pi' }, - { 'id': 4, 'title': 'Mad Max: Fury Road' }, - { 'id': 5, 'title': 'Moana' }, - { 'id': 6, 'title': 'Philadelphia'} - ]) + await client.index("movies").addDocuments([ + { id: 1, title: "Carol" }, + { id: 2, title: "Wonder Woman" }, + { id: 3, title: "Life of Pi" }, + { id: 4, title: "Mad Max: Fury Road" }, + { id: 5, title: "Moana" }, + { id: 6, title: "Philadelphia" }, + ]); // be aware this client is using the masterKey, it should not be used in front end - const search = await index.search('philodelphia') - console.log(search) + const search = await client.index("movies").search("philodelphia"); + console.log(search); facet_search_1: |- - client.index('books').searchForFacetValues({ - facetQuery: 'fiction', - facetName: 'genres' - filter: 'rating > 3' - }) + await client.index("books").searchForFacetValues({ + facetQuery: "fiction", + facetName: "genres", + filter: "rating > 3", + }); facet_search_2: |- - client.index('books').updateFaceting({ + await client.index("books").updateFaceting({ sortFacetValuesBy: { - genres: 'count' - } - }) + genres: "count", + }, + }); facet_search_3: |- - client.index('books').searchForFacetValues({ - facetQuery: 'c', - facetName: 'genres' - }) + await client.index("books").searchForFacetValues({ + facetQuery: "c", + facetName: "genres", + }); search_parameter_guide_show_ranking_score_details_1: |- - client.index('movies').search('dragon', { showRankingScoreDetails: true }) + await client + .index("movies") + .search("dragon", { showRankingScoreDetails: true }); negative_search_1: |- - client.index('movies').search('-escape') + await client.index("movies").search("-escape"); negative_search_2: |- - client.index('movies').search('-"escape"') + await client.index("movies").search('-"escape"'); search_parameter_reference_ranking_score_threshold_1: |- - client.index('INDEX_NAME').search('badman', { rankingScoreThreshold: 0.2 }) + await client + .index("INDEX_NAME") + .search("badman", { rankingScoreThreshold: 0.2 }); search_parameter_reference_retrieve_vectors_1: |- - client.index('INDEX_NAME').search('kitchen utensils', { + await client.index("INDEX_NAME").search("kitchen utensils", { retrieveVectors: true, hybrid: { - embedder: 'EMBEDDER_NAME' - } - }) + embedder: "EMBEDDER_NAME", + }, + }); search_parameter_guide_hybrid_1: |- - client.index('INDEX_NAME').search('kitchen utensils', { + await client.index("INDEX_NAME").search("kitchen utensils", { hybrid: { semanticRatio: 0.9, - embedder: 'EMBEDDER_NAME' - } - }) + embedder: "EMBEDDER_NAME", + }, + }); get_similar_post_1: |- - client.index('INDEX_NAME').searchSimilarDocuments({ id: 'TARGET_DOCUMENT_ID', embedder: 'default' }) + await client + .index("INDEX_NAME") + .searchSimilarDocuments({ id: "TARGET_DOCUMENT_ID", embedder: "default" }); search_parameter_guide_matching_strategy_3: |- - client.index('movies').search('white shirt', { - matchingStrategy: 'frequency' - }) + await client.index("movies").search("white shirt", { + matchingStrategy: "frequency", + }); search_parameter_reference_distinct_1: |- - client.index('INDEX_NAME').search('QUERY TERMS', { distinct: 'ATTRIBUTE_A' }) + await client + .index("INDEX_NAME") + .search("QUERY TERMS", { distinct: "ATTRIBUTE_A" }); distinct_attribute_guide_filterable_1: |- - client.index('products').updateFilterableAttributes(['product_id', 'sku', 'url']) + await client + .index("products") + .updateFilterableAttributes(["product_id", "sku", "url"]); distinct_attribute_guide_distinct_parameter_1: |- - client.index('products').search('white shirt', { distinct: 'sku' }) + await client.index("products").search("white shirt", { distinct: "sku" }); multi_search_federated_1: |- - client.multiSearch({ + await client.multiSearch({ federation: {}, queries: [ { - indexUid: 'movies', - q: 'batman', + indexUid: "movies", + q: "batman", }, { - indexUid: 'comics', - q: 'batman', + indexUid: "comics", + q: "batman", }, - ] - }) + ], + }); search_parameter_reference_locales_1: |- - client.index('INDEX_NAME').search('QUERY TEXT IN JAPANESE', { locales: ['jpn'] }) + await client + .index("INDEX_NAME") + .search("QUERY TEXT IN JAPANESE", { locales: ["jpn"] }); get_localized_attribute_settings_1: |- - client.index('INDEX_NAME').getLocalizedAttributes() + await client.index("INDEX_NAME").getLocalizedAttributes(); update_localized_attribute_settings_1: |- - client.index('INDEX_NAME').updateLocalizedAttributes([ - { attributePatterns: ['*_ja'], locales: ['jpn'] }, - ]) + await client + .index("INDEX_NAME") + .updateLocalizedAttributes([ + { attributePatterns: ["*_ja"], locales: ["jpn"] }, + ]); reset_localized_attribute_settings_1: |- - client.index('INDEX_NAME').resetLocalizedAttributes() + await client.index("INDEX_NAME").resetLocalizedAttributes(); get_facet_search_settings_1: |- - client.index('INDEX_NAME').getFacetSearch(); + await client.index("INDEX_NAME").getFacetSearch(); update_facet_search_settings_1: |- - client.index('INDEX_NAME').updateFacetSearch(false); + await client.index("INDEX_NAME").updateFacetSearch(false); reset_facet_search_settings_1: |- - client.index('INDEX_NAME').resetFacetSearch(); + await client.index("INDEX_NAME").resetFacetSearch(); get_prefix_search_settings_1: |- - client.index('INDEX_NAME').getPrefixSearch(); + await client.index("INDEX_NAME").getPrefixSearch(); update_prefix_search_settings_1: |- - client.index('INDEX_NAME').updatePrefixSearch('disabled'); + await client.index("INDEX_NAME").updatePrefixSearch("disabled"); reset_prefix_search_settings_1: |- - client.index('INDEX_NAME').resetPrefixSearch(); + await client.index("INDEX_NAME").resetPrefixSearch(); get_all_batches_1: |- - client.batches.getBatches(); + await client.batches.getBatches(); get_batch_1: |- - client.batches.getBatch(BATCH_UID); -# Vector search + await client.batches.getBatch(123); update_embedders_1: |- - client.index('INDEX_NAME').updateEmbedders({ + await client.index("INDEX_NAME").updateEmbedders({ default: { - source: 'openAi', - apiKey: 'OPEN_AI_API_KEY', - model: 'text-embedding-3-small', - documentTemplate: 'A document titled '{{doc.title}}' whose description starts with {{doc.overview|truncatewords: 20}}' - } + source: "openAi", + apiKey: "OPEN_AI_API_KEY", + model: "text-embedding-3-small", + documentTemplate: + "A document titled '{{doc.title}}' whose description starts with {{doc.overview|truncatewords: 20}}", + }, }); diff --git a/.gitignore b/.gitignore index 868fe3fb0..4ee31bb32 100644 --- a/.gitignore +++ b/.gitignore @@ -133,3 +133,4 @@ package dist_default_export_in_index no_default_export_in_index playgrounds/javascript/yarn.lock +generated-code-samples diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aea3406bf..071853847 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -70,6 +70,25 @@ yarn style:fix yarn build ``` +### Code samples + +In this repository, code samples are linted and type-checked. To achieve this, +we generate TypeScript files from `.code-samples.meilisearch.yaml` and vice +versa. + +> [!WARNING] +> +> Each of these commands overwrite their target files, so use them carefully. + +```bash +# Generate all the code sample files from YAML file +yarn generate-code-sample-files +# Generate YAML file from all the code sample files +yarn generate-code-samples-yaml +# Generate all + new code sample files from YAML file +yarn generate-code-sample-files new_sample_one new_sample_two +``` + ## Git Guidelines ### Git Branches diff --git a/package.json b/package.json index 4121006e3..03d9304e5 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,7 @@ "build": "vite build && tsc -p tsconfig.build.json && vite --mode production-umd build", "postbuild": "node scripts/build.js", "test": "vitest run --coverage", - "types": "tsc -p tsconfig.json --noEmit", + "types": "yarn generate-code-sample-files && tsc -p tsconfig.json --noEmit", "types:watch": "yarn types --watch", "test:env:browser": "yarn build && node scripts/copy-umd-file.js --to ./tests/env/express/public && yarn --cwd tests/env/express && yarn --cwd tests/env/express test", "test:watch": "vitest watch", @@ -55,12 +55,14 @@ "test:env:nodejs": "yarn build && node tests/env/node/index.cjs && node tests/env/node/getting_started.cjs", "test:env:esm": "yarn --cwd tests/env/esm && yarn --cwd tests/env/esm start", "test:env:nitro-app": "yarn build && yarn --cwd tests/env/nitro-app test", + "generate-code-sample-files": "node scripts/code-samples.js from-yaml", + "generate-code-samples-yaml": "node scripts/code-samples.js to-yaml", "fmt": "prettier -c ./**/*.{js,ts}", "fmt:fix": "prettier -w ./**/*.{js,ts}", "lint": "eslint", "lint:fix": "eslint --fix", - "style": "yarn fmt && yarn lint", - "style:fix": "yarn fmt:fix && yarn lint:fix", + "style": "yarn generate-code-sample-files && prettier --ignore-path=.prettierignore -c generated-code-samples && yarn fmt && yarn lint", + "style:fix": "yarn generate-code-sample-files && prettier --ignore-path=.prettierignore -c generated-code-samples && yarn fmt:fix && yarn lint:fix", "prepare": "husky" }, "files": [ @@ -70,22 +72,24 @@ ], "devDependencies": { "@eslint/js": "^9.23.0", - "@vitest/coverage-v8": "^3.1.1", + "@types/js-yaml": "^4.0.9", "@types/node": "^22.15.3", "@typescript-eslint/utils": "^8.29.0", + "@vitest/coverage-v8": "^3.1.1", "@vitest/eslint-plugin": "^1.1.38", "eslint": "^9.25.1", "eslint-config-prettier": "^10.1.2", "eslint-plugin-tsdoc": "^0.4.0", - "typescript": "^5.8.3", - "vite": "^6.3.4", "globals": "^16.0.0", "husky": "^9.1.7", + "js-yaml": "^4.1.0", "lint-staged": "15.5.1", "prettier": "^3.5.3", "prettier-plugin-jsdoc": "^1.3.2", "typedoc": "^0.28.3", + "typescript": "^5.8.3", "typescript-eslint": "^8.31.1", + "vite": "^6.3.4", "vitest": "^3.1.1" }, "packageManager": "yarn@1.22.22" diff --git a/scripts/code-samples.js b/scripts/code-samples.js new file mode 100644 index 000000000..a1868e2bb --- /dev/null +++ b/scripts/code-samples.js @@ -0,0 +1,11 @@ +import { argv } from "node:process"; + +if (argv[2] === "to-yaml" && argv.length === 3) { + await import("./code-samples/to-yaml.js"); +} else if (argv[2] === "from-yaml") { + await import("./code-samples/from-yaml.js"); +} else { + throw new Error( + "expected `to-yaml` (+ new code samples names) or `from-yaml` as arguments", + ); +} diff --git a/scripts/code-samples/from-yaml.js b/scripts/code-samples/from-yaml.js new file mode 100644 index 000000000..5adc76fa2 --- /dev/null +++ b/scripts/code-samples/from-yaml.js @@ -0,0 +1,80 @@ +import { argv } from "node:process"; +import { writeFileSync, mkdirSync } from "node:fs"; +import { + generatedCodeSamplesDir, + iterateCodeSamples, + delimiter, +} from "./shared.js"; + +const headerImport = 'import { MeiliSearch } from "meilisearch";\n'; +const headerClientDeclaration = + 'const client = new MeiliSearch({ host: "http://127.0.0.1:7700" });\n'; +const headerComment = + "// Code below this line will be written to code samples YAML file\n" + + '// For more information consult CONTRIBUTING.md "Tests and Linter" section\n' + + delimiter + + "\n"; + +const jsonFilesToGenerate = ["games", "movies", "meteorites"]; + +try { + mkdirSync(generatedCodeSamplesDir); +} catch (error) { + if (error.code !== "EEXIST") { + throw error; + } +} + +// generate JSON files used by samples, so type check passes +for (const jsonFileToGenerate of jsonFilesToGenerate) { + writeFileSync( + new URL(jsonFileToGenerate + ".json", generatedCodeSamplesDir), + "[]\n", + ); +} + +const clientVarRegExp = /(?<=const|let ).+(?= = new MeiliSearch\()/; + +let generatedFileTally = 0; + +for (const { sampleName, code } of iterateCodeSamples()) { + let header = ""; + + const clientVarMatch = code.match(clientVarRegExp); + const clientVarLiteral = clientVarMatch?.[0] ?? "client"; + const clientVarUsageRegExp = new RegExp(`${clientVarLiteral}\\s*\\.`); + + // if there is client usage in the code sample + if (clientVarUsageRegExp.test(code)) { + // generate import if there isn't already one + if (!code.includes('from "meilisearch";\n')) { + header += headerImport; + } + + // generate client declaration if there isn't already one + if (clientVarMatch === null) { + header += headerClientDeclaration; + } + } + + header += headerComment; + + writeFileSync( + new URL(sampleName + ".ts", generatedCodeSamplesDir), + header + code + "\n", + ); + + generatedFileTally += 1; +} + +// generate additional files from arguments passed +for (const sampleName of argv.slice(3)) { + writeFileSync( + new URL(sampleName + ".ts", generatedCodeSamplesDir), + headerImport + headerClientDeclaration + headerComment, + ); + + generatedFileTally += 1; +} + +console.log(`generated ${generatedFileTally} code sample file(s)`); diff --git a/scripts/code-samples/shared.js b/scripts/code-samples/shared.js new file mode 100644 index 000000000..922e4f3bb --- /dev/null +++ b/scripts/code-samples/shared.js @@ -0,0 +1,59 @@ +import { readFileSync } from "node:fs"; +import { load } from "js-yaml"; + +export const codeSamplesPath = new URL( + "../../.code-samples.meilisearch.yaml", + import.meta.url, +); + +export const generatedCodeSamplesDir = new URL( + "../../generated-code-samples/", + import.meta.url, +); + +export const delimiter = "// -~-~-~-~-"; + +export function* iterateCodeSamples() { + let codeSamplesContents; + try { + codeSamplesContents = readFileSync(codeSamplesPath, { + encoding: "utf-8", + }); + } catch (error) { + if (error?.code !== "ENOENT") { + throw error; + } + + return; + } + + const codeSamples = load(codeSamplesContents, { + filename: codeSamplesPath.href, + onWarning: console.warn, + }); + + // YAML file is empty + if (codeSamples === undefined) { + return; + } + + if (codeSamples === null || typeof codeSamples !== "object") { + throw new Error( + `expected YAML contents to be of type \`Record\`; got ${String(codeSamples)}`, + { + cause: codeSamples, + }, + ); + } + + for (const [sampleName, code] of Object.entries(codeSamples)) { + if (typeof code !== "string") { + throw new Error( + `expected YAML contents to be of type \`Record\`; at ${sampleName} got ${String(code)}`, + { cause: code }, + ); + } + + yield { sampleName, code }; + } +} diff --git a/scripts/code-samples/to-yaml.js b/scripts/code-samples/to-yaml.js new file mode 100644 index 000000000..03be665c9 --- /dev/null +++ b/scripts/code-samples/to-yaml.js @@ -0,0 +1,101 @@ +import { readdir } from "node:fs/promises"; +import { readFileSync, writeFileSync } from "node:fs"; +import { parse, join } from "node:path"; +import { + codeSamplesPath, + generatedCodeSamplesDir, + iterateCodeSamples, + delimiter, +} from "./shared.js"; + +const codeSampleNamesFromYaml = Array.from( + iterateCodeSamples(), + (v) => v.sampleName, +); + +/** @type {import("node:fs").Dirent[]} */ +const dirEntries = await readdir(generatedCodeSamplesDir, { + withFileTypes: true, +}).catch((error) => { + if (error?.code !== "ENOENT") { + throw error; + } + + return []; +}); + +if (dirEntries.length === 0) { + throw new Error( + `there are no code sample files at ${generatedCodeSamplesDir.href}\n` + + "tip: first generate them from the YAML file, consult CONTRIBUTING.md on how to use this script", + ); +} + +function throwError() { + throw new Error( + `expected generated code samples directory at ${generatedCodeSamplesDir.href} to only contain TypeScript and JSON files`, + { cause: dirEntries }, + ); +} + +const manipulatedCodeSamples = dirEntries + .map((dirEnt) => { + if (!dirEnt.isFile()) { + throwError(); + } + + const { ext, name } = parse(dirEnt.name); + if (ext !== ".ts" && ext !== ".json") { + throwError(); + } + + if (ext === ".json") { + return null; + } + + const codeSampleContent = readFileSync( + join(dirEnt.parentPath, dirEnt.name), + { encoding: "utf-8" }, + ); + + const splitContent = codeSampleContent.split("\n"); + const indexOfDelimiter = splitContent.findIndex((v) => v === delimiter); + + const indentedContent = splitContent + // get rid of code before delimiter + .slice(indexOfDelimiter === -1 ? 0 : indexOfDelimiter + 1) + // add padding + .map((v) => (v === "" ? v : " " + v)) + .join("\n") + .trimEnd(); + + // get position in current code samples YAML file, to be able to order it the same way + const index = codeSampleNamesFromYaml.indexOf(name); + + return { name, indentedContent, index }; + }) + .filter((v) => v !== null) + .sort(({ index: indexA }, { index: indexB }) => indexA - indexB); + +// for every new code sample, place them at the end of the file instead of the start +if (manipulatedCodeSamples.some((v) => v.index !== -1)) { + while (manipulatedCodeSamples.at(0)?.index === -1) { + manipulatedCodeSamples.push(manipulatedCodeSamples.shift()); + } +} + +const serializedCodeSamples = manipulatedCodeSamples + .map(({ name, indentedContent }) => name + ": |-\n" + indentedContent) + .join("\n"); + +const header = + "# This code-samples file is used by the Meilisearch documentation.\n" + + "# Every example written here will be automatically fetched by\n" + + "# the documentation on build.\n" + + "# You can read more at https://github.com/meilisearch/documentation\n" + + '# This file is generated, read more in CONTRIBUTING.md "Tests and Linter" section.\n' + + "---\n"; + +writeFileSync(codeSamplesPath, header + serializedCodeSamples + "\n"); + +console.log(`generated ${manipulatedCodeSamples.length} code sample(s)`); diff --git a/tsconfig.json b/tsconfig.json index 2c9559fc9..0bb66c7e7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -8,6 +8,12 @@ "lib": ["ESNext", "DOM", "DOM.Iterable"], "resolveJsonModule": true, "strict": true, - "verbatimModuleSyntax": true + "verbatimModuleSyntax": true, + // For testing purposes + // TODO: Should use this for tests as well perhaps, resolve.alias + "paths": { + "meilisearch": ["./src/index.ts"], + "meilisearch/token": ["./src/token.ts"] + } } } diff --git a/yarn.lock b/yarn.lock index 827be91b8..3f010e7cc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -539,6 +539,11 @@ dependencies: "@types/unist" "*" +"@types/js-yaml@^4.0.9": + version "4.0.9" + resolved "https://registry.yarnpkg.com/@types/js-yaml/-/js-yaml-4.0.9.tgz#cd82382c4f902fed9691a2ed79ec68c5898af4c2" + integrity sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg== + "@types/json-schema@^7.0.15": version "7.0.15" resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"