diff --git a/.code-samples.meilisearch.yaml b/.code-samples.meilisearch.yaml index 8af0f27..db106fe 100644 --- a/.code-samples.meilisearch.yaml +++ b/.code-samples.meilisearch.yaml @@ -2,76 +2,25 @@ # Every example written here will be automatically fetched by # the documentation and the landing page on build --- -search_parameter_guide_show_ranking_score_1: |- - await client.index('movies').search('dragon', SearchQuery(showRankingScore: true)); -facet_search_1: |- - await client.index('books').facetSearch(FacetSearchQuery( - facetQuery: 'fiction', - facetName: 'genres', - filter: 'rating > 3' - ), - ); -facet_search_2: |- - await client.index('books').updateFaceting(Faceting(sortFacetValuesBy: {'genres': 'count'})); -facet_search_3: |- - await client.index('books').facetSearch(FacetSearchQuery( - facetQuery: 'c', - facetName: 'genres', - ), - ); -search_parameter_guide_attributes_to_search_on_1: |- - await client.index('movies').search('adventure', SearchQuery(attributesToSearchOn: ['overview'])); -get_documents_post_1: |- - await client.index('movies').getDocuments( - params: DocumentsQuery( - filter: '(rating > 3 AND (genres = Adventure OR genres = Fiction)) AND language = English', - fields: ['title', 'genres', 'rating', 'language'], - limit: 3 - ) - ); -delete_documents_by_filter_1: |- - await client.index('movies') - .deleteDocuments(DeleteDocumentsQuery(filter: 'genres = action OR genres = adventure')); -get_documents_1: |- - await client.index('movies') - .getDocuments(params: DocumentsQuery(limit: 2, filter: 'genres = action')); -multi_search_1: |- - await client.multiSearch(MultiSearchQuery(queries: [ - IndexSearchQuery( - query: 'pooh', - indexUid: 'movies', - limit: 5 - ), - IndexSearchQuery( - query: 'nemo', - indexUid: 'movies', - limit: 5 - ), - IndexSearchQuery( - query: 'us', - indexUid: 'movies_ratings' - ), - ])); -faceted_search_update_settings_1: |- - await client.index('movie_ratings').updateFilterableAttributes(['genres', 'rating', 'language']); -search_parameter_guide_facet_stats_1: |- - await client.index('movie_ratings').search('Batman', SearchQuery(facets: ['genres', 'rating'])); -faceted_search_1: |- - await client.index('books').search('', SearchQuery(facets: ['genres', 'rating', 'language'])); -filtering_guide_nested_1: |- - await client.index('movie_ratings').search('thriller', SearchQuery(filter: 'rating.users >= 90')); -sorting_guide_sort_nested_1: |- - await client.index('movie_ratings').search('thriller', SearchQuery(sort: ['rating.users:asc'])); -async_guide_filter_by_date_1: |- - await client.getTasks(params: TasksQuery(afterEnqueuedAt: DateTime(2020, 10, 11, 11, 49, 53))); -async_guide_multiple_filters_1: |- - await client.getTasks(params: TasksQuery(indexUids: ['movies'], types: ['documentAdditionOrUpdate','documentDeletion'], statuses: ['processing'])); -async_guide_filter_by_ids_1: |- - await client.getTasks(params: TasksQuery(uids: [5, 10, 13])); -async_guide_filter_by_statuses_1: |- - await client.getTasks(params: TasksQuery(statuses: ['failed', 'canceled'])); -async_guide_filter_by_types_1: |- - await client.getTasks(params: TasksQuery(types: ['dumpCreation', 'indexSwap'])); +search_parameter_guide_show_ranking_score_1: "await client\n .index('movies')\n .search('dragon', SearchQuery(showRankingScore: true));" +facet_search_1: "await client.index('books').facetSearch(\n FacetSearchQuery(\n facetQuery: 'fiction',\n facetName: 'genres',\n filter: 'rating > 3',\n ),\n );" +facet_search_2: "await client.index('books').updateFaceting(\n Faceting(\n sortFacetValuesBy: {\n 'genres': FacetingSortTypes.count,\n },\n ),\n );" +facet_search_3: "await client.index('books').facetSearch(\n FacetSearchQuery(\n facetQuery: 'c',\n facetName: 'genres',\n ),\n );" +search_parameter_guide_attributes_to_search_on_1: "await client.index('books').facetSearch(\n FacetSearchQuery(\n facetQuery: 'c',\n facetName: 'genres',\n ),\n );" +get_documents_post_1: "await client.index('movies').getDocuments(\n params: DocumentsQuery(\n filterExpression: Meili.and([\n 'language'.toMeiliAttribute().eq('English'.toMeiliValue()),\n Meili.and([\n 'rating'.toMeiliAttribute().gt(3.toMeiliValue()),\n Meili.or([\n 'genres'.toMeiliAttribute().eq('Adventure'.toMeiliValue()),\n 'genres'.toMeiliAttribute().eq('Fiction'.toMeiliValue()),\n ]),\n ]),\n ]),\n fields: ['title', 'genres', 'rating', 'language'],\n limit: 3,\n ),\n );" +delete_documents_by_filter_1: "await client.index('movies').deleteDocuments(\n DeleteDocumentsQuery(\n filterExpression: Meili.or([\n Meili.attr('genres').eq(Meili.value('action')),\n Meili.attr('genres').eq(Meili.value('adventure')),\n ]),\n ),\n );" +get_documents_1: "await client.index('movies').getDocuments(\n params: DocumentsQuery(\n limit: 2,\n filter: Meili.attr('genres').eq('action'.toMeiliValue()),\n ),\n );" +multi_search_1: "await client.multiSearch(MultiSearchQuery(queries: [\n IndexSearchQuery(query: 'pooh', indexUid: 'movies', limit: 5),\n IndexSearchQuery(query: 'nemo', indexUid: 'movies', limit: 5),\n IndexSearchQuery(query: 'us', indexUid: 'movies_ratings'),\n]));" +faceted_search_update_settings_1: "await client\n .index('movie_ratings')\n .updateFilterableAttributes(['genres', 'rating', 'language']);" +search_parameter_guide_facet_stats_1: "await client\n .index('movie_ratings')\n .search('Batman', SearchQuery(facets: ['genres', 'rating']));" +faceted_search_1: "await client\n .index('books')\n .search('', SearchQuery(facets: ['genres', 'rating', 'language']));" +filtering_guide_nested_1: "await client.index('movie_ratings').search(\n 'thriller',\n SearchQuery(\n filterExpression: Meili.gte(\n \/\/or Meili.attr('rating.users')\n \/\/or 'rating.users'.toMeiliAttribute()\n Meili.attrFromParts(['rating', 'users']),\n Meili.value(90),\n ),\n ),\n );" +sorting_guide_sort_nested_1: "await client\n .index('movie_ratings')\n .search('thriller', SearchQuery(sort: ['rating.users:asc']));" +async_guide_filter_by_date_1: "await client.getTasks(\n params: TasksQuery(\n afterEnqueuedAt: DateTime(2020, 10, 11, 11, 49, 53),\n ),\n);" +async_guide_multiple_filters_1: "await client.getTasks(\n params: TasksQuery(\n indexUids: ['movies'],\n types: ['documentAdditionOrUpdate', 'documentDeletion'],\n statuses: ['processing'],\n ),\n);" +async_guide_filter_by_ids_1: "await client.getTasks(\n params: TasksQuery(\n uids: [5, 10, 13],\n ),\n);" +async_guide_filter_by_statuses_1: "await client.getTasks(\n params: TasksQuery(\n statuses: ['failed', 'canceled'],\n ),\n);" +async_guide_filter_by_types_1: "await client.getTasks(\n params: TasksQuery(\n types: ['dumpCreation', 'indexSwap'],\n ),\n);" async_guide_filter_by_index_uids_1: |- await client.getTasks(params: TasksQuery(indexUids: ['movies'])); delete_tasks_1: |- @@ -80,48 +29,27 @@ cancel_tasks_1: |- await client.cancelTasks(params: CancelTasksQuery(uids: [1, 2])); async_guide_canceled_by_1: |- await client.getTasks(params: TasksQuery(canceledBy: [9, 15])); -swap_indexes_1: |- - await client.swapIndexes([SwapIndex(['indexA', 'indexB']), SwapIndex(['indexX', 'indexY'])]); -search_parameter_guide_hitsperpage_1: |- - await client.index('movies').search('', SearchQuery(hitsPerPage: 15)) as PaginatedSearchResult; -search_parameter_guide_page_1: |- - await client.index('movies').search('', SearchQuery(page: 2)) as PaginatedSearchResult; -synonyms_guide_1: |- - await client.index('movies').updateSynonyms({ - 'great': ['fantastic'], - 'fantastic': ['great'] - }); -date_guide_index_1: |- - import 'dart:io'; - import 'dart:convert'; - - final json = await File('games.json').readAsString(); - - await client.index('games').addDocumentsJson(json); -date_guide_filterable_attributes_1: |- - await client.index('games').updateFilterableAttributes(['release_timestamp']); -date_guide_filter_1: |- - await client.index('games').search('', - SearchQuery(filter: - 'release_timestamp >= 1514761200 AND release_timestamp < 1672527600')); -date_guide_sortable_attributes_1: |- - await client.index('games').updateSortableAttributes(['release_timestamp']); -date_guide_sort_1: |- - await client.index('games').search('', SearchQuery(sort: ['release_timestamp:desc'])); +swap_indexes_1: "await client.swapIndexes([\n SwapIndex(['indexA', 'indexB']),\n SwapIndex(['indexX', 'indexY']),\n]);" +search_parameter_guide_hitsperpage_1: "await client\n .index('movies')\n .search('', SearchQuery(hitsPerPage: 15))\n .asPaginatedResult();" +search_parameter_guide_page_1: "await client\n .index('movies')\n .search('', SearchQuery(page: 2))\n .asPaginatedResult();" +synonyms_guide_1: "await client.index('movies').updateSynonyms({\n 'great': ['fantastic'],\n 'fantastic': ['great'],\n});" +date_guide_index_1: "\/\/import 'dart:io';\n\/\/import 'dart:convert';\nfinal json = await File('games.json').readAsString();\nawait client.index('games').addDocumentsJson(json);" +date_guide_filterable_attributes_1: "await client\n .index('games')\n .updateFilterableAttributes(['release_timestamp']);" +date_guide_filter_1: "await client.index('games').search(\n '',\n SearchQuery(\n filterExpression: Meili.and([\n Meili.gte(\n 'release_timestamp'.toMeiliAttribute(),\n Meili.value(DateTime(2017, 12, 31, 23, 0)),\n ),\n Meili.lt(\n 'release_timestamp'.toMeiliAttribute(),\n Meili.value(DateTime(2022, 12, 31, 23, 0)),\n ),\n ]),\n ),\n );" +date_guide_sortable_attributes_1: "await client\n .index('games')\n .updateSortableAttributes(['release_timestamp']);" +date_guide_sort_1: "await client\n .index('games')\n .search('', SearchQuery(sort: ['release_timestamp:desc']));" get_all_tasks_paginating_1: |- await client.getTasks(params: TasksQuery(limit: 2, from: 10)); get_all_tasks_paginating_2: |- await client.getTasks(params: TasksQuery(limit: 2, from: 8)); get_pagination_settings_1: |- await client.index('movies').getPagination(); -update_pagination_settings_1: |- - await client.index('books').updatePagination(Pagination(maxTotalHits: 100)); +update_pagination_settings_1: "await client\n .index('books')\n .updatePagination(Pagination(maxTotalHits: 100));" reset_pagination_settings_1: |- await client.index('movies').resetPagination(); get_faceting_settings_1: |- await client.index('movies').getFaceting(); -update_faceting_settings_1: |- - await client.index('books').updateFaceting(Faceting(maxValuesPerFacet: 2, sortFacetValuesBy: {'*': 'alpha', 'genres': 'count'})); +update_faceting_settings_1: "await client.index('books').updateFaceting(Faceting(\n maxValuesPerFacet: 2,\n sortFacetValuesBy: {\n '*': FacetingSortTypes.alpha,\n 'genres': FacetingSortTypes.count\n }));" reset_faceting_settings_1: |- await client.index('movies').resetFaceting(); get_one_index_1: |- @@ -134,8 +62,7 @@ update_an_index_1: |- await client.index('movies').update(primaryKey: 'id'); delete_an_index_1: |- await client.index('movies').delete(); -get_one_document_1: |- - await client.index('movies').getDocument(25684, fields: ['id', 'title', 'poster', 'release_date']); +get_one_document_1: "await client.index('movies').getDocument(25684,\n fields: ['id', 'title', 'poster', 'release_date']);" add_or_replace_documents_1: |- await client.index('movies').addDocuments([ { @@ -148,21 +75,12 @@ add_or_replace_documents_1: |- 'release_date': '2019-03-23' } ]); -add_or_update_documents_1: |- - await client.index('movies').updateDocuments([ - { - 'id': 287947, - 'title': 'Shazam ⚡️', - 'genres': 'comedy' - } - ]); +add_or_update_documents_1: "await client.index('movies').updateDocuments([\n {\n 'id': 287947,\n 'title': 'Shazam ⚡️',\n 'genres': 'comedy',\n }\n]);" delete_all_documents_1: |- await client.index('movies').deleteAllDocuments(); delete_one_document_1: |- await client.index('movies').deleteDocument(25684); -delete_documents_by_batch_1: |- - await client.index('movies') - .deleteDocuments(DeleteDocumentsQuery(ids: [23488, 153738, 437035, 363869])); +delete_documents_by_batch_1: "await client.index('movies').deleteDocuments(\n DeleteDocumentsQuery(\n ids: [23488, 153738, 437035, 363869],\n ),\n );" search_post_1: |- await client.index('movies').search('American ninja'); get_task_1: |- @@ -171,40 +89,12 @@ get_all_tasks_1: |- await client.getTasks(); get_settings_1: |- await client.index('movies').getSettings(); -update_settings_1: |- - await client.index('movies').updateSettings(IndexSettings( - 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'], - }, - )); +update_settings_1: "await client.index('movies').updateSettings(\n IndexSettings(\n rankingRules: [\n 'words',\n 'typo',\n 'proximity',\n 'attribute',\n 'sort',\n 'exactness',\n 'release_date:desc',\n 'rank:desc'\n ],\n distinctAttribute: 'movie_id',\n searchableAttributes: ['title', 'overview', 'genres'],\n displayedAttributes: [\n 'title',\n 'overview',\n 'genres',\n 'release_date'\n ],\n stopWords: ['the', 'a', 'an'],\n sortableAttributes: ['title', 'release_date'],\n synonyms: {\n 'wolverine': ['xmen', 'logan'],\n 'logan': ['wolverine'],\n },\n ),\n );" reset_settings_1: |- await client.index('movies').resetSettings(); get_synonyms_1: |- await client.index('movies').getSynonyms(); -update_synonyms_1: |- - await client.index('movies').updateSynonyms( - { - 'wolverine': ['xmen', 'logan'], - 'logan': ['wolverine', 'xmen'], - 'wow': ['world of warcraft'] - } - ); +update_synonyms_1: "await client.index('movies').updateSynonyms({\n 'wolverine': ['xmen', 'logan'],\n 'logan': ['wolverine', 'xmen'],\n 'wow': ['world of warcraft'],\n});" reset_synonyms_1: |- await client.index('movies').resetSynonyms(); get_stop_words_1: |- @@ -215,17 +105,7 @@ reset_stop_words_1: |- await client.index('movies').resetStopWords(); get_ranking_rules_1: |- await client.index('movies').getRankingRules(); -update_ranking_rules_1: |- - await client.index('movies').updateRankingRules([ - 'words', - 'typo', - 'proximity', - 'attribute', - 'sort', - 'exactness', - 'release_date:asc', - 'rank:desc' - ]); +update_ranking_rules_1: "await client.index('movies').updateRankingRules([\n 'words',\n 'typo',\n 'proximity',\n 'attribute',\n 'sort',\n 'exactness',\n 'release_date:asc',\n 'rank:desc',\n]);" reset_ranking_rules_1: |- await client.index('movies').resetRankingRules(); get_distinct_attribute_1: |- @@ -236,32 +116,17 @@ reset_distinct_attribute_1: |- await client.index('shoes').resetDistinctAttribute(); get_filterable_attributes_1: |- await client.index('movies').getFilterableAttributes(); -update_filterable_attributes_1: |- - await client.index('movies').updateFilterableAttributes([ - 'genres', - 'director' - ]); +update_filterable_attributes_1: "await client\n .index('movies')\n .updateFilterableAttributes(['genres', 'director']);" reset_filterable_attributes_1: |- await client.index('movies').resetFilterableAttributes(); get_searchable_attributes_1: |- await client.index('movies').getSearchableAttributes(); -update_searchable_attributes_1: |- - await client.index('movies').updateSearchableAttributes([ - 'title', - 'overview', - 'genres' - ]); +update_searchable_attributes_1: "await client\n .index('movies')\n .updateSearchableAttributes(['title', 'overview', 'genres']);" reset_searchable_attributes_1: |- await client.index('movies').resetSearchableAttributes(); get_displayed_attributes_1: |- await client.index('movies').getDisplayedAttributes(); -update_displayed_attributes_1: |- - await client.index('movies').updateDisplayedAttributes([ - 'title', - 'overview', - 'genres', - 'release_date' - ]); +update_displayed_attributes_1: "await client.index('movies').updateDisplayedAttributes([\n 'title',\n 'overview',\n 'genres',\n 'release_date',\n]);" reset_displayed_attributes_1: |- await client.index('movies').resetDisplayedAttributes(); get_typo_tolerance_1: |- @@ -287,54 +152,24 @@ get_version_1: |- await client.getVersion(); distinct_attribute_guide_1: |- await client.index('jackets').updateDistinctAttribute('product_id'); -field_properties_guide_searchable_1: |- - await client.index('movies').updateSearchableAttributes(['title', 'overview', 'genres']); -field_properties_guide_displayed_1: |- - await client.index('movies').updateDisplayedAttributes(['title', 'overview', 'genres', 'release_date']); -filtering_guide_1: |- - await client - .index('movie_ratings') - .search('Avengers', SearchQuery(filter: 'release_date > 795484800')); -filtering_guide_2: |- - await client.index('movie_ratings') - .search('Batman', - SearchQuery(filter: 'release_date > 795484800 AND (director = "Tim Burton" OR director = "Christopher Nolan")' - ) - ); -filtering_guide_3: |- - await client.index('movie_ratings').search('Planet of the Apes', - SearchQuery(filter: 'release_date > 1577884550 AND (NOT director = "Tim Burton")')); +field_properties_guide_searchable_1: "await client\n .index('movies')\n .updateSearchableAttributes(['title', 'overview', 'genres']);" +field_properties_guide_displayed_1: "await client.index('movies').updateDisplayedAttributes([\n 'title',\n 'overview',\n 'genres',\n 'release_date',\n]);" +filtering_guide_1: "await client.index('movie_ratings').search(\n 'Avengers',\n SearchQuery(\n filterExpression: Meili.gt(\n Meili.attr('release_date'),\n DateTime.utc(1995, 3, 18).toMeiliValue(),\n ),\n ),\n );" +filtering_guide_2: "await client.index('movie_ratings').search(\n 'Batman',\n SearchQuery(\n filterExpression: Meili.and([\n Meili.attr('release_date')\n .gt(DateTime.utc(1995, 3, 18).toMeiliValue()),\n Meili.or([\n 'director'.toMeiliAttribute().eq('Tim Burton'.toMeiliValue()),\n 'director'\n .toMeiliAttribute()\n .eq('Christopher Nolan'.toMeiliValue()),\n ]),\n ]),\n ),\n );" +filtering_guide_3: "await client.index('movie_ratings').search(\n 'Planet of the Apes',\n SearchQuery(\n filterExpression: Meili.and([\n Meili.attr('release_date')\n .gt(DateTime.utc(2020, 1, 1, 13, 15, 50).toMeiliValue()),\n Meili.not(\n Meili.attr('director').eq(\"Tim Burton\".toMeiliValue()),\n ),\n ]),\n ),\n );" search_parameter_guide_query_1: |- await client.index('movies').search('shifu'); search_parameter_guide_offset_1: |- await client.index('movies').search('shifu', SearchQuery(offset: 1)); search_parameter_guide_limit_1: |- await client.index('movies').search('shifu', SearchQuery(limit: 2)); -search_parameter_guide_matching_strategy_1: |- - await client.index('movies').search('big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.last)); -search_parameter_guide_matching_strategy_2: |- - await client.index('movies').search('big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.all)); -search_parameter_guide_retrieve_1: |- - await client - .index('movies') - .search('shifu', SearchQuery(attributesToRetrieve: ['overview', 'title'])); -search_parameter_guide_crop_1: |- - await client - .index('movies') - .search('shifu', SearchQuery(attributesToCrop: ['overview'], cropLength: 5)); -search_parameter_guide_highlight_1: |- - await client - .index('movies') - .search('winter feast', SearchQuery(attributesToHighlight: ['overview'])); -search_parameter_guide_show_matches_position_1: |- - await client.index('movies').search('winter feast', SearchQuery(showMatchesPosition: true)); -add_movies_json_1: |- - import 'dart:io'; - import 'dart:convert'; - - final json = await File('movies.json').readAsString(); - - await client.index('movies').addDocumentsJson(json); +search_parameter_guide_matching_strategy_1: "await client.index('movies').search(\n 'big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.last));" +search_parameter_guide_matching_strategy_2: "await client.index('movies').search(\n 'big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.all));" +search_parameter_guide_retrieve_1: "await client.index('movies').search(\n 'shifu', SearchQuery(attributesToRetrieve: ['overview', 'title']));" +search_parameter_guide_crop_1: "await client.index('movies').search(\n 'shifu', SearchQuery(attributesToCrop: ['overview'], cropLength: 5));" +search_parameter_guide_highlight_1: "await client.index('movies').search(\n 'winter feast', SearchQuery(attributesToHighlight: ['overview']));" +search_parameter_guide_show_matches_position_1: "await client\n .index('movies')\n .search('winter feast', SearchQuery(showMatchesPosition: true));" +add_movies_json_1: "\/\/ import 'dart:io';\n\/\/ import 'dart:convert';\nfinal json = await File('movies.json').readAsString();\nawait client.index('movies').addDocumentsJson(json);" primary_field_guide_create_index_primary_key: |- await client.createIndex('books', primaryKey: 'reference_number'); primary_field_guide_update_document_primary_key: |- @@ -370,142 +205,58 @@ typo_tolerance_guide_4: |- ), ); await client.index('movies').updateTypoTolerance(toUpdate); -getting_started_add_documents_md: |- - ```bash - dart pub add meilisearch - ``` - - ```dart - import 'package:meilisearch/meilisearch.dart'; - import 'dart:io'; - import 'dart:convert'; - - var client = MeiliSearchClient('http://localhost:7700', 'aSampleMasterKey'); - - final json = await File('movies.json').readAsString(); - - await client.index('movies').addDocumentsJson(json); - ``` - - [About this SDK](https://github.com/meilisearch/meilisearch-dart/) -getting_started_search_md: |- - ```dart - await client.index('movies').search('botman'); - ``` - - [About this SDK](https://github.com/meilisearch/meilisearch-dart/) -getting_started_update_ranking_rules: |- - await client.index('movies').updateRankingRules([ - 'exactness', - 'words', - 'typo', - 'proximity', - 'attribute', - 'sort', - 'release_date:asc', - 'rank:desc' - ]); -getting_started_update_searchable_attributes: |- - await client.index('movies').updateSearchableAttributes([ - 'title' - ]); +getting_started_add_documents_md: "```bash\ndart pub add meilisearch\n```\n```dart\nimport 'package:meilisearch\/meilisearch.dart';\nimport 'dart:io';\nimport 'dart:convert';\nvar client = MeiliSearchClient('http:\/\/localhost:7700', 'aSampleMasterKey');\nfinal json = await File('movies.json').readAsString();\nawait client.index('movies').addDocumentsJson(json);\n```\n[About this SDK](https:\/\/github.com\/meilisearch\/meilisearch-dart\/)" +getting_started_search_md: "```dart\nawait client.index('movies').search('botman');\n```\n[About this SDK](https:\/\/github.com\/meilisearch\/meilisearch-dart\/)" +getting_started_update_ranking_rules: "await client.index('movies').updateRankingRules([\n 'exactness',\n 'words',\n 'typo',\n 'proximity',\n 'attribute',\n 'sort',\n 'release_date:asc',\n 'rank:desc',\n]);" +getting_started_update_searchable_attributes: "await client.index('movies').updateSearchableAttributes(['title']);" getting_started_update_stop_words: |- await client.index('movies').updateStopWords(['the']); getting_started_check_task_status: |- await client.getTask(0); -getting_started_synonyms: |- - await client.index('movies').updateSynonyms({ - 'winnie': ['piglet'], - 'piglet': ['winnie'] - }); -getting_started_update_displayed_attributes: |- - await client.index('movies').updateDisplayedAttributes([ - 'title', - 'overview', - 'poster' - ]); -getting_started_add_meteorites: |- - final json = await File('meteorites.json').readAsString(); - - await client.index('meteorites').addDocumentsJson(json); -getting_started_configure_settings: |- - await client.index('meteorites').updateSettings(IndexSettings( - filterableAttributes: ['mass', '_geo'], - sortableAttributes: ['mass', '_geo'] - )); -getting_started_geo_radius: |- - await client.index('meteorites').search('', SearchQuery(filter: '_geoRadius(46.9480, 7.4474, 210000)')); -getting_started_geo_point: |- - await client.index('meteorites').search('', SearchQuery(sort: ['_geoPoint(48.8583701, 2.2922926):asc'])); -getting_started_sorting: |- - await client.index('meteorites').search('', SearchQuery(sort: ['mass:asc'], filter: 'mass < 200')); -getting_started_filtering: |- - await client.index('meteorites').search('', SearchQuery(filter: 'mass < 200')); -getting_started_faceting: |- - await client.index('books').updateFaceting(Faceting(maxValuesPerFacet: 2, sortFacetValuesBy: {'*': 'count'})); +getting_started_synonyms: "await client.index('movies').updateSynonyms({\n 'winnie': ['piglet'],\n 'piglet': ['winnie'],\n});" +getting_started_update_displayed_attributes: "await client\n .index('movies')\n .updateDisplayedAttributes(['title', 'overview', 'poster']);" +getting_started_add_meteorites: "final json = await File('meteorites.json').readAsString();\nawait client.index('meteorites').addDocumentsJson(json);" +getting_started_configure_settings: "await client.index('meteorites').updateSettings(IndexSettings(\n filterableAttributes: ['mass', '_geo'],\n sortableAttributes: ['mass', '_geo']));" +getting_started_geo_radius: "await client.index('meteorites').search(\n '',\n SearchQuery(\n filterExpression: Meili.geoRadius(\n (lat: 46.9480, lng: 7.4474),\n 210000,\n ),\n ),\n );" +getting_started_geo_point: "await client.index('meteorites').search(\n '', SearchQuery(sort: ['_geoPoint(48.8583701, 2.2922926):asc']));" +getting_started_sorting: "await client.index('meteorites').search(\n '',\n SearchQuery(\n sort: ['mass:asc'],\n filterExpression: Meili.attr('mass').lt(200.toMeiliValue()),\n ),\n );" +getting_started_filtering: "await client\n .index('meteorites')\n .search('', SearchQuery(filter: 'mass < 200'));" +getting_started_faceting: "await client.index('books').updateFaceting(Faceting(\n maxValuesPerFacet: 2,\n sortFacetValuesBy: {'*': FacetingSortTypes.count}));" getting_started_typo_tolerance: |- final toUpdate = TypoTolerance( minWordSizeForTypos: MinWordSizeForTypos(oneTypo: 4), ); await client.index('movies').updateTypoTolerance(toUpdate); -getting_started_pagination: |- - await client.index('books').updatePagination(Pagination(maxTotalHits: 500)); +getting_started_pagination: "await client\n .index('books')\n .updatePagination(Pagination(maxTotalHits: 500));" filtering_update_settings_1: |- await client.index('movies').updateFilterableAttributes([ 'director', 'genres', ]); -faceted_search_walkthrough_filter_1: |- - await client.index('movies').search('thriller', SearchQuery(filter: [ - ['genres = Horror', 'genres = Mystery'], - 'director = "Jordan Peele"' - ])); +faceted_search_walkthrough_filter_1: "await client.index('movies').search(\n 'thriller',\n SearchQuery(filter: [\n ['genres = Horror', 'genres = Mystery'],\n 'director = \"Jordan Peele\"'\n ]));" post_dump_1: |- await client.createDump(); phrase_search_1: |- await client.index('movies').search('"african american" horror'); sorting_guide_update_sortable_attributes_1: |- await client.index('books').updateSortableAttributes(['author', 'price']); -sorting_guide_update_ranking_rules_1: |- - await client.index('books').updateRankingRules([ - 'words', - 'sort', - 'typo', - 'proximity', - 'attribute', - 'exactness' - ]); -sorting_guide_sort_parameter_1: |- - await client.index('books').search('science fiction', SearchQuery(sort: ['price:asc'])); -sorting_guide_sort_parameter_2: |- - await client.index('books').search('butler', SearchQuery(sort: ['author:desc'])); +sorting_guide_update_ranking_rules_1: "await client.index('books').updateRankingRules(\n ['words', 'sort', 'typo', 'proximity', 'attribute', 'exactness']);" +sorting_guide_sort_parameter_1: "await client\n .index('books')\n .search('science fiction', SearchQuery(sort: ['price:asc']));" +sorting_guide_sort_parameter_2: "await client\n .index('books')\n .search('butler', SearchQuery(sort: ['author:desc']));" get_sortable_attributes_1: |- await client.index('books').getSortableAttributes(); -update_sortable_attributes_1: |- - await client.index('books').updateSortableAttributes([ - 'price', - 'author' - ]); +update_sortable_attributes_1: "await client.index('books').updateSortableAttributes(['price', 'author']);" reset_sortable_attributes_1: |- await client.index('books').resetSortableAttributes(); -search_parameter_guide_sort_1: |- - await client.index('books').search('science fiction', SearchQuery(sort: ['price:asc'])); +search_parameter_guide_sort_1: "await client\n .index('books')\n .search('science fiction', SearchQuery(sort: ['price:asc']));" geosearch_guide_filter_settings_1: |- await client.index('restaurants').updateFilterableAttributes(['_geo']); -geosearch_guide_filter_usage_1: |- - await await client - .index('restaurants') - .search('', SearchQuery(filter: '_geoRadius(45.472735, 9.184019, 2000)')); -geosearch_guide_filter_usage_2: |- - await await client - .index('restaurants') - .search('', SearchQuery(filter: '_geoRadius(45.472735, 9.184019, 2000) AND type = pizza')); +geosearch_guide_filter_usage_1: "await client.index('restaurants').search(\n '',\n SearchQuery(\n filterExpression: Meili.geoRadius(\n (lat: 45.472735, lng: 9.184019),\n 2000,\n ),\n ),\n );" +geosearch_guide_filter_usage_2: "await client.index('restaurants').search(\n '',\n SearchQuery(\n filterExpression: Meili.and([\n Meili.geoRadius(\n (lat: 45.472735, lng: 9.184019),\n 2000,\n ),\n Meili.attr('type').eq('pizza'.toMeiliValue())\n ]),\n ),\n );" geosearch_guide_sort_settings_1: |- await client.index('restaurants').updateSortableAttributes(['_geo']); -geosearch_guide_sort_usage_1: |- - await client.index('restaurants').search('', SearchQuery(sort: ['_geoPoint(48.8561446, 2.2978204):asc'])); -geosearch_guide_sort_usage_2: |- - await client.index('restaurants').search('', SearchQuery(sort: ['_geoPoint(48.8561446, 2.2978204):asc', 'rating:desc'])); +geosearch_guide_sort_usage_1: "await client.index('restaurants').search(\n '', SearchQuery(sort: ['_geoPoint(48.8561446, 2.2978204):asc']));" +geosearch_guide_sort_usage_2: "await client.index('restaurants').search(\n '',\n SearchQuery(\n sort: ['_geoPoint(48.8561446, 2.2978204):asc', 'rating:desc']));" authorization_header_1: |- var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); await client.getKeys(); @@ -513,27 +264,14 @@ get_one_key_1: |- await client.getKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d'); get_all_keys_1: |- await client.getKeys(params: KeysQuery(limit: 3)); -create_a_key_1: |- - await client.createKey( - description: 'Add documents: Products API key', - actions: ['documents.add'], - indexes: ['products'], - expiresAt: DateTime(2042, 04, 02) - ); -update_a_key_1: |- - await client.updateKey( - '6062abda-a5aa-4414-ac91-ecd7944c0f8d', - description: 'Manage documents: Products/Reviews API key', - name: 'Products/Reviews API key' - ); +create_a_key_1: "await client.createKey(\n description: 'Add documents: Products API key',\n actions: ['documents.add'],\n indexes: ['products'],\n expiresAt: DateTime(2042, 04, 02));" +update_a_key_1: "await client.updateKey(\n '6062abda-a5aa-4414-ac91-ecd7944c0f8d',\n description: 'Manage documents: Products\/Reviews API key',\n name: 'Products\/Reviews API key',\n);" delete_a_key_1: |- await client.deleteKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d'); security_guide_search_key_1: |- var client = MeiliSearchClient('http://localhost:7700', 'apiKey'); await client.index('patient_medical_records').search(''); -security_guide_update_key_1: |- - var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); - await client.updateKey('74c9c733-3368-4738-bbe5-1d18a5fecb37', description: 'Default Search API Key'); +security_guide_update_key_1: "var client = MeiliSearchClient('http:\/\/localhost:7700', 'masterKey');\nawait client.updateKey(\n '74c9c733-3368-4738-bbe5-1d18a5fecb37',\n description: 'Default Search API Key',\n);" security_guide_create_key_1: |- var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); await client.createKey( @@ -548,48 +286,7 @@ security_guide_list_keys_1: |- security_guide_delete_key_1: |- var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); await client.deleteKey('ac5cd97d-5a4b-4226-a868-2d0eb6d197ab'); -tenant_token_guide_generate_sdk_1: |- - final uid = '85c3c2f9-bdd6-41f1-abd8-11fcf80e0f76'; - final apiKey = 'B5KdX2MY2jV6EXfUs6scSfmC...'; - final expiresAt = DateTime.utc(2025, 12, 20); - final searchRules = { - 'patient_medical_records': { - 'filter': 'user_id = 1' - } - }; - - final token = client.generateTenantToken( - uid, - searchRules, - apiKey: apiKey, // optional - expiresAt: expiresAt // optional - ); -tenant_token_guide_search_sdk_1: |- - final frontEndClient = MeiliSearchClient('http://localhost:7700', token); - await frontEndClient.index('patient_medical_records').search('blood test'); -landing_getting_started_1: |- - var client = MeiliSearchClient('http://localhost:7700', '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'} - ]); -search_parameter_guide_crop_marker_1: |- - await client - .index('movies') - .search('shifu', SearchQuery(attributesToCrop: ['overview'], cropMarker: '[…]')); -search_parameter_guide_highlight_tag_1: |- - await client.index('movies').search( - 'winter feast', - SearchQuery( - attributesToHighlight: ['overview'], - highlightPreTag: '', - highlightPostTag: '')); -geosearch_guide_filter_usage_3: |- - await client.index('restaurants') - .search('', SearchQuery(filter: '_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])')), - }); +search_parameter_guide_crop_marker_1: "await client.index('movies').search(\n 'shifu',\n SearchQuery(\n attributesToCrop: ['overview'],\n cropMarker: '[…]',\n ),\n );" +search_parameter_guide_highlight_tag_1: "await client.index('movies').search(\n 'winter feast',\n SearchQuery(\n attributesToHighlight: ['overview'],\n highlightPreTag: '',\n highlightPostTag: '<\/span>',\n ),\n );" +geosearch_guide_filter_usage_3: "await client.index('restaurants').search(\n '',\n SearchQuery(\n filter:\n '_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])'));\n});" +search_get_1: await client.index('movies').search('American ninja'); diff --git a/.github/workflows/pre-release-tests.yml b/.github/workflows/pre-release-tests.yml index a498df8..68d2419 100644 --- a/.github/workflows/pre-release-tests.yml +++ b/.github/workflows/pre-release-tests.yml @@ -27,4 +27,4 @@ jobs: - name: Meilisearch (${{ env.MEILISEARCH_VERSION }}) setup with Docker run: docker run -d -p 7700:7700 getmeili/meilisearch:${{ env.MEILISEARCH_VERSION }} meilisearch --master-key=masterKey --no-analytics - name: Run integration tests - run: docker run --net="host" -v $PWD:/package -w /package dart:${{ matrix.version }} /bin/sh -c 'dart pub get && dart run test' + run: docker run --net="host" -v $PWD:/package -w /package dart:${{ matrix.version }} /bin/sh -c 'dart pub get && dart pub get -C tool && dart run test' diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 877ff05..d944725 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -41,6 +41,7 @@ jobs: - name: Install dependencies run: | dart pub get + dart pub get -C tool dart pub global activate coverage - name: Run integration tests run: dart test --concurrency=4 --reporter=github --coverage=./coverage/reports @@ -61,7 +62,9 @@ jobs: steps: - uses: actions/checkout@v3 - name: Install dependencies - run: dart pub get + run: | + dart pub get + dart pub get -C tool - name: Run linter run: dart analyze --fatal-infos && dart format . --set-exit-if-changed @@ -75,6 +78,19 @@ jobs: with: config_file: .yamllint.yml + check-code-samples: + name: check .code-samples.meilisearch.yaml + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: dart-lang/setup-dart@v1 + with: + sdk: '3.0.0' + - name: check if samples changed + run: | + dart pub get + dart pub get -C tool + dart run ./tool/bin/meili.dart update-samples --fail-on-change pana: runs-on: ubuntu-latest timeout-minutes: 10 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index c790302..eecd17b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -9,6 +9,7 @@ First of all, thank you for contributing to Meilisearch! The goal of this docume - [Requirements ](#requirements-) - [Setup ](#setup-) - [Tests and Linter ](#tests-and-linter-) + - [Updating code samples](#updating-code-samples) - [Git Guidelines](#git-guidelines) - [Git Branches ](#git-branches-) - [Git Commits ](#git-commits-) @@ -76,6 +77,31 @@ dart test dart analyze ``` +### Updating code samples + +Some PRs require updating the code samples (found in `.code-samples.meilisearch.yaml`), this is done automatically using code excerpts, which are actual pieces of code subject to testing and linting. + +A lot of them are placed in `test/code_samples.dart`. + +Also most of the tests in that file are skipped, since they are mostly duplicated in other test files. + +The process to define a new code sample is as follows: +1. Add the piece of code in `test/code_samples.dart` +2. surround it with `#docregion key` and `#enddocregion`, e.g. + ``` + // #docregion meilisearch_contributing_1 + final client = MeilisearchClient(); + anything(); + // #enddocregion + ``` +3. run this command to update the code samples + ```bash + dart run ./tool/bin/meili.dart update-samples + ``` +4. to test if the code samples are updated correctly, run: + ```bash + dart run ./tool/bin/meili.dart update-samples --fail-on-change + ``` ## Git Guidelines ### Git Branches diff --git a/README.md b/README.md index c3a7afa..85e4a39 100644 --- a/README.md +++ b/README.md @@ -28,13 +28,7 @@ **Meilisearch** is an open-source search engine. [Learn more about Meilisearch.](https://github.com/meilisearch/meilisearch) ---- - -### 🔥 On November 2nd, we are hosting our first-ever live demo and product updates for [Meilisearch Cloud](https://www.meilisearch.com/cloud?utm_campaign=oss&utm_source=github&utm_medium=meilisearch). Make sure to [register here](https://us06web.zoom.us/meeting/register/tZMlc-mqrjIsH912-HTRe-AaT-pp41bDe81a#/registration) and bring your questions for live Q&A! - ---- - -## Table of Contents +## Table of Contents - [📖 Documentation](#-documentation) - [⚡ Supercharge your Meilisearch experience](#-supercharge-your-meilisearch-experience) diff --git a/lib/src/filter_builder/values.dart b/lib/src/filter_builder/values.dart index c1a8a19..db1f6d4 100644 --- a/lib/src/filter_builder/values.dart +++ b/lib/src/filter_builder/values.dart @@ -29,8 +29,10 @@ class MeiliDateTimeValueExpression extends MeiliValueExpressionBase { "DateTime passed to Meili must be in UTC to avoid inconsistency accross multiple devices", ); + /// Unix epoch time is seconds since epoch @override - String transform() => value.millisecondsSinceEpoch.toString(); + String transform() => + (value.millisecondsSinceEpoch / 1000).floor().toString(); @override bool operator ==(Object other) { diff --git a/test/code_samples.dart b/test/code_samples.dart new file mode 100644 index 0000000..3de892b --- /dev/null +++ b/test/code_samples.dart @@ -0,0 +1,888 @@ +// ignore_for_file: unused_element + +import 'dart:io'; + +import 'package:meilisearch/meilisearch.dart'; +import 'package:test/test.dart'; + +import 'utils/client.dart'; + +void main() { + // this file hosts some code samples referenced in + // .code-samples.meilisearch.yaml + // it's subject to tests, lint rules, deprecation notices, etc... + group('code samples', () { + setUpClient(); + + test('excerpts', () async { + void a1() async { + // #docregion typo_tolerance_guide_1 + final toUpdate = TypoTolerance(enabled: false); + await client.index('movies').updateTypoTolerance(toUpdate); + // #enddocregion + } + + void a2() async { + // #docregion typo_tolerance_guide_2 + final toUpdate = TypoTolerance( + disableOnAttributes: ['title'], + ); + await client.index('movies').updateTypoTolerance(toUpdate); + // #enddocregion + } + + void a3() async { + // #docregion typo_tolerance_guide_3 + final toUpdate = TypoTolerance( + disableOnWords: ['shrek'], + ); + await client.index('movies').updateTypoTolerance(toUpdate); + // #enddocregion + } + + void a4() async { + // #docregion typo_tolerance_guide_4 + final toUpdate = TypoTolerance( + minWordSizeForTypos: MinWordSizeForTypos( + oneTypo: 4, + twoTypos: 10, + ), + ); + await client.index('movies').updateTypoTolerance(toUpdate); + // #enddocregion + } + + void a8() async { + // #docregion getting_started_add_meteorites + final json = await File('meteorites.json').readAsString(); + + await client.index('meteorites').addDocumentsJson(json); + // #enddocregion + } + + void a10() async { + // #docregion add_movies_json_1 + // import 'dart:io'; + // import 'dart:convert'; + final json = await File('movies.json').readAsString(); + await client.index('movies').addDocumentsJson(json); + // #enddocregion + } + + void a11() async { + // #docregion security_guide_delete_key_1 + var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); + await client.deleteKey('ac5cd97d-5a4b-4226-a868-2d0eb6d197ab'); + // #enddocregion + } + + void a12() async { + // #docregion security_guide_list_keys_1 + var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); + await client.getKeys(); + // #enddocregion + } + + void a13() async { + // #docregion security_guide_create_key_1 + var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); + await client.createKey( + description: 'Search patient records key', + actions: ['search'], + indexes: ['patient_medical_records'], + expiresAt: DateTime(2023, 01, 01), + ); + // #enddocregion + } + + void a14() async { + // #docregion authorization_header_1 + var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); + await client.getKeys(); + // #enddocregion + } + + void a15() async { + // #docregion security_guide_search_key_1 + var client = MeiliSearchClient('http://localhost:7700', 'apiKey'); + await client.index('patient_medical_records').search(''); + // #enddocregion + } + + void a16() async { + // #docregion security_guide_update_key_1 + var client = MeiliSearchClient('http://localhost:7700', 'masterKey'); + await client.updateKey( + '74c9c733-3368-4738-bbe5-1d18a5fecb37', + description: 'Default Search API Key', + ); + // #enddocregion + } + + // #docregion date_guide_index_1 + //import 'dart:io'; + //import 'dart:convert'; + + final json = await File('games.json').readAsString(); + + await client.index('games').addDocumentsJson(json); + // #enddocregion + + // #docregion date_guide_filterable_attributes_1 + await client + .index('games') + .updateFilterableAttributes(['release_timestamp']); + // #enddocregion + + // #docregion date_guide_filter_1 + await client.index('games').search( + '', + SearchQuery( + filterExpression: Meili.and([ + Meili.gte( + 'release_timestamp'.toMeiliAttribute(), + Meili.value(DateTime(2017, 12, 31, 23, 0)), + ), + Meili.lt( + 'release_timestamp'.toMeiliAttribute(), + Meili.value(DateTime(2022, 12, 31, 23, 0)), + ), + ]), + ), + ); + // #enddocregion + + // #docregion date_guide_sortable_attributes_1 + await client + .index('games') + .updateSortableAttributes(['release_timestamp']); + // #enddocregion + + // #docregion date_guide_sort_1 + await client + .index('games') + .search('', SearchQuery(sort: ['release_timestamp:desc'])); + // #enddocregion + + // #docregion get_all_tasks_paginating_1 + await client.getTasks(params: TasksQuery(limit: 2, from: 10)); + // #enddocregion + + // #docregion get_all_tasks_paginating_2 + await client.getTasks(params: TasksQuery(limit: 2, from: 8)); + // #enddocregion + + // #docregion get_pagination_settings_1 + await client.index('movies').getPagination(); + // #enddocregion + + // #docregion update_pagination_settings_1 + await client + .index('books') + .updatePagination(Pagination(maxTotalHits: 100)); + // #enddocregion + + // #docregion reset_pagination_settings_1 + await client.index('movies').resetPagination(); + // #enddocregion + + // #docregion get_faceting_settings_1 + await client.index('movies').getFaceting(); + // #enddocregion + + // #docregion update_faceting_settings_1 + await client.index('books').updateFaceting(Faceting( + maxValuesPerFacet: 2, + sortFacetValuesBy: { + '*': FacetingSortTypes.alpha, + 'genres': FacetingSortTypes.count + })); + // #enddocregion + + // #docregion reset_faceting_settings_1 + await client.index('movies').resetFaceting(); + // #enddocregion + + // #docregion get_one_index_1 + await client.getIndex('movies'); + // #enddocregion + + // #docregion list_all_indexes_1 + await client.getIndexes(params: IndexesQuery(limit: 3)); + // #enddocregion + + // #docregion create_an_index_1 + await client.createIndex('movies', primaryKey: 'id'); + // #enddocregion + + // #docregion update_an_index_1 + await client.index('movies').update(primaryKey: 'id'); + // #enddocregion + + // #docregion delete_an_index_1 + await client.index('movies').delete(); + // #enddocregion + + // #docregion get_one_document_1 + await client.index('movies').getDocument(25684, + fields: ['id', 'title', 'poster', 'release_date']); + // #enddocregion + + // #docregion add_or_replace_documents_1 + 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' + } + ]); + // #enddocregion + + // #docregion add_or_update_documents_1 + await client.index('movies').updateDocuments([ + { + 'id': 287947, + 'title': 'Shazam ⚡️', + 'genres': 'comedy', + } + ]); + // #enddocregion + + // #docregion delete_all_documents_1 + await client.index('movies').deleteAllDocuments(); + // #enddocregion + + // #docregion delete_one_document_1 + await client.index('movies').deleteDocument(25684); + // #enddocregion + + // #docregion delete_documents_by_batch_1 + await client.index('movies').deleteDocuments( + DeleteDocumentsQuery( + ids: [23488, 153738, 437035, 363869], + ), + ); + // #enddocregion + + // #docregion search_post_1 + await client.index('movies').search('American ninja'); + // #enddocregion + + // #docregion get_task_1 + await client.getTask(1); + // #enddocregion + + // #docregion get_all_tasks_1 + await client.getTasks(); + // #enddocregion + + // #docregion get_settings_1 + await client.index('movies').getSettings(); + // #enddocregion + + // #docregion update_settings_1 + await client.index('movies').updateSettings( + IndexSettings( + 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'], + }, + ), + ); + // #enddocregion + + // #docregion reset_settings_1 + await client.index('movies').resetSettings(); + // #enddocregion + + // #docregion get_synonyms_1 + await client.index('movies').getSynonyms(); + // #enddocregion + + // #docregion update_synonyms_1 + await client.index('movies').updateSynonyms({ + 'wolverine': ['xmen', 'logan'], + 'logan': ['wolverine', 'xmen'], + 'wow': ['world of warcraft'], + }); + // #enddocregion + + // #docregion reset_synonyms_1 + await client.index('movies').resetSynonyms(); + // #enddocregion + + // #docregion get_stop_words_1 + await client.index('movies').getStopWords(); + // #enddocregion + + // #docregion update_stop_words_1 + await client.index('movies').updateStopWords(['of', 'the', 'to']); + // #enddocregion + + // #docregion reset_stop_words_1 + await client.index('movies').resetStopWords(); + // #enddocregion + + // #docregion get_ranking_rules_1 + await client.index('movies').getRankingRules(); + // #enddocregion + + // #docregion update_ranking_rules_1 + await client.index('movies').updateRankingRules([ + 'words', + 'typo', + 'proximity', + 'attribute', + 'sort', + 'exactness', + 'release_date:asc', + 'rank:desc', + ]); + // #enddocregion + + // #docregion reset_ranking_rules_1 + await client.index('movies').resetRankingRules(); + // #enddocregion + + // #docregion get_distinct_attribute_1 + await client.index('shoes').getDistinctAttribute(); + // #enddocregion + + // #docregion update_distinct_attribute_1 + await client.index('shoes').updateDistinctAttribute('skuid'); + // #enddocregion + + // #docregion reset_distinct_attribute_1 + await client.index('shoes').resetDistinctAttribute(); + // #enddocregion + + // #docregion get_filterable_attributes_1 + await client.index('movies').getFilterableAttributes(); + // #enddocregion + + // #docregion update_filterable_attributes_1 + await client + .index('movies') + .updateFilterableAttributes(['genres', 'director']); + // #enddocregion + + // #docregion reset_filterable_attributes_1 + await client.index('movies').resetFilterableAttributes(); + // #enddocregion + + // #docregion get_searchable_attributes_1 + await client.index('movies').getSearchableAttributes(); + // #enddocregion + + // #docregion update_searchable_attributes_1 + await client + .index('movies') + .updateSearchableAttributes(['title', 'overview', 'genres']); + // #enddocregion + + // #docregion reset_searchable_attributes_1 + await client.index('movies').resetSearchableAttributes(); + // #enddocregion + + // #docregion get_displayed_attributes_1 + await client.index('movies').getDisplayedAttributes(); + // #enddocregion + + // #docregion update_displayed_attributes_1 + await client.index('movies').updateDisplayedAttributes([ + 'title', + 'overview', + 'genres', + 'release_date', + ]); + // #enddocregion + + // #docregion reset_displayed_attributes_1 + await client.index('movies').resetDisplayedAttributes(); + // #enddocregion + + // #docregion get_typo_tolerance_1 + await client.index('books').getTypoTolerance(); + // #enddocregion + + // #docregion update_typo_tolerance_1 + final toUpdate = TypoTolerance( + minWordSizeForTypos: MinWordSizeForTypos( + oneTypo: 4, + twoTypos: 10, + ), + disableOnAttributes: ['title'], + ); + await client.index('books').updateTypoTolerance(toUpdate); + // #enddocregion + + // #docregion reset_typo_tolerance_1 + await client.index('books').resetTypoTolerance(); + // #enddocregion + + // #docregion get_index_stats_1 + await client.index('movies').getStats(); + // #enddocregion + + // #docregion get_indexes_stats_1 + await client.getStats(); + // #enddocregion + + // #docregion get_health_1 + await client.health(); + // #enddocregion + + // #docregion get_version_1 + await client.getVersion(); + // #enddocregion + + // #docregion distinct_attribute_guide_1 + await client.index('jackets').updateDistinctAttribute('product_id'); + // #enddocregion + + // #docregion field_properties_guide_searchable_1 + await client + .index('movies') + .updateSearchableAttributes(['title', 'overview', 'genres']); + // #enddocregion + + // #docregion field_properties_guide_displayed_1 + await client.index('movies').updateDisplayedAttributes([ + 'title', + 'overview', + 'genres', + 'release_date', + ]); + // #enddocregion + + // #docregion filtering_guide_1 + await client.index('movie_ratings').search( + 'Avengers', + SearchQuery( + filterExpression: Meili.gt( + Meili.attr('release_date'), + DateTime.utc(1995, 3, 18).toMeiliValue(), + ), + ), + ); + // #enddocregion + + // #docregion filtering_guide_2 + await client.index('movie_ratings').search( + 'Batman', + SearchQuery( + filterExpression: Meili.and([ + Meili.attr('release_date') + .gt(DateTime.utc(1995, 3, 18).toMeiliValue()), + Meili.or([ + 'director'.toMeiliAttribute().eq('Tim Burton'.toMeiliValue()), + 'director' + .toMeiliAttribute() + .eq('Christopher Nolan'.toMeiliValue()), + ]), + ]), + ), + ); + // #enddocregion + + // #docregion filtering_guide_3 + await client.index('movie_ratings').search( + 'Planet of the Apes', + SearchQuery( + filterExpression: Meili.and([ + Meili.attr('release_date') + .gt(DateTime.utc(2020, 1, 1, 13, 15, 50).toMeiliValue()), + Meili.not( + Meili.attr('director').eq("Tim Burton".toMeiliValue()), + ), + ]), + ), + ); + // #enddocregion + + // #docregion search_parameter_guide_query_1 + await client.index('movies').search('shifu'); + // #enddocregion + + // #docregion search_parameter_guide_offset_1 + await client.index('movies').search('shifu', SearchQuery(offset: 1)); + // #enddocregion + + // #docregion search_parameter_guide_limit_1 + await client.index('movies').search('shifu', SearchQuery(limit: 2)); + // #enddocregion + + // #docregion search_parameter_guide_matching_strategy_1 + await client.index('movies').search( + 'big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.last)); + // #enddocregion + + // #docregion search_parameter_guide_matching_strategy_2 + await client.index('movies').search( + 'big fat liar', SearchQuery(matchingStrategy: MatchingStrategy.all)); + // #enddocregion + + // #docregion search_parameter_guide_retrieve_1 + await client.index('movies').search( + 'shifu', SearchQuery(attributesToRetrieve: ['overview', 'title'])); + // #enddocregion + + // #docregion search_parameter_guide_crop_1 + await client.index('movies').search( + 'shifu', SearchQuery(attributesToCrop: ['overview'], cropLength: 5)); + // #enddocregion + + // #docregion search_parameter_guide_highlight_1 + await client.index('movies').search( + 'winter feast', SearchQuery(attributesToHighlight: ['overview'])); + // #enddocregion + + // #docregion search_parameter_guide_show_matches_position_1 + await client + .index('movies') + .search('winter feast', SearchQuery(showMatchesPosition: true)); + // #enddocregion + + // #docregion primary_field_guide_create_index_primary_key + await client.createIndex('books', primaryKey: 'reference_number'); + // #enddocregion + + // #docregion primary_field_guide_update_document_primary_key + await client.updateIndex('books', 'title'); + // #enddocregion + + // #docregion primary_field_guide_add_document_primary_key + await client.index('movies').addDocuments([ + { + 'reference_number': 287947, + 'title': 'Diary of a Wimpy Kid', + 'author': 'Jeff Kinney', + 'genres': ['comedy', 'humor'], + 'price': 5.00 + } + ], primaryKey: 'reference_number'); + // #enddocregion + + // #docregion getting_started_update_ranking_rules + await client.index('movies').updateRankingRules([ + 'exactness', + 'words', + 'typo', + 'proximity', + 'attribute', + 'sort', + 'release_date:asc', + 'rank:desc', + ]); + // #enddocregion + + // #docregion getting_started_update_searchable_attributes + await client.index('movies').updateSearchableAttributes(['title']); + // #enddocregion + + // #docregion getting_started_update_stop_words + await client.index('movies').updateStopWords(['the']); + // #enddocregion + + // #docregion getting_started_check_task_status + await client.getTask(0); + // #enddocregion + + // #docregion getting_started_synonyms + await client.index('movies').updateSynonyms({ + 'winnie': ['piglet'], + 'piglet': ['winnie'], + }); + // #enddocregion + + // #docregion getting_started_update_displayed_attributes + await client + .index('movies') + .updateDisplayedAttributes(['title', 'overview', 'poster']); + // #enddocregion + + // #docregion getting_started_configure_settings + await client.index('meteorites').updateSettings(IndexSettings( + filterableAttributes: ['mass', '_geo'], + sortableAttributes: ['mass', '_geo'])); + // #enddocregion + + // #docregion getting_started_geo_radius + await client.index('meteorites').search( + '', + SearchQuery( + filterExpression: Meili.geoRadius( + (lat: 46.9480, lng: 7.4474), + 210000, + ), + ), + ); + // #enddocregion + + // #docregion getting_started_geo_point + await client.index('meteorites').search( + '', SearchQuery(sort: ['_geoPoint(48.8583701, 2.2922926):asc'])); + // #enddocregion + + // #docregion getting_started_sorting + await client.index('meteorites').search( + '', + SearchQuery( + sort: ['mass:asc'], + filterExpression: Meili.attr('mass').lt(200.toMeiliValue()), + ), + ); + // #enddocregion + + // #docregion getting_started_filtering + await client + .index('meteorites') + .search('', SearchQuery(filter: 'mass < 200')); + // #enddocregion + + // #docregion getting_started_faceting + await client.index('books').updateFaceting(Faceting( + maxValuesPerFacet: 2, + sortFacetValuesBy: {'*': FacetingSortTypes.count})); + // #enddocregion + + void a9() async { + // #docregion getting_started_typo_tolerance + final toUpdate = TypoTolerance( + minWordSizeForTypos: MinWordSizeForTypos(oneTypo: 4), + ); + await client.index('movies').updateTypoTolerance(toUpdate); + // #enddocregion + } + + // #docregion getting_started_pagination + await client + .index('books') + .updatePagination(Pagination(maxTotalHits: 500)); + // #enddocregion + + // #docregion filtering_update_settings_1 + await client.index('movies').updateFilterableAttributes([ + 'director', + 'genres', + ]); + // #enddocregion + + // #docregion faceted_search_walkthrough_filter_1 + await client.index('movies').search( + 'thriller', + SearchQuery(filter: [ + ['genres = Horror', 'genres = Mystery'], + 'director = "Jordan Peele"' + ])); + // #enddocregion + + // #docregion post_dump_1 + await client.createDump(); + // #enddocregion + + // #docregion phrase_search_1 + await client.index('movies').search('"african american" horror'); + // #enddocregion + + // #docregion sorting_guide_update_sortable_attributes_1 + await client.index('books').updateSortableAttributes(['author', 'price']); + // #enddocregion + + // #docregion sorting_guide_update_ranking_rules_1 + await client.index('books').updateRankingRules( + ['words', 'sort', 'typo', 'proximity', 'attribute', 'exactness']); + // #enddocregion + + // #docregion sorting_guide_sort_parameter_1 + await client + .index('books') + .search('science fiction', SearchQuery(sort: ['price:asc'])); + // #enddocregion + + // #docregion sorting_guide_sort_parameter_2 + await client + .index('books') + .search('butler', SearchQuery(sort: ['author:desc'])); + // #enddocregion + + // #docregion get_sortable_attributes_1 + await client.index('books').getSortableAttributes(); + // #enddocregion + + // #docregion update_sortable_attributes_1 + await client.index('books').updateSortableAttributes(['price', 'author']); + // #enddocregion + + // #docregion reset_sortable_attributes_1 + await client.index('books').resetSortableAttributes(); + // #enddocregion + + // #docregion search_parameter_guide_sort_1 + await client + .index('books') + .search('science fiction', SearchQuery(sort: ['price:asc'])); + // #enddocregion + + // #docregion geosearch_guide_filter_settings_1 + await client.index('restaurants').updateFilterableAttributes(['_geo']); + // #enddocregion + + // #docregion geosearch_guide_filter_usage_1 + await client.index('restaurants').search( + '', + SearchQuery( + filterExpression: Meili.geoRadius( + (lat: 45.472735, lng: 9.184019), + 2000, + ), + ), + ); + // #enddocregion + + // #docregion geosearch_guide_filter_usage_2 + await client.index('restaurants').search( + '', + SearchQuery( + filterExpression: Meili.and([ + Meili.geoRadius( + (lat: 45.472735, lng: 9.184019), + 2000, + ), + Meili.attr('type').eq('pizza'.toMeiliValue()) + ]), + ), + ); + // #enddocregion + + // #docregion geosearch_guide_sort_settings_1 + await client.index('restaurants').updateSortableAttributes(['_geo']); + // #enddocregion + + // #docregion geosearch_guide_sort_usage_1 + await client.index('restaurants').search( + '', SearchQuery(sort: ['_geoPoint(48.8561446, 2.2978204):asc'])); + // #enddocregion + + // #docregion geosearch_guide_sort_usage_2 + await client.index('restaurants').search( + '', + SearchQuery( + sort: ['_geoPoint(48.8561446, 2.2978204):asc', 'rating:desc'])); + // #enddocregion + + // #docregion get_one_key_1 + await client.getKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d'); + // #enddocregion + + // #docregion get_all_keys_1 + await client.getKeys(params: KeysQuery(limit: 3)); + // #enddocregion + + // #docregion create_a_key_1 + await client.createKey( + description: 'Add documents: Products API key', + actions: ['documents.add'], + indexes: ['products'], + expiresAt: DateTime(2042, 04, 02)); + // #enddocregion + + // #docregion update_a_key_1 + await client.updateKey( + '6062abda-a5aa-4414-ac91-ecd7944c0f8d', + description: 'Manage documents: Products/Reviews API key', + name: 'Products/Reviews API key', + ); + // #enddocregion + + // #docregion delete_a_key_1 + await client.deleteKey('6062abda-a5aa-4414-ac91-ecd7944c0f8d'); + // #enddocregion + + // #docregion search_parameter_guide_crop_marker_1 + await client.index('movies').search( + 'shifu', + SearchQuery( + attributesToCrop: ['overview'], + cropMarker: '[…]', + ), + ); + // #enddocregion + + // #docregion search_parameter_guide_highlight_tag_1 + await client.index('movies').search( + 'winter feast', + SearchQuery( + attributesToHighlight: ['overview'], + highlightPreTag: '', + highlightPostTag: '', + ), + ); + // #enddocregion + + // #docregion geosearch_guide_filter_usage_3 + await client.index('restaurants').search( + '', + SearchQuery( + filter: + '_geoBoundingBox([45.494181, 9.214024], [45.449484, 9.179175])')); + }); + // #enddocregion + // skip this test, since it's only used for generating code samples + }, skip: true); + +// unformatted examples +/* + // #docregion getting_started_search_md + ```dart + await client.index('movies').search('botman'); + ``` + + [About this SDK](https://github.com/meilisearch/meilisearch-dart/) + // #enddocregion + + // #docregion getting_started_add_documents_md + ```bash + dart pub add meilisearch + ``` + + ```dart + import 'package:meilisearch/meilisearch.dart'; + import 'dart:io'; + import 'dart:convert'; + + var client = MeiliSearchClient('http://localhost:7700', 'aSampleMasterKey'); + + final json = await File('movies.json').readAsString(); + + await client.index('movies').addDocumentsJson(json); + ``` + + [About this SDK](https://github.com/meilisearch/meilisearch-dart/) + // #enddocregion +*/ +} diff --git a/test/documents_test.dart b/test/documents_test.dart index b0c7a8e..8023a8f 100644 --- a/test/documents_test.dart +++ b/test/documents_test.dart @@ -557,4 +557,49 @@ void main() { }); }); }); + + test( + 'documents code samples', + () async { + // #docregion delete_documents_by_filter_1 + await client.index('movies').deleteDocuments( + DeleteDocumentsQuery( + filterExpression: Meili.or([ + Meili.attr('genres').eq(Meili.value('action')), + Meili.attr('genres').eq(Meili.value('adventure')), + ]), + ), + ); + // #enddocregion + + // #docregion get_documents_1 + await client.index('movies').getDocuments( + params: DocumentsQuery( + limit: 2, + filter: Meili.attr('genres').eq('action'.toMeiliValue()), + ), + ); + // #enddocregion + + // #docregion get_documents_post_1 + await client.index('movies').getDocuments( + params: DocumentsQuery( + filterExpression: Meili.and([ + 'language'.toMeiliAttribute().eq('English'.toMeiliValue()), + Meili.and([ + 'rating'.toMeiliAttribute().gt(3.toMeiliValue()), + Meili.or([ + 'genres'.toMeiliAttribute().eq('Adventure'.toMeiliValue()), + 'genres'.toMeiliAttribute().eq('Fiction'.toMeiliValue()), + ]), + ]), + ]), + fields: ['title', 'genres', 'rating', 'language'], + limit: 3, + ), + ); + // #enddocregion + }, + skip: true, + ); } diff --git a/test/exceptions_test.dart b/test/exceptions_test.dart index 3981a7d..6975cbf 100644 --- a/test/exceptions_test.dart +++ b/test/exceptions_test.dart @@ -25,7 +25,7 @@ void main() { throwsA(isA().having( (error) => error.toString(), // Actual 'toString() method', // Description of the check - 'MeiliSearchApiError - message: The request returned an invalid status code of 404.', // Expected + contains('404'), // Expected )), ); }); diff --git a/test/filter_builder_test.dart b/test/filter_builder_test.dart index 86431fc..fe962c8 100644 --- a/test/filter_builder_test.dart +++ b/test/filter_builder_test.dart @@ -75,7 +75,7 @@ void main() { }); test("Dates", () { final testData = [ - [DateTime.utc(1999, 12, 14, 18, 53, 56), '945197636000'], + [DateTime.utc(1999, 12, 14, 18, 53, 56), '945197636'], ]; for (var element in testData) { diff --git a/test/multi_index_search_test.dart b/test/multi_index_search_test.dart index 7aabbc7..64abd3f 100644 --- a/test/multi_index_search_test.dart +++ b/test/multi_index_search_test.dart @@ -46,4 +46,14 @@ void main() { expect(result.results.last.hits.length, 2); }); }); + + test('code samples', () async { + // #docregion multi_search_1 + await client.multiSearch(MultiSearchQuery(queries: [ + IndexSearchQuery(query: 'pooh', indexUid: 'movies', limit: 5), + IndexSearchQuery(query: 'nemo', indexUid: 'movies', limit: 5), + IndexSearchQuery(query: 'us', indexUid: 'movies_ratings'), + ])); + // #enddocregion + }, skip: true); } diff --git a/test/search_test.dart b/test/search_test.dart index 0fbb14f..45a1768 100644 --- a/test/search_test.dart +++ b/test/search_test.dart @@ -604,4 +604,102 @@ void main() { ); }); }); + + test('search code samples', () async { + // #docregion search_get_1 + await client.index('movies').search('American ninja'); + // #enddocregion + + // #docregion search_parameter_guide_show_ranking_score_1 + await client + .index('movies') + .search('dragon', SearchQuery(showRankingScore: true)); + // #enddocregion + }, skip: true); + + test('facet search code samples', () async { + // #docregion facet_search_1 + await client.index('books').facetSearch( + FacetSearchQuery( + facetQuery: 'fiction', + facetName: 'genres', + filter: 'rating > 3', + ), + ); + // #enddocregion + + // #docregion facet_search_2 + await client.index('books').updateFaceting( + Faceting( + sortFacetValuesBy: { + 'genres': FacetingSortTypes.count, + }, + ), + ); + // #enddocregion + + // #docregion facet_search_3 + await client.index('books').facetSearch( + FacetSearchQuery( + facetQuery: 'c', + facetName: 'genres', + ), + ); + // #enddocregion + + // #docregion search_parameter_guide_attributes_to_search_on_1 + await client.index('books').facetSearch( + FacetSearchQuery( + facetQuery: 'c', + facetName: 'genres', + ), + ); + // #enddocregion + + // #docregion search_parameter_guide_facet_stats_1 + await client + .index('movie_ratings') + .search('Batman', SearchQuery(facets: ['genres', 'rating'])); + // #enddocregion + + // #docregion faceted_search_1 + await client + .index('books') + .search('', SearchQuery(facets: ['genres', 'rating', 'language'])); + // #enddocregion + + // #docregion filtering_guide_nested_1 + await client.index('movie_ratings').search( + 'thriller', + SearchQuery( + filterExpression: Meili.gte( + //or Meili.attr('rating.users') + //or 'rating.users'.toMeiliAttribute() + Meili.attrFromParts(['rating', 'users']), + Meili.value(90), + ), + ), + ); + // #enddocregion + + // #docregion sorting_guide_sort_nested_1 + await client + .index('movie_ratings') + .search('thriller', SearchQuery(sort: ['rating.users:asc'])); + // #enddocregion + + // #docregion search_parameter_guide_page_1 + await client + .index('movies') + .search('', SearchQuery(page: 2)) + .asPaginatedResult(); + // #enddocregion + + // #docregion search_parameter_guide_hitsperpage_1 + await client + .index('movies') + .search('', SearchQuery(hitsPerPage: 15)) + .asPaginatedResult(); + // #enddocregion + }, skip: true); } diff --git a/test/settings_test.dart b/test/settings_test.dart index 0c457e4..c6d9862 100644 --- a/test/settings_test.dart +++ b/test/settings_test.dart @@ -400,4 +400,19 @@ void main() { }); }); }); + + test('code samples', () async { + // #docregion faceted_search_update_settings_1 + await client + .index('movie_ratings') + .updateFilterableAttributes(['genres', 'rating', 'language']); + // #enddocregion + + // #docregion synonyms_guide_1 + await client.index('movies').updateSynonyms({ + 'great': ['fantastic'], + 'fantastic': ['great'], + }); + // #enddocregion + }); } diff --git a/test/swaps_test.dart b/test/swaps_test.dart index f7f4cbd..58ba326 100644 --- a/test/swaps_test.dart +++ b/test/swaps_test.dart @@ -30,4 +30,13 @@ void main() { ]); }); }); + + test('code samples', () async { + // #docregion swap_indexes_1 + await client.swapIndexes([ + SwapIndex(['indexA', 'indexB']), + SwapIndex(['indexX', 'indexY']), + ]); + // #enddocregion + }, skip: true); } diff --git a/test/tasks_test.dart b/test/tasks_test.dart index 7a94e86..f521aeb 100644 --- a/test/tasks_test.dart +++ b/test/tasks_test.dart @@ -66,4 +66,60 @@ void main() { ); }); }); + + test( + 'code samples', + () async { + // #docregion async_guide_filter_by_date_1 + await client.getTasks( + params: TasksQuery( + afterEnqueuedAt: DateTime(2020, 10, 11, 11, 49, 53), + ), + ); + // #enddocregion + // #docregion async_guide_multiple_filters_1 + await client.getTasks( + params: TasksQuery( + indexUids: ['movies'], + types: ['documentAdditionOrUpdate', 'documentDeletion'], + statuses: ['processing'], + ), + ); + // #enddocregion + // #docregion async_guide_filter_by_ids_1 + await client.getTasks( + params: TasksQuery( + uids: [5, 10, 13], + ), + ); + // #enddocregion + // #docregion async_guide_filter_by_statuses_1 + await client.getTasks( + params: TasksQuery( + statuses: ['failed', 'canceled'], + ), + ); + // #enddocregion + // #docregion async_guide_filter_by_types_1 + await client.getTasks( + params: TasksQuery( + types: ['dumpCreation', 'indexSwap'], + ), + ); + // #enddocregion + // #docregion async_guide_filter_by_index_uids_1 + await client.getTasks(params: TasksQuery(indexUids: ['movies'])); + // #enddocregion + // #docregion delete_tasks_1 + await client.deleteTasks(params: DeleteTasksQuery(uids: [1, 2])); + // #enddocregion + // #docregion cancel_tasks_1 + await client.cancelTasks(params: CancelTasksQuery(uids: [1, 2])); + // #enddocregion + // #docregion async_guide_canceled_by_1 + await client.getTasks(params: TasksQuery(canceledBy: [9, 15])); + // #enddocregion + }, + skip: true, + ); } diff --git a/tool/.gitignore b/tool/.gitignore new file mode 100644 index 0000000..3a85790 --- /dev/null +++ b/tool/.gitignore @@ -0,0 +1,3 @@ +# https://dart.dev/guides/libraries/private-files +# Created by `dart pub` +.dart_tool/ diff --git a/tool/analysis_options.yaml b/tool/analysis_options.yaml new file mode 100644 index 0000000..c0a9532 --- /dev/null +++ b/tool/analysis_options.yaml @@ -0,0 +1,18 @@ +# This file configures the static analysis results for your project (errors, +# warnings, and lints). +# +# This enables the 'recommended' set of lints from `package:lints`. +# This set helps identify many issues that may lead to problems when running +# or consuming Dart code, and enforces writing Dart using a single, idiomatic +# style and format. +# +# If you want a smaller set of lints you can change this to specify +# 'package:lints/core.yaml'. These are just the most critical lints +# (the recommended set includes the core lints). +# The core lints are also what is used by pub.dev for scoring packages. + +include: package:lints/recommended.yaml + +linter: + rules: + avoid_print: false diff --git a/tool/bin/meili.dart b/tool/bin/meili.dart new file mode 100644 index 0000000..201457b --- /dev/null +++ b/tool/bin/meili.dart @@ -0,0 +1 @@ +export 'package:meili_tool/src/main.dart'; diff --git a/tool/lib/src/command_base.dart b/tool/lib/src/command_base.dart new file mode 100644 index 0000000..bd7853d --- /dev/null +++ b/tool/lib/src/command_base.dart @@ -0,0 +1,37 @@ +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:meili_tool/src/result.dart'; +import 'package:platform/platform.dart'; +import 'package:path/path.dart' as p; + +abstract class MeiliCommandBase extends Command { + final Directory packageDirectory; + + MeiliCommandBase( + this.packageDirectory, { + this.platform = const LocalPlatform(), + }); + + /// The current platform. + /// + /// This can be overridden for testing. + final Platform platform; + + /// A context that matches the default for [platform]. + p.Context get path => platform.isWindows ? p.windows : p.posix; + // Returns the relative path from [from] to [entity] in Posix style. + /// + /// This should be used when, for example, printing package-relative paths in + /// status or error messages. + String getRelativePosixPath( + FileSystemEntity entity, { + required Directory from, + }) => + p.posix.joinAll(path.split(path.relative(entity.path, from: from.path))); + + String get indentation => ' '; + + bool getBoolArg(String key) { + return (argResults![key] as bool?) ?? false; + } +} diff --git a/tool/lib/src/core.dart b/tool/lib/src/core.dart new file mode 100644 index 0000000..9835d0a --- /dev/null +++ b/tool/lib/src/core.dart @@ -0,0 +1,16 @@ +/// Error thrown when a command needs to exit with a non-zero exit code. +/// +/// While there is no specific definition of the meaning of different non-zero +/// exit codes for this tool, commands should follow the general convention: +/// 1: The command ran correctly, but found errors. +/// 2: The command failed to run because the arguments were invalid. +/// >2: The command failed to run correctly for some other reason. Ideally, +/// each such failure should have a unique exit code within the context of +/// that command. +class ToolExit extends Error { + /// Creates a tool exit with the given [exitCode]. + ToolExit(this.exitCode); + + /// The code that the process should exit with. + final int exitCode; +} diff --git a/tool/lib/src/main.dart b/tool/lib/src/main.dart new file mode 100644 index 0000000..ce3dc16 --- /dev/null +++ b/tool/lib/src/main.dart @@ -0,0 +1,53 @@ +import 'dart:io' as io; + +import 'package:args/command_runner.dart'; +import 'package:file/file.dart'; +import 'package:file/local.dart'; +import 'package:meili_tool/src/output_utils.dart'; +import 'package:meili_tool/src/result.dart'; + +import 'core.dart'; +import 'update_samples_command.dart'; + +void main(List arguments) { + const FileSystem fileSystem = LocalFileSystem(); + final Directory scriptDir = + fileSystem.file(io.Platform.script.toFilePath()).parent; + final Directory toolsDir = + scriptDir.basename == 'bin' ? scriptDir.parent : scriptDir.parent.parent; + + final Directory meilisearchDirectory = toolsDir.parent; + + final commandRunner = CommandRunner( + 'dart run ./tool/bin/meili.dart', 'Productivity utils for meilisearch.') + ..addCommand(UpdateSamplesCommand(meilisearchDirectory)); + + commandRunner.run(arguments).then((value) { + if (value == null) { + print('MUST output either a success or fail.'); + assert(false); + io.exit(255); + } + switch (value.state) { + case RunState.succeeded: + printSuccess('Success!'); + break; + case RunState.failed: + printError('Failed!'); + if (value.details.isNotEmpty) { + printError(value.details.join('\n')); + } + io.exit(255); + } + }).catchError((Object e) { + final ToolExit toolExit = e as ToolExit; + int exitCode = toolExit.exitCode; + // This should never happen; this check is here to guarantee that a ToolExit + // never accidentally has code 0 thus causing CI to pass. + if (exitCode == 0) { + assert(false); + exitCode = 255; + } + io.exit(exitCode); + }, test: (Object e) => e is ToolExit); +} diff --git a/tool/lib/src/output_utils.dart b/tool/lib/src/output_utils.dart new file mode 100644 index 0000000..7fd39f6 --- /dev/null +++ b/tool/lib/src/output_utils.dart @@ -0,0 +1,44 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +import 'dart:io'; + +import 'package:colorize/colorize.dart'; +import 'package:meta/meta.dart'; + +export 'package:colorize/colorize.dart' show Styles; + +/// True if color should be applied. +/// +/// Defaults to autodetecting stdout. +@visibleForTesting +bool useColorForOutput = stdout.supportsAnsiEscapes; + +String _colorizeIfAppropriate(String string, Styles color) { + if (!useColorForOutput) { + return string; + } + return Colorize(string).apply(color).toString(); +} + +/// Prints [message] in green, if the environment supports color. +void printSuccess(String message) { + print(_colorizeIfAppropriate(message, Styles.GREEN)); +} + +/// Prints [message] in yellow, if the environment supports color. +void printWarning(String message) { + print(_colorizeIfAppropriate(message, Styles.YELLOW)); +} + +/// Prints [message] in red, if the environment supports color. +void printError(String message) { + print(_colorizeIfAppropriate(message, Styles.RED)); +} + +/// Returns [message] with escapes to print it in [color], if the environment +/// supports color. +String colorizeString(String message, Styles color) { + return _colorizeIfAppropriate(message, color); +} diff --git a/tool/lib/src/result.dart b/tool/lib/src/result.dart new file mode 100644 index 0000000..331bf43 --- /dev/null +++ b/tool/lib/src/result.dart @@ -0,0 +1,34 @@ +/// Possible outcomes of a command run for a package. +enum RunState { + /// The command succeeded for the package. + succeeded, + + /// The command failed for the package. + failed, +} + +/// The result of a [runForPackage] call. +class PackageResult { + /// A successful result. + PackageResult.success() : this._(RunState.succeeded); + + /// A run that failed. + /// + /// If [errors] are provided, they will be listed in the summary, otherwise + /// the summary will simply show that the package failed. + PackageResult.fail([List errors = const []]) + : this._(RunState.failed, errors); + + const PackageResult._(this.state, [this.details = const []]); + + /// The state the package run completed with. + final RunState state; + + /// Information about the result: + /// - For `succeeded`, this is empty. + /// - For `skipped`, it contains a single entry describing why the run was + /// skipped. + /// - For `failed`, it contains zero or more specific error details to be + /// shown in the summary. + final List details; +} diff --git a/tool/lib/src/update_samples_command.dart b/tool/lib/src/update_samples_command.dart new file mode 100644 index 0000000..5462008 --- /dev/null +++ b/tool/lib/src/update_samples_command.dart @@ -0,0 +1,269 @@ +// Source: https://github.com/flutter/packages/blob/d0411e450a8d94fcb221e8d8eacd3b1f8ca0e2fc/script/tool/lib/src/update_excerpts_command.dart +// but modified to accept yaml files. + +import 'dart:async'; +import 'package:file/file.dart'; +import 'package:http/http.dart' as http; +import 'package:meili_tool/src/command_base.dart'; +import 'package:meili_tool/src/result.dart'; +import 'package:yaml/yaml.dart'; +import 'package:yaml_edit/yaml_edit.dart'; + +class _SourceFile { + final File file; + final List contents; + Map? result; + + _SourceFile({ + required this.file, + required this.contents, + }); +} + +class UpdateSamplesCommand extends MeiliCommandBase { + static const String _failOnChangeFlag = 'fail-on-change'; + static const String _checkRemoteRepoFlag = 'check-remote-repository'; + static const String _generateMissingExcerpts = 'generate-missing-excerpts'; + + UpdateSamplesCommand( + super.packageDirectory, { + super.platform, + }) { + argParser.addFlag( + _failOnChangeFlag, + help: 'Fail if the command does anything. ' + '(Used in CI to ensure excerpts are up to date.)', + ); + argParser.addFlag( + _checkRemoteRepoFlag, + hide: true, + help: + 'Check the remote code samples to see if there are missing/useless keys', + ); + argParser.addFlag( + _generateMissingExcerpts, + hide: true, + help: 'Generate entries that are found in code samples, but not in code', + ); + } + + @override + String get description => + 'Updates .code-samples.meilisearch.yaml, based on code from code files'; + + @override + String get name => 'update-samples'; + + static const docregion = '#docregion'; + static const enddocregion = '#enddocregion'; + final startRegionRegex = RegExp(RegExp.escape(docregion) + r'\s+(?\w+)'); + + @override + Future run() async { + try { + final failOnChange = getBoolArg(_failOnChangeFlag); + final checkRemoteRepo = getBoolArg(_checkRemoteRepoFlag); + final generateMissingExcerpts = getBoolArg(_generateMissingExcerpts); + //read the samples yaml file + final changedKeys = {}; + final File samplesFile = + packageDirectory.childFile('.code-samples.meilisearch.yaml'); + final samplesContentRaw = await samplesFile.readAsString(); + final samplesYaml = loadYaml(samplesContentRaw); + if (samplesYaml is! YamlMap) { + print(samplesYaml.runtimeType); + return PackageResult.fail(['samples yaml must be an YamlMap']); + } + + final newSamplesYaml = YamlEditor(samplesContentRaw); + final foundCodeSamples = {}; + final missingSamples = {}; + final sourceFiles = await _discoverSourceFiles(); + for (var sourceFile in sourceFiles) { + final newValues = _runInFile(sourceFile); + foundCodeSamples.addAll(newValues); + sourceFile.result = newValues; + for (var element in newValues.entries) { + final existingValue = samplesYaml[element.key]; + if (existingValue != null) { + if (existingValue == element.value) { + continue; + } else { + changedKeys[element.key] = element.value; + } + } else { + changedKeys[element.key] = element.value; + } + } + if (failOnChange && changedKeys.isNotEmpty) { + return PackageResult.fail([ + 'found changed keys: ${changedKeys.keys.toList()}', + ]); + } + + if (!failOnChange) { + for (var changedEntry in changedKeys.entries) { + newSamplesYaml.update([changedEntry.key], changedEntry.value); + } + } + } + + for (var entry in samplesYaml.entries) { + if (foundCodeSamples.containsKey(entry.key)) { + continue; + } + missingSamples[entry.key] = entry.value; + } + if (generateMissingExcerpts) { + final targetFile = packageDirectory + .childDirectory('test') + .childFile('missing_samples.dart'); + final sb = StringBuffer(); + + sb.writeln(r"import 'package:meilisearch/meilisearch.dart';"); + sb.writeln('late MeiliSearchClient client;'); + sb.writeln('void main() async {'); + for (var element in missingSamples.entries) { + sb.writeln('// #docregion ${element.key}'); + sb.writeln(element.value); + sb.writeln('// #enddocregion'); + sb.writeln(); + } + sb.writeln('}'); + await targetFile.writeAsString(sb.toString()); + } + + // for now don't check remote repository + if (checkRemoteRepo) { + final fullSamplesYaml = await getFullCorrectSamples(); + final missingEntries = fullSamplesYaml.entries + .where((element) => !samplesYaml.containsKey(element.key)); + final oldEntries = samplesYaml.entries + .where((element) => !fullSamplesYaml.containsKey(element.key)); + if (failOnChange) { + if (missingEntries.isNotEmpty || oldEntries.isNotEmpty) { + return PackageResult.fail([ + if (missingEntries.isNotEmpty) + 'found the following missing entries: ${missingEntries.map((e) => e.key).join('\n')}', + if (oldEntries.isNotEmpty) + 'found the following useless entries: ${oldEntries.map((e) => e.key).join('\n')}', + ]); + } + } else { + for (var element in missingEntries) { + newSamplesYaml.update([element.key], element.value); + } + for (var element in oldEntries) { + newSamplesYaml.remove([element.key]); + } + } + } + + if (!failOnChange && !generateMissingExcerpts) { + await samplesFile.writeAsString(newSamplesYaml.toString()); + } + return PackageResult.success(); + } on PackageResult catch (e) { + return e; + } + } + + Future getFullCorrectSamples() async { + final uri = Uri.parse( + 'https://raw.githubusercontent.com/meilisearch/documentation/main/.code-samples.meilisearch.yaml'); + final data = await http.get(uri); + final parsed = loadYaml(data.body, sourceUrl: uri); + return parsed as YamlMap; + } + + Map _runInFile(_SourceFile file) { + int lineNumber = 0; + String? currentKey; + final keys = []; + final res = {}; + final currentKeyLines = >[]; + for (var line in file.contents) { + lineNumber++; + if (currentKey == null) { + final capture = startRegionRegex.firstMatch(line); + if (capture == null) { + continue; + } + final key = capture.namedGroup('key'); + if (key == null) { + throw PackageResult.fail(['found a #docregion with no key']); + } + if (keys.contains(key)) { + throw PackageResult.fail(['found duplicate keys $key']); + } + keys.add(key); + currentKey = key; + } else { + if (line.contains(enddocregion)) { + final sb = StringBuffer(); + final unindentedLines = + unindentLines(currentKeyLines.map((e) => e.value).toList()) + .join('\n'); + sb.write(unindentedLines); + //add to results. + res[currentKey] = sb.toString(); + + currentKey = null; + currentKeyLines.clear(); + } else { + currentKeyLines.add(MapEntry(lineNumber, line)); + } + } + } + return res; + } + + List unindentLines(List src) { + if (src.isEmpty) { + return src; + } + final ogFirst = src.first; + final trimmedFirst = ogFirst.trimLeft(); + final firstIndentation = ogFirst.length - trimmedFirst.length; + final res = []; + for (var element in src) { + final trimmedLine = element.trimLeft(); + if (trimmedLine.isEmpty) { + continue; + } + var indentation = element.length - trimmedLine.length; + indentation -= firstIndentation; + res.add('${" " * indentation}$trimmedLine'); + } + + return res; + } + + Future> _discoverSourceFiles() async { + final libDir = packageDirectory.childDirectory('lib'); + final testsDir = packageDirectory.childDirectory('test'); + //look in dart files and generate a new yaml file based on the referenced code. + final allDartFiles = [ + ...libDir.listSync(recursive: true), + ...testsDir.listSync(recursive: true), + ].where((element) => element.basename.toLowerCase().endsWith('.dart')); + + final sourceFiles = <_SourceFile>[]; + for (var dartFile in allDartFiles) { + if (dartFile is! File) { + continue; + } + final fileContents = await dartFile.readAsLines(); + if (!fileContents.any((line) => line.contains(docregion))) { + continue; + } + sourceFiles.add( + _SourceFile( + file: dartFile, + contents: fileContents, + ), + ); + } + return sourceFiles; + } +} diff --git a/tool/pubspec.yaml b/tool/pubspec.yaml new file mode 100644 index 0000000..be5bf8b --- /dev/null +++ b/tool/pubspec.yaml @@ -0,0 +1,30 @@ +name: meili_tool +description: | + Productivity tools for meilisearch dart repository, + most of this is inspired from the flutter packages repository https://github.com/flutter/packages/. +version: 1.0.0 +repository: https://github.com/meilisearch/meilisearch-dart + +environment: + sdk: '>=3.0.0 <4.0.0' + +# Add regular dependencies here. +dependencies: + lints: ^2.0.0 + test: ^1.21.0 + args: ^2.4.2 + cli_util: ^0.4.0 + file: ^7.0.0 + path: ^1.8.3 + platform: ^3.1.2 + collection: ^1.15.0 + colorize: ^3.0.0 + meta: ^1.10.0 + yaml: ^3.1.2 + yaml_edit: ^2.1.1 + http: ^1.1.0 + +dev_dependencies: + build_runner: ^2.0.3 + matcher: ^0.12.10 + mockito: '>=5.3.2 <=5.4.0'