mirror of
				https://github.com/meilisearch/meilisearch.git
				synced 2025-10-31 16:06:31 +00:00 
			
		
		
		
	Merge #5187
	
		
			
	
		
	
	
		
	
		
			Some checks failed
		
		
	
	
		
			
				
	
				Indexing bench (push) / Run and upload benchmarks (push) Waiting to run
				
					
					
				
			
		
			
				
	
				Benchmarks of indexing (push) / Run and upload benchmarks (push) Waiting to run
				
					
					
				
			
		
			
				
	
				Benchmarks of search for geo (push) / Run and upload benchmarks (push) Waiting to run
				
					
					
				
			
		
			
				
	
				Benchmarks of search for songs (push) / Run and upload benchmarks (push) Waiting to run
				
					
					
				
			
		
			
				
	
				Benchmarks of search for Wikipedia articles (push) / Run and upload benchmarks (push) Waiting to run
				
					
					
				
			
		
			
				
	
				Test suite / Tests on ${{ matrix.os }} (macos-13) (push) Waiting to run
				
					
					
				
			
		
			
				
	
				Test suite / Tests on ${{ matrix.os }} (windows-2022) (push) Failing after 12s
				
					
					
				
			
		
			
				
	
				Test suite / Tests on ubuntu-20.04 (push) Failing after 20s
				
					
					
				
			
		
			
				
	
				Test suite / Tests almost all features (push) Has been skipped
				
					
					
				
			
		
			
				
	
				Test suite / Test disabled tokenization (push) Has been skipped
				
					
					
				
			
		
			
				
	
				Test suite / Run tests in debug (push) Failing after 16s
				
					
					
				
			
		
			
				
	
				Test suite / Run Clippy (push) Successful in 33m58s
				
					
					
				
			
		
			
				
	
				Test suite / Run Rustfmt (push) Successful in 11m45s
				
					
					
				
			
		
			
				
	
				Run the indexing fuzzer / Setup the action (push) Successful in 1h10m33s
				
					
					
				
			
		
		
	
	
				
					
				
			
		
			Some checks failed
		
		
	
	Indexing bench (push) / Run and upload benchmarks (push) Waiting to run
				Benchmarks of indexing (push) / Run and upload benchmarks (push) Waiting to run
				Benchmarks of search for geo (push) / Run and upload benchmarks (push) Waiting to run
				Benchmarks of search for songs (push) / Run and upload benchmarks (push) Waiting to run
				Benchmarks of search for Wikipedia articles (push) / Run and upload benchmarks (push) Waiting to run
				Test suite / Tests on ${{ matrix.os }} (macos-13) (push) Waiting to run
				Test suite / Tests on ${{ matrix.os }} (windows-2022) (push) Failing after 12s
				Test suite / Tests on ubuntu-20.04 (push) Failing after 20s
				Test suite / Tests almost all features (push) Has been skipped
				Test suite / Test disabled tokenization (push) Has been skipped
				Test suite / Run tests in debug (push) Failing after 16s
				Test suite / Run Clippy (push) Successful in 33m58s
				Test suite / Run Rustfmt (push) Successful in 11m45s
				Run the indexing fuzzer / Setup the action (push) Successful in 1h10m33s
				5187: Bring back v1.12.0 of pre-release changes into `main` r=irevoire a=curquiza Co-authored-by: ManyTheFish <many@meilisearch.com> Co-authored-by: Louis Dureuil <louis@meilisearch.com> Co-authored-by: Clément Renault <clement@meilisearch.com> Co-authored-by: meili-bors[bot] <89034592+meili-bors[bot]@users.noreply.github.com> Co-authored-by: Many the fish <many@meilisearch.com>
This commit is contained in:
		| @@ -24,7 +24,7 @@ tempfile = "3.14.0" | ||||
| criterion = { version = "0.5.1", features = ["html_reports"] } | ||||
| rand = "0.8.5" | ||||
| rand_chacha = "0.3.1" | ||||
| roaring = "0.10.6" | ||||
| roaring = "0.10.7" | ||||
|  | ||||
| [build-dependencies] | ||||
| anyhow = "1.0.86" | ||||
|   | ||||
| @@ -8,6 +8,7 @@ use bumpalo::Bump; | ||||
| use criterion::{criterion_group, criterion_main, Criterion}; | ||||
| use milli::documents::PrimaryKey; | ||||
| use milli::heed::{EnvOpenOptions, RwTxn}; | ||||
| use milli::progress::Progress; | ||||
| use milli::update::new::indexer; | ||||
| use milli::update::{IndexDocumentsMethod, IndexerConfig, Settings}; | ||||
| use milli::vector::EmbeddingConfigs; | ||||
| @@ -16,6 +17,7 @@ use rand::seq::SliceRandom; | ||||
| use rand_chacha::rand_core::SeedableRng; | ||||
| use roaring::RoaringBitmap; | ||||
|  | ||||
| #[cfg(not(windows))] | ||||
| #[global_allocator] | ||||
| static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; | ||||
|  | ||||
| @@ -150,13 +152,14 @@ fn indexing_songs_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -164,7 +167,7 @@ fn indexing_songs_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -216,13 +219,14 @@ fn reindexing_songs_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -230,7 +234,7 @@ fn reindexing_songs_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -260,13 +264,14 @@ fn reindexing_songs_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -274,7 +279,7 @@ fn reindexing_songs_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -328,13 +333,14 @@ fn deleting_songs_in_batches_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -342,7 +348,7 @@ fn deleting_songs_in_batches_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -404,13 +410,14 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -418,7 +425,7 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -448,13 +455,14 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -462,7 +470,7 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -488,13 +496,14 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -502,7 +511,7 @@ fn indexing_songs_in_three_batches_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -555,13 +564,14 @@ fn indexing_songs_without_faceted_numbers(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -569,7 +579,7 @@ fn indexing_songs_without_faceted_numbers(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -621,13 +631,14 @@ fn indexing_songs_without_faceted_fields(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -635,7 +646,7 @@ fn indexing_songs_without_faceted_fields(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -687,13 +698,14 @@ fn indexing_wiki(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -701,7 +713,7 @@ fn indexing_wiki(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -752,13 +764,14 @@ fn reindexing_wiki(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -766,7 +779,7 @@ fn reindexing_wiki(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -796,13 +809,14 @@ fn reindexing_wiki(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -810,7 +824,7 @@ fn reindexing_wiki(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -863,13 +877,14 @@ fn deleting_wiki_in_batches_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -877,7 +892,7 @@ fn deleting_wiki_in_batches_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -939,13 +954,14 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -953,7 +969,7 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -984,13 +1000,14 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -998,7 +1015,7 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1025,13 +1042,14 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1039,7 +1057,7 @@ fn indexing_wiki_in_three_batches(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1091,13 +1109,14 @@ fn indexing_movies_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1105,7 +1124,7 @@ fn indexing_movies_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1156,13 +1175,14 @@ fn reindexing_movies_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1170,7 +1190,7 @@ fn reindexing_movies_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1200,13 +1220,14 @@ fn reindexing_movies_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1214,7 +1235,7 @@ fn reindexing_movies_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1267,13 +1288,14 @@ fn deleting_movies_in_batches_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1281,7 +1303,7 @@ fn deleting_movies_in_batches_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1321,6 +1343,7 @@ fn delete_documents_from_ids(index: Index, document_ids_to_delete: Vec<RoaringBi | ||||
|         indexer::index( | ||||
|             &mut wtxn, | ||||
|             &index, | ||||
|             &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|             config.grenad_parameters(), | ||||
|             &db_fields_ids_map, | ||||
|             new_fields_ids_map, | ||||
| @@ -1328,7 +1351,7 @@ fn delete_documents_from_ids(index: Index, document_ids_to_delete: Vec<RoaringBi | ||||
|             &document_changes, | ||||
|             EmbeddingConfigs::default(), | ||||
|             &|| false, | ||||
|             &|_| (), | ||||
|             &Progress::default(), | ||||
|         ) | ||||
|         .unwrap(); | ||||
|  | ||||
| @@ -1378,13 +1401,14 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1392,7 +1416,7 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1422,13 +1446,14 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1436,7 +1461,7 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1462,13 +1487,14 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1476,7 +1502,7 @@ fn indexing_movies_in_three_batches(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1551,13 +1577,14 @@ fn indexing_nested_movies_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1565,7 +1592,7 @@ fn indexing_nested_movies_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1641,13 +1668,14 @@ fn deleting_nested_movies_in_batches_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1655,7 +1683,7 @@ fn deleting_nested_movies_in_batches_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1723,13 +1751,14 @@ fn indexing_nested_movies_without_faceted_fields(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1737,7 +1766,7 @@ fn indexing_nested_movies_without_faceted_fields(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1789,13 +1818,14 @@ fn indexing_geo(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1803,7 +1833,7 @@ fn indexing_geo(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1854,13 +1884,14 @@ fn reindexing_geo(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1868,7 +1899,7 @@ fn reindexing_geo(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1898,13 +1929,14 @@ fn reindexing_geo(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1912,7 +1944,7 @@ fn reindexing_geo(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
| @@ -1965,13 +1997,14 @@ fn deleting_geo_in_batches_default(c: &mut Criterion) { | ||||
|                         None, | ||||
|                         &mut new_fields_ids_map, | ||||
|                         &|| false, | ||||
|                         &|_progress| (), | ||||
|                         Progress::default(), | ||||
|                     ) | ||||
|                     .unwrap(); | ||||
|  | ||||
|                 indexer::index( | ||||
|                     &mut wtxn, | ||||
|                     &index, | ||||
|                     &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                     config.grenad_parameters(), | ||||
|                     &db_fields_ids_map, | ||||
|                     new_fields_ids_map, | ||||
| @@ -1979,7 +2012,7 @@ fn deleting_geo_in_batches_default(c: &mut Criterion) { | ||||
|                     &document_changes, | ||||
|                     EmbeddingConfigs::default(), | ||||
|                     &|| false, | ||||
|                     &|_| (), | ||||
|                     &Progress::default(), | ||||
|                 ) | ||||
|                 .unwrap(); | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,7 @@ use criterion::{criterion_group, criterion_main}; | ||||
| use milli::update::Settings; | ||||
| use utils::Conf; | ||||
|  | ||||
| #[cfg(not(windows))] | ||||
| #[global_allocator] | ||||
| static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,7 @@ use criterion::{criterion_group, criterion_main}; | ||||
| use milli::update::Settings; | ||||
| use utils::Conf; | ||||
|  | ||||
| #[cfg(not(windows))] | ||||
| #[global_allocator] | ||||
| static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; | ||||
|  | ||||
|   | ||||
| @@ -5,6 +5,7 @@ use criterion::{criterion_group, criterion_main}; | ||||
| use milli::update::Settings; | ||||
| use utils::Conf; | ||||
|  | ||||
| #[cfg(not(windows))] | ||||
| #[global_allocator] | ||||
| static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; | ||||
|  | ||||
|   | ||||
| @@ -10,6 +10,7 @@ use bumpalo::Bump; | ||||
| use criterion::BenchmarkId; | ||||
| use memmap2::Mmap; | ||||
| use milli::heed::EnvOpenOptions; | ||||
| use milli::progress::Progress; | ||||
| use milli::update::new::indexer; | ||||
| use milli::update::{IndexDocumentsMethod, IndexerConfig, Settings}; | ||||
| use milli::vector::EmbeddingConfigs; | ||||
| @@ -110,13 +111,14 @@ pub fn base_setup(conf: &Conf) -> Index { | ||||
|             None, | ||||
|             &mut new_fields_ids_map, | ||||
|             &|| false, | ||||
|             &|_progress| (), | ||||
|             Progress::default(), | ||||
|         ) | ||||
|         .unwrap(); | ||||
|  | ||||
|     indexer::index( | ||||
|         &mut wtxn, | ||||
|         &index, | ||||
|         &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|         config.grenad_parameters(), | ||||
|         &db_fields_ids_map, | ||||
|         new_fields_ids_map, | ||||
| @@ -124,7 +126,7 @@ pub fn base_setup(conf: &Conf) -> Index { | ||||
|         &document_changes, | ||||
|         EmbeddingConfigs::default(), | ||||
|         &|| false, | ||||
|         &|_| (), | ||||
|         &Progress::default(), | ||||
|     ) | ||||
|     .unwrap(); | ||||
|  | ||||
|   | ||||
| @@ -17,7 +17,7 @@ http = "1.1.0" | ||||
| meilisearch-types = { path = "../meilisearch-types" } | ||||
| once_cell = "1.19.0" | ||||
| regex = "1.10.5" | ||||
| roaring = { version = "0.10.6", features = ["serde"] } | ||||
| roaring = { version = "0.10.7", features = ["serde"] } | ||||
| serde = { version = "1.0.204", features = ["derive"] } | ||||
| serde_json = { version = "1.0.120", features = ["preserve_order"] } | ||||
| tar = "0.4.41" | ||||
|   | ||||
| @@ -292,6 +292,8 @@ pub(crate) mod test { | ||||
|             embedders: Setting::NotSet, | ||||
|             search_cutoff_ms: Setting::NotSet, | ||||
|             localized_attributes: Setting::NotSet, | ||||
|             facet_search: Setting::NotSet, | ||||
|             prefix_search: Setting::NotSet, | ||||
|             _kind: std::marker::PhantomData, | ||||
|         }; | ||||
|         settings.check() | ||||
|   | ||||
| @@ -382,6 +382,8 @@ impl<T> From<v5::Settings<T>> for v6::Settings<v6::Unchecked> { | ||||
|             embedders: v6::Setting::NotSet, | ||||
|             localized_attributes: v6::Setting::NotSet, | ||||
|             search_cutoff_ms: v6::Setting::NotSet, | ||||
|             facet_search: v6::Setting::NotSet, | ||||
|             prefix_search: v6::Setting::NotSet, | ||||
|             _kind: std::marker::PhantomData, | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -136,6 +136,14 @@ pub struct File { | ||||
| } | ||||
|  | ||||
| impl File { | ||||
|     pub fn from_parts(path: PathBuf, file: Option<NamedTempFile>) -> Self { | ||||
|         Self { path, file } | ||||
|     } | ||||
|  | ||||
|     pub fn into_parts(self) -> (PathBuf, Option<NamedTempFile>) { | ||||
|         (self.path, self.file) | ||||
|     } | ||||
|  | ||||
|     pub fn dry_file() -> Result<Self> { | ||||
|         Ok(Self { path: PathBuf::new(), file: None }) | ||||
|     } | ||||
|   | ||||
| @@ -10,6 +10,7 @@ use either::Either; | ||||
| use fuzzers::Operation; | ||||
| use milli::documents::mmap_from_objects; | ||||
| use milli::heed::EnvOpenOptions; | ||||
| use milli::progress::Progress; | ||||
| use milli::update::new::indexer; | ||||
| use milli::update::{IndexDocumentsMethod, IndexerConfig}; | ||||
| use milli::vector::EmbeddingConfigs; | ||||
| @@ -128,13 +129,14 @@ fn main() { | ||||
|                                     None, | ||||
|                                     &mut new_fields_ids_map, | ||||
|                                     &|| false, | ||||
|                                     &|_progress| (), | ||||
|                                     Progress::default(), | ||||
|                                 ) | ||||
|                                 .unwrap(); | ||||
|  | ||||
|                             indexer::index( | ||||
|                                 &mut wtxn, | ||||
|                                 &index, | ||||
|                                 &milli::ThreadPoolNoAbortBuilder::new().build().unwrap(), | ||||
|                                 indexer_config.grenad_parameters(), | ||||
|                                 &db_fields_ids_map, | ||||
|                                 new_fields_ids_map, | ||||
| @@ -142,7 +144,7 @@ fn main() { | ||||
|                                 &document_changes, | ||||
|                                 embedders, | ||||
|                                 &|| false, | ||||
|                                 &|_| (), | ||||
|                                 &Progress::default(), | ||||
|                             ) | ||||
|                             .unwrap(); | ||||
|  | ||||
|   | ||||
| @@ -13,6 +13,9 @@ license.workspace = true | ||||
| [dependencies] | ||||
| anyhow = "1.0.86" | ||||
| bincode = "1.3.3" | ||||
| bumpalo = "3.16.0" | ||||
| bumparaw-collections = "0.1.2" | ||||
| convert_case = "0.6.0" | ||||
| csv = "1.3.0" | ||||
| derive_builder = "0.20.0" | ||||
| dump = { path = "../dump" } | ||||
| @@ -21,16 +24,15 @@ file-store = { path = "../file-store" } | ||||
| flate2 = "1.0.30" | ||||
| meilisearch-auth = { path = "../meilisearch-auth" } | ||||
| meilisearch-types = { path = "../meilisearch-types" } | ||||
| memmap2 = "0.9.4" | ||||
| page_size = "0.6.0" | ||||
| raw-collections = { git = "https://github.com/meilisearch/raw-collections.git", version = "0.1.0" } | ||||
| rayon = "1.10.0" | ||||
| roaring = { version = "0.10.6", features = ["serde"] } | ||||
| roaring = { version = "0.10.7", features = ["serde"] } | ||||
| serde = { version = "1.0.204", features = ["derive"] } | ||||
| serde_json = { version = "1.0.120", features = ["preserve_order"] } | ||||
| synchronoise = "1.0.1" | ||||
| tempfile = "3.10.1" | ||||
| thiserror = "1.0.61" | ||||
| memmap2 = "0.9.4" | ||||
| time = { version = "0.3.36", features = [ | ||||
|     "serde-well-known", | ||||
|     "formatting", | ||||
| @@ -40,12 +42,11 @@ time = { version = "0.3.36", features = [ | ||||
| tracing = "0.1.40" | ||||
| ureq = "2.10.0" | ||||
| uuid = { version = "1.10.0", features = ["serde", "v4"] } | ||||
| bumpalo = "3.16.0" | ||||
|  | ||||
| [dev-dependencies] | ||||
| arroy = "0.5.0" | ||||
| big_s = "1.0.2" | ||||
| crossbeam = "0.8.4" | ||||
| crossbeam-channel = "0.5.13" | ||||
| insta = { version = "1.39.0", features = ["json", "redactions"] } | ||||
| maplit = "1.0.2" | ||||
| meili-snap = { path = "../meili-snap" } | ||||
|   | ||||
| @@ -115,13 +115,6 @@ pub enum BatchKind { | ||||
|         allow_index_creation: bool, | ||||
|         settings_ids: Vec<TaskId>, | ||||
|     }, | ||||
|     SettingsAndDocumentOperation { | ||||
|         settings_ids: Vec<TaskId>, | ||||
|         method: IndexDocumentsMethod, | ||||
|         allow_index_creation: bool, | ||||
|         primary_key: Option<String>, | ||||
|         operation_ids: Vec<TaskId>, | ||||
|     }, | ||||
|     Settings { | ||||
|         allow_index_creation: bool, | ||||
|         settings_ids: Vec<TaskId>, | ||||
| @@ -146,7 +139,6 @@ impl BatchKind { | ||||
|         match self { | ||||
|             BatchKind::DocumentOperation { allow_index_creation, .. } | ||||
|             | BatchKind::ClearAndSettings { allow_index_creation, .. } | ||||
|             | BatchKind::SettingsAndDocumentOperation { allow_index_creation, .. } | ||||
|             | BatchKind::Settings { allow_index_creation, .. } => Some(*allow_index_creation), | ||||
|             _ => None, | ||||
|         } | ||||
| @@ -154,10 +146,7 @@ impl BatchKind { | ||||
|  | ||||
|     fn primary_key(&self) -> Option<Option<&str>> { | ||||
|         match self { | ||||
|             BatchKind::DocumentOperation { primary_key, .. } | ||||
|             | BatchKind::SettingsAndDocumentOperation { primary_key, .. } => { | ||||
|                 Some(primary_key.as_deref()) | ||||
|             } | ||||
|             BatchKind::DocumentOperation { primary_key, .. } => Some(primary_key.as_deref()), | ||||
|             _ => None, | ||||
|         } | ||||
|     } | ||||
| @@ -275,8 +264,7 @@ impl BatchKind { | ||||
|                 Break(BatchKind::IndexDeletion { ids }) | ||||
|             } | ||||
|             ( | ||||
|                 BatchKind::ClearAndSettings { settings_ids: mut ids, allow_index_creation: _, mut other } | ||||
|                 | BatchKind::SettingsAndDocumentOperation { operation_ids: mut ids, method: _, allow_index_creation: _, primary_key: _, settings_ids: mut other }, | ||||
|                 BatchKind::ClearAndSettings { settings_ids: mut ids, allow_index_creation: _, mut other }, | ||||
|                 K::IndexDeletion, | ||||
|             ) => { | ||||
|                 ids.push(id); | ||||
| @@ -356,15 +344,9 @@ impl BatchKind { | ||||
|             ) => Break(this), | ||||
|  | ||||
|             ( | ||||
|                 BatchKind::DocumentOperation { method, allow_index_creation, primary_key, operation_ids }, | ||||
|                 this @ BatchKind::DocumentOperation { .. }, | ||||
|                 K::Settings { .. }, | ||||
|             ) => Continue(BatchKind::SettingsAndDocumentOperation { | ||||
|                 settings_ids: vec![id], | ||||
|                 method, | ||||
|                 allow_index_creation, | ||||
|                 primary_key, | ||||
|                 operation_ids, | ||||
|             }), | ||||
|             ) => Break(this), | ||||
|  | ||||
|             (BatchKind::DocumentDeletion { mut deletion_ids, includes_by_filter: _ }, K::DocumentClear) => { | ||||
|                 deletion_ids.push(id); | ||||
| @@ -477,63 +459,7 @@ impl BatchKind { | ||||
|                     allow_index_creation, | ||||
|                 }) | ||||
|             } | ||||
|             ( | ||||
|                 BatchKind::SettingsAndDocumentOperation { settings_ids, method: _, mut operation_ids, allow_index_creation, primary_key: _ }, | ||||
|                 K::DocumentClear, | ||||
|             ) => { | ||||
|                 operation_ids.push(id); | ||||
|                 Continue(BatchKind::ClearAndSettings { | ||||
|                     settings_ids, | ||||
|                     other: operation_ids, | ||||
|                     allow_index_creation, | ||||
|                 }) | ||||
|             } | ||||
|  | ||||
|             ( | ||||
|                 BatchKind::SettingsAndDocumentOperation { settings_ids, method: ReplaceDocuments, mut operation_ids, allow_index_creation, primary_key: _}, | ||||
|                 K::DocumentImport { method: ReplaceDocuments, primary_key: pk2, .. }, | ||||
|             ) => { | ||||
|                 operation_ids.push(id); | ||||
|                 Continue(BatchKind::SettingsAndDocumentOperation { | ||||
|                     settings_ids, | ||||
|                     method: ReplaceDocuments, | ||||
|                     allow_index_creation, | ||||
|                         primary_key: pk2, | ||||
|                     operation_ids, | ||||
|                 }) | ||||
|             } | ||||
|             ( | ||||
|                 BatchKind::SettingsAndDocumentOperation { settings_ids, method: UpdateDocuments, allow_index_creation, primary_key: _, mut operation_ids }, | ||||
|                 K::DocumentImport { method: UpdateDocuments, primary_key: pk2, .. }, | ||||
|             ) => { | ||||
|                 operation_ids.push(id); | ||||
|                 Continue(BatchKind::SettingsAndDocumentOperation { | ||||
|                     settings_ids, | ||||
|                     method: UpdateDocuments, | ||||
|                     allow_index_creation, | ||||
|                     primary_key: pk2, | ||||
|                     operation_ids, | ||||
|                 }) | ||||
|             } | ||||
|             // But we can't batch a settings and a doc op with another doc op | ||||
|             // this MUST be AFTER the two previous branch | ||||
|             ( | ||||
|                 this @ BatchKind::SettingsAndDocumentOperation { .. }, | ||||
|                 K::DocumentDeletion { .. } | K::DocumentImport { .. }, | ||||
|             ) => Break(this), | ||||
|             ( | ||||
|                 BatchKind::SettingsAndDocumentOperation { mut settings_ids, method, allow_index_creation,primary_key, operation_ids }, | ||||
|                 K::Settings { .. }, | ||||
|             ) => { | ||||
|                 settings_ids.push(id); | ||||
|                 Continue(BatchKind::SettingsAndDocumentOperation { | ||||
|                     settings_ids, | ||||
|                     method, | ||||
|                     allow_index_creation, | ||||
|                     primary_key, | ||||
|                     operation_ids, | ||||
|                 }) | ||||
|             } | ||||
|             ( | ||||
|                 BatchKind::IndexCreation { .. } | ||||
|                 | BatchKind::IndexDeletion { .. } | ||||
| @@ -808,30 +734,30 @@ mod tests { | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn document_addition_batch_with_settings() { | ||||
|     fn document_addition_doesnt_batch_with_settings() { | ||||
|         // simple case | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true)]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|  | ||||
|         // multiple settings and doc addition | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None), settings(true), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [2, 3], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None), settings(true), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [2, 3], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None), settings(true), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None), settings(true), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))"); | ||||
|  | ||||
|         // addition and setting unordered | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1, 3], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 2] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_imp(UpdateDocuments, true, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1, 3], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 2] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_imp(UpdateDocuments, true, None), settings(true)]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|  | ||||
|         // We ensure this kind of batch doesn't batch with forbidden operations | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_imp(UpdateDocuments, true, None)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_imp(ReplaceDocuments, true, None)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_del()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_del()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_create()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_create()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_update()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_update()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_swap()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_swap()]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         // Doesn't batch with other forbidden operations | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_imp(UpdateDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_del()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_del()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_create()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_create()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_update()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_update()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_swap()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_swap()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -859,8 +785,8 @@ mod tests { | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_clr(), settings(true)]), @"Some((DocumentClear { ids: [0] }, false))"); | ||||
|  | ||||
|         debug_snapshot!(autobatch_from(true, None, [settings(true), doc_clr(), settings(true)]), @"Some((ClearAndSettings { other: [1], allow_index_creation: true, settings_ids: [0, 2] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_clr()]), @"Some((ClearAndSettings { other: [0, 2], allow_index_creation: true, settings_ids: [1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_clr()]), @"Some((ClearAndSettings { other: [0, 2], allow_index_creation: true, settings_ids: [1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_clr()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_clr()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -907,50 +833,6 @@ mod tests { | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [0, 1] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 1] }, false))"); | ||||
|  | ||||
|         // Then the mixed cases. | ||||
|         // The index already exists, whatever is the right of the tasks it shouldn't change the result. | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(true), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments,false, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, false, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments,false, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, false, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments,false, None), settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, false, None), settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments,false, None), settings(true), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, false, None), settings(true), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments,true, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments,true, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(UpdateDocuments, true, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))"); | ||||
|  | ||||
|         // When the index doesn't exists yet it's more complicated. | ||||
|         // Either the first task we encounter create it, in which case we can create a big batch with everything. | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments, true, None), settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(UpdateDocuments, true, None), settings(true), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments, true, None), settings(true), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(UpdateDocuments, true, None), settings(true), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))"); | ||||
|         // The right of the tasks following isn't really important. | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments,true, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(UpdateDocuments, true, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments,true, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(UpdateDocuments, true, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, true))"); | ||||
|         // Or, the second case; the first task doesn't create the index and thus we wants to batch it with only tasks that can't create an index. | ||||
|         // that can be a second task that don't have the right to create an index. Or anything that can't create an index like an index deletion, document deletion, document clear, etc. | ||||
|         // All theses tasks are going to throw an error `Index doesn't exist` once the batch is processed. | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments,false, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(UpdateDocuments, false, None), settings(false), idx_del()]), @"Some((IndexDeletion { ids: [0, 2, 1] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments,false, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(UpdateDocuments, false, None), settings(false), doc_clr(), idx_del()]), @"Some((IndexDeletion { ids: [1, 3, 0, 2] }, false))"); | ||||
|         // The third and final case is when the first task doesn't create an index but is directly followed by a task creating an index. In this case we can't batch whit what | ||||
|         // follows because we first need to process the erronous batch. | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments,false, None), settings(true), idx_del()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(UpdateDocuments, false, None), settings(true), idx_del()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments,false, None), settings(true), doc_clr(), idx_del()]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(UpdateDocuments, false, None), settings(true), doc_clr(), idx_del()]), @"Some((DocumentOperation { method: UpdateDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))"); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
| @@ -959,13 +841,13 @@ mod tests { | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, false, None), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0, 1] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, false, None), doc_imp(ReplaceDocuments, false, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0, 1] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, false, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(true, None, [doc_imp(ReplaceDocuments, false, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))"); | ||||
|  | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments, false, None), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments, true, None), doc_imp(ReplaceDocuments, true, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0, 1] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments, false, None), doc_imp(ReplaceDocuments, false, None)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0, 1] }, false))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((SettingsAndDocumentOperation { settings_ids: [1], method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments, true, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: true, primary_key: None, operation_ids: [0] }, true))"); | ||||
|         debug_snapshot!(autobatch_from(false,None,  [doc_imp(ReplaceDocuments, false, None), settings(true)]), @"Some((DocumentOperation { method: ReplaceDocuments, allow_index_creation: false, primary_key: None, operation_ids: [0] }, false))"); | ||||
|  | ||||
|         // batch deletion and addition | ||||
|   | ||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							| @@ -104,7 +104,7 @@ pub enum Error { | ||||
|     )] | ||||
|     InvalidTaskCanceledBy { canceled_by: String }, | ||||
|     #[error( | ||||
|         "{index_uid} is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_), and can not be more than 512 bytes." | ||||
|         "{index_uid} is not a valid index uid. Index uid can be an integer or a string containing only alphanumeric characters, hyphens (-) and underscores (_), and can not be more than 400 bytes." | ||||
|     )] | ||||
|     InvalidIndexUid { index_uid: String }, | ||||
|     #[error("Task `{0}` not found.")] | ||||
| @@ -122,8 +122,11 @@ pub enum Error { | ||||
|     Dump(#[from] dump::Error), | ||||
|     #[error(transparent)] | ||||
|     Heed(#[from] heed::Error), | ||||
|     #[error(transparent)] | ||||
|     Milli(#[from] milli::Error), | ||||
|     #[error("{}", match .index_uid { | ||||
|         Some(uid) if !uid.is_empty() => format!("Index `{}`: {error}", uid), | ||||
|         _ => format!("{error}") | ||||
|     })] | ||||
|     Milli { error: milli::Error, index_uid: Option<String> }, | ||||
|     #[error("An unexpected crash occurred when processing the task.")] | ||||
|     ProcessBatchPanicked, | ||||
|     #[error(transparent)] | ||||
| @@ -190,7 +193,7 @@ impl Error { | ||||
|             | Error::AbortedTask | ||||
|             | Error::Dump(_) | ||||
|             | Error::Heed(_) | ||||
|             | Error::Milli(_) | ||||
|             | Error::Milli { .. } | ||||
|             | Error::ProcessBatchPanicked | ||||
|             | Error::FileStore(_) | ||||
|             | Error::IoError(_) | ||||
| @@ -209,6 +212,20 @@ impl Error { | ||||
|     pub fn with_custom_error_code(self, code: Code) -> Self { | ||||
|         Self::WithCustomErrorCode(code, Box::new(self)) | ||||
|     } | ||||
|  | ||||
|     pub fn from_milli(err: milli::Error, index_uid: Option<String>) -> Self { | ||||
|         match err { | ||||
|             milli::Error::UserError(milli::UserError::InvalidFilter(_)) => { | ||||
|                 Self::Milli { error: err, index_uid } | ||||
|                     .with_custom_error_code(Code::InvalidDocumentFilter) | ||||
|             } | ||||
|             milli::Error::UserError(milli::UserError::InvalidFilterExpression { .. }) => { | ||||
|                 Self::Milli { error: err, index_uid } | ||||
|                     .with_custom_error_code(Code::InvalidDocumentFilter) | ||||
|             } | ||||
|             _ => Self::Milli { error: err, index_uid }, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ErrorCode for Error { | ||||
| @@ -236,7 +253,7 @@ impl ErrorCode for Error { | ||||
|             // TODO: not sure of the Code to use | ||||
|             Error::NoSpaceLeftInTaskQueue => Code::NoSpaceLeftOnDevice, | ||||
|             Error::Dump(e) => e.error_code(), | ||||
|             Error::Milli(e) => e.error_code(), | ||||
|             Error::Milli { error, .. } => error.error_code(), | ||||
|             Error::ProcessBatchPanicked => Code::Internal, | ||||
|             Error::Heed(e) => e.error_code(), | ||||
|             Error::HeedTransaction(e) => e.error_code(), | ||||
|   | ||||
| @@ -3,14 +3,13 @@ use std::path::Path; | ||||
| use std::time::Duration; | ||||
|  | ||||
| use meilisearch_types::heed::{EnvClosingEvent, EnvFlags, EnvOpenOptions}; | ||||
| use meilisearch_types::milli::Index; | ||||
| use meilisearch_types::milli::{Index, Result}; | ||||
| use time::OffsetDateTime; | ||||
| use uuid::Uuid; | ||||
|  | ||||
| use super::IndexStatus::{self, Available, BeingDeleted, Closing, Missing}; | ||||
| use crate::clamp_to_page_size; | ||||
| use crate::lru::{InsertionOutcome, LruMap}; | ||||
| use crate::{clamp_to_page_size, Result}; | ||||
|  | ||||
| /// Keep an internally consistent view of the open indexes in memory. | ||||
| /// | ||||
| /// This view is made of an LRU cache that will evict the least frequently used indexes when new indexes are opened. | ||||
|   | ||||
| @@ -5,6 +5,7 @@ use std::{fs, thread}; | ||||
|  | ||||
| use meilisearch_types::heed::types::{SerdeJson, Str}; | ||||
| use meilisearch_types::heed::{Database, Env, RoTxn, RwTxn}; | ||||
| use meilisearch_types::milli; | ||||
| use meilisearch_types::milli::update::IndexerConfig; | ||||
| use meilisearch_types::milli::{FieldDistribution, Index}; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| @@ -121,7 +122,7 @@ impl IndexStats { | ||||
|     /// # Parameters | ||||
|     /// | ||||
|     /// - rtxn: a RO transaction for the index, obtained from `Index::read_txn()`. | ||||
|     pub fn new(index: &Index, rtxn: &RoTxn) -> Result<Self> { | ||||
|     pub fn new(index: &Index, rtxn: &RoTxn) -> milli::Result<Self> { | ||||
|         Ok(IndexStats { | ||||
|             number_of_documents: index.number_of_documents(rtxn)?, | ||||
|             database_size: index.on_disk_size()?, | ||||
| @@ -183,13 +184,18 @@ impl IndexMapper { | ||||
|                 // Error if the UUIDv4 somehow already exists in the map, since it should be fresh. | ||||
|                 // This is very unlikely to happen in practice. | ||||
|                 // TODO: it would be better to lazily create the index. But we need an Index::open function for milli. | ||||
|                 let index = self.index_map.write().unwrap().create( | ||||
|                     &uuid, | ||||
|                     &index_path, | ||||
|                     date, | ||||
|                     self.enable_mdb_writemap, | ||||
|                     self.index_base_map_size, | ||||
|                 )?; | ||||
|                 let index = self | ||||
|                     .index_map | ||||
|                     .write() | ||||
|                     .unwrap() | ||||
|                     .create( | ||||
|                         &uuid, | ||||
|                         &index_path, | ||||
|                         date, | ||||
|                         self.enable_mdb_writemap, | ||||
|                         self.index_base_map_size, | ||||
|                     ) | ||||
|                     .map_err(|e| Error::from_milli(e, Some(uuid.to_string())))?; | ||||
|  | ||||
|                 wtxn.commit()?; | ||||
|  | ||||
| @@ -357,7 +363,9 @@ impl IndexMapper { | ||||
|                     }; | ||||
|                     let index_path = self.base_path.join(uuid.to_string()); | ||||
|                     // take the lock to reopen the environment. | ||||
|                     reopen.reopen(&mut self.index_map.write().unwrap(), &index_path)?; | ||||
|                     reopen | ||||
|                         .reopen(&mut self.index_map.write().unwrap(), &index_path) | ||||
|                         .map_err(|e| Error::from_milli(e, Some(uuid.to_string())))?; | ||||
|                     continue; | ||||
|                 } | ||||
|                 BeingDeleted => return Err(Error::IndexNotFound(name.to_string())), | ||||
| @@ -372,13 +380,15 @@ impl IndexMapper { | ||||
|                         Missing => { | ||||
|                             let index_path = self.base_path.join(uuid.to_string()); | ||||
|  | ||||
|                             break index_map.create( | ||||
|                                 &uuid, | ||||
|                                 &index_path, | ||||
|                                 None, | ||||
|                                 self.enable_mdb_writemap, | ||||
|                                 self.index_base_map_size, | ||||
|                             )?; | ||||
|                             break index_map | ||||
|                                 .create( | ||||
|                                     &uuid, | ||||
|                                     &index_path, | ||||
|                                     None, | ||||
|                                     self.enable_mdb_writemap, | ||||
|                                     self.index_base_map_size, | ||||
|                                 ) | ||||
|                                 .map_err(|e| Error::from_milli(e, Some(uuid.to_string())))?; | ||||
|                         } | ||||
|                         Available(index) => break index, | ||||
|                         Closing(_) => { | ||||
| @@ -460,6 +470,7 @@ impl IndexMapper { | ||||
|                 let index = self.index(rtxn, index_uid)?; | ||||
|                 let index_rtxn = index.read_txn()?; | ||||
|                 IndexStats::new(&index, &index_rtxn) | ||||
|                     .map_err(|e| Error::from_milli(e, Some(uuid.to_string()))) | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|   | ||||
| @@ -353,7 +353,7 @@ pub fn snapshot_canceled_by(rtxn: &RoTxn, db: Database<BEU32, RoaringBitmapCodec | ||||
|  | ||||
| pub fn snapshot_batch(batch: &Batch) -> String { | ||||
|     let mut snap = String::new(); | ||||
|     let Batch { uid, details, stats, started_at, finished_at } = batch; | ||||
|     let Batch { uid, details, stats, started_at, finished_at, progress: _ } = batch; | ||||
|     if let Some(finished_at) = finished_at { | ||||
|         assert!(finished_at > started_at); | ||||
|     } | ||||
|   | ||||
| @@ -26,6 +26,7 @@ mod index_mapper; | ||||
| #[cfg(test)] | ||||
| mod insta_snapshot; | ||||
| mod lru; | ||||
| mod processing; | ||||
| mod utils; | ||||
| pub mod uuid_codec; | ||||
|  | ||||
| @@ -56,12 +57,12 @@ use meilisearch_types::heed::types::{SerdeBincode, SerdeJson, Str, I128}; | ||||
| use meilisearch_types::heed::{self, Database, Env, PutFlags, RoTxn, RwTxn}; | ||||
| use meilisearch_types::milli::documents::DocumentsBatchBuilder; | ||||
| use meilisearch_types::milli::index::IndexEmbeddingConfig; | ||||
| use meilisearch_types::milli::update::new::indexer::document_changes::Progress; | ||||
| use meilisearch_types::milli::update::IndexerConfig; | ||||
| use meilisearch_types::milli::vector::{Embedder, EmbedderOptions, EmbeddingConfigs}; | ||||
| use meilisearch_types::milli::{self, CboRoaringBitmapCodec, Index, RoaringBitmapCodec, BEU32}; | ||||
| use meilisearch_types::task_view::TaskView; | ||||
| use meilisearch_types::tasks::{Kind, KindWithContent, Status, Task, TaskProgress}; | ||||
| use meilisearch_types::tasks::{Kind, KindWithContent, Status, Task}; | ||||
| use processing::ProcessingTasks; | ||||
| use rayon::current_num_threads; | ||||
| use rayon::prelude::{IntoParallelIterator, ParallelIterator}; | ||||
| use roaring::RoaringBitmap; | ||||
| @@ -72,7 +73,8 @@ use utils::{filter_out_references_to_newer_tasks, keep_ids_within_datetimes, map | ||||
| use uuid::Uuid; | ||||
|  | ||||
| use crate::index_mapper::IndexMapper; | ||||
| use crate::utils::{check_index_swap_validity, clamp_to_page_size, ProcessingBatch}; | ||||
| use crate::processing::{AtomicTaskStep, BatchProgress}; | ||||
| use crate::utils::{check_index_swap_validity, clamp_to_page_size}; | ||||
|  | ||||
| pub(crate) type BEI128 = I128<BE>; | ||||
|  | ||||
| @@ -163,48 +165,6 @@ impl Query { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Debug, Clone)] | ||||
| pub struct ProcessingTasks { | ||||
|     batch: Option<ProcessingBatch>, | ||||
|     /// The list of tasks ids that are currently running. | ||||
|     processing: RoaringBitmap, | ||||
|     /// The progress on processing tasks | ||||
|     progress: Option<TaskProgress>, | ||||
| } | ||||
|  | ||||
| impl ProcessingTasks { | ||||
|     /// Creates an empty `ProcessingAt` struct. | ||||
|     fn new() -> ProcessingTasks { | ||||
|         ProcessingTasks { batch: None, processing: RoaringBitmap::new(), progress: None } | ||||
|     } | ||||
|  | ||||
|     /// Stores the currently processing tasks, and the date time at which it started. | ||||
|     fn start_processing(&mut self, processing_batch: ProcessingBatch, processing: RoaringBitmap) { | ||||
|         self.batch = Some(processing_batch); | ||||
|         self.processing = processing; | ||||
|     } | ||||
|  | ||||
|     fn update_progress(&mut self, progress: Progress) -> TaskProgress { | ||||
|         self.progress.get_or_insert_with(TaskProgress::default).update(progress) | ||||
|     } | ||||
|  | ||||
|     /// Set the processing tasks to an empty list | ||||
|     fn stop_processing(&mut self) -> Self { | ||||
|         self.progress = None; | ||||
|  | ||||
|         Self { | ||||
|             batch: std::mem::take(&mut self.batch), | ||||
|             processing: std::mem::take(&mut self.processing), | ||||
|             progress: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Returns `true` if there, at least, is one task that is currently processing that we must stop. | ||||
|     fn must_cancel_processing_tasks(&self, canceled_tasks: &RoaringBitmap) -> bool { | ||||
|         !self.processing.is_disjoint(canceled_tasks) | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Default, Clone, Debug)] | ||||
| struct MustStopProcessing(Arc<AtomicBool>); | ||||
|  | ||||
| @@ -407,7 +367,7 @@ pub struct IndexScheduler { | ||||
|     /// | ||||
|     /// See [self.breakpoint()](`IndexScheduler::breakpoint`) for an explanation. | ||||
|     #[cfg(test)] | ||||
|     test_breakpoint_sdr: crossbeam::channel::Sender<(Breakpoint, bool)>, | ||||
|     test_breakpoint_sdr: crossbeam_channel::Sender<(Breakpoint, bool)>, | ||||
|  | ||||
|     /// A list of planned failures within the [`tick`](IndexScheduler::tick) method of the index scheduler. | ||||
|     /// | ||||
| @@ -476,7 +436,7 @@ impl IndexScheduler { | ||||
|     /// Create an index scheduler and start its run loop. | ||||
|     pub fn new( | ||||
|         options: IndexSchedulerOptions, | ||||
|         #[cfg(test)] test_breakpoint_sdr: crossbeam::channel::Sender<(Breakpoint, bool)>, | ||||
|         #[cfg(test)] test_breakpoint_sdr: crossbeam_channel::Sender<(Breakpoint, bool)>, | ||||
|         #[cfg(test)] planned_failures: Vec<(usize, tests::FailureLocation)>, | ||||
|     ) -> Result<Self> { | ||||
|         std::fs::create_dir_all(&options.tasks_path)?; | ||||
| @@ -813,7 +773,7 @@ impl IndexScheduler { | ||||
|             let mut batch_tasks = RoaringBitmap::new(); | ||||
|             for batch_uid in batch_uids { | ||||
|                 if processing_batch.as_ref().map_or(false, |batch| batch.uid == *batch_uid) { | ||||
|                     batch_tasks |= &processing_tasks; | ||||
|                     batch_tasks |= &*processing_tasks; | ||||
|                 } else { | ||||
|                     batch_tasks |= self.tasks_in_batch(rtxn, *batch_uid)?; | ||||
|                 } | ||||
| @@ -827,13 +787,13 @@ impl IndexScheduler { | ||||
|                 match status { | ||||
|                     // special case for Processing tasks | ||||
|                     Status::Processing => { | ||||
|                         status_tasks |= &processing_tasks; | ||||
|                         status_tasks |= &*processing_tasks; | ||||
|                     } | ||||
|                     status => status_tasks |= &self.get_status(rtxn, *status)?, | ||||
|                 }; | ||||
|             } | ||||
|             if !status.contains(&Status::Processing) { | ||||
|                 tasks -= &processing_tasks; | ||||
|                 tasks -= &*processing_tasks; | ||||
|             } | ||||
|             tasks &= status_tasks; | ||||
|         } | ||||
| @@ -882,7 +842,7 @@ impl IndexScheduler { | ||||
|         // Once we have filtered the two subsets, we put them back together and assign it back to `tasks`. | ||||
|         tasks = { | ||||
|             let (mut filtered_non_processing_tasks, mut filtered_processing_tasks) = | ||||
|                 (&tasks - &processing_tasks, &tasks & &processing_tasks); | ||||
|                 (&tasks - &*processing_tasks, &tasks & &*processing_tasks); | ||||
|  | ||||
|             // special case for Processing tasks | ||||
|             // A closure that clears the filtered_processing_tasks if their started_at date falls outside the given bounds | ||||
| @@ -1090,7 +1050,7 @@ impl IndexScheduler { | ||||
|         // Once we have filtered the two subsets, we put them back together and assign it back to `batches`. | ||||
|         batches = { | ||||
|             let (mut filtered_non_processing_batches, mut filtered_processing_batches) = | ||||
|                 (&batches - &processing.processing, &batches & &processing.processing); | ||||
|                 (&batches - &*processing.processing, &batches & &*processing.processing); | ||||
|  | ||||
|             // special case for Processing batches | ||||
|             // A closure that clears the filtered_processing_batches if their started_at date falls outside the given bounds | ||||
| @@ -1440,7 +1400,7 @@ impl IndexScheduler { | ||||
|  | ||||
|         // if the task doesn't delete anything and 50% of the task queue is full, we must refuse to enqueue the incomming task | ||||
|         if !matches!(&kind, KindWithContent::TaskDeletion { tasks, .. } if !tasks.is_empty()) | ||||
|             && (self.env.non_free_pages_size()? * 100) / self.env.info().map_size as u64 > 50 | ||||
|             && (self.env.non_free_pages_size()? * 100) / self.env.info().map_size as u64 > 40 | ||||
|         { | ||||
|             return Err(Error::NoSpaceLeftInTaskQueue); | ||||
|         } | ||||
| @@ -1606,7 +1566,8 @@ impl IndexScheduler { | ||||
|  | ||||
|         // We reset the must_stop flag to be sure that we don't stop processing tasks | ||||
|         self.must_stop_processing.reset(); | ||||
|         self.processing_tasks | ||||
|         let progress = self | ||||
|             .processing_tasks | ||||
|             .write() | ||||
|             .unwrap() | ||||
|             // We can clone the processing batch here because we don't want its modification to affect the view of the processing batches | ||||
| @@ -1619,11 +1580,12 @@ impl IndexScheduler { | ||||
|         let res = { | ||||
|             let cloned_index_scheduler = self.private_clone(); | ||||
|             let processing_batch = &mut processing_batch; | ||||
|             let progress = progress.clone(); | ||||
|             std::thread::scope(|s| { | ||||
|                 let handle = std::thread::Builder::new() | ||||
|                     .name(String::from("batch-operation")) | ||||
|                     .spawn_scoped(s, move || { | ||||
|                         cloned_index_scheduler.process_batch(batch, processing_batch) | ||||
|                         cloned_index_scheduler.process_batch(batch, processing_batch, progress) | ||||
|                     }) | ||||
|                     .unwrap(); | ||||
|                 handle.join().unwrap_or(Err(Error::ProcessBatchPanicked)) | ||||
| @@ -1636,6 +1598,7 @@ impl IndexScheduler { | ||||
|         #[cfg(test)] | ||||
|         self.maybe_fail(tests::FailureLocation::AcquiringWtxn)?; | ||||
|  | ||||
|         progress.update_progress(BatchProgress::WritingTasksToDisk); | ||||
|         processing_batch.finished(); | ||||
|         let mut wtxn = self.env.write_txn().map_err(Error::HeedTransaction)?; | ||||
|         let mut canceled = RoaringBitmap::new(); | ||||
| @@ -1645,12 +1608,15 @@ impl IndexScheduler { | ||||
|                 #[cfg(test)] | ||||
|                 self.breakpoint(Breakpoint::ProcessBatchSucceeded); | ||||
|  | ||||
|                 let (task_progress, task_progress_obj) = AtomicTaskStep::new(tasks.len() as u32); | ||||
|                 progress.update_progress(task_progress_obj); | ||||
|                 let mut success = 0; | ||||
|                 let mut failure = 0; | ||||
|                 let mut canceled_by = None; | ||||
|  | ||||
|                 #[allow(unused_variables)] | ||||
|                 for (i, mut task) in tasks.into_iter().enumerate() { | ||||
|                     task_progress.fetch_add(1, Ordering::Relaxed); | ||||
|                     processing_batch.update(&mut task); | ||||
|                     if task.status == Status::Canceled { | ||||
|                         canceled.insert(task.uid); | ||||
| @@ -1678,9 +1644,10 @@ impl IndexScheduler { | ||||
|                 tracing::info!("A batch of tasks was successfully completed with {success} successful tasks and {failure} failed tasks."); | ||||
|             } | ||||
|             // If we have an abortion error we must stop the tick here and re-schedule tasks. | ||||
|             Err(Error::Milli(milli::Error::InternalError( | ||||
|                 milli::InternalError::AbortedIndexation, | ||||
|             ))) | ||||
|             Err(Error::Milli { | ||||
|                 error: milli::Error::InternalError(milli::InternalError::AbortedIndexation), | ||||
|                 .. | ||||
|             }) | ||||
|             | Err(Error::AbortedTask) => { | ||||
|                 #[cfg(test)] | ||||
|                 self.breakpoint(Breakpoint::AbortedIndexation); | ||||
| @@ -1699,9 +1666,10 @@ impl IndexScheduler { | ||||
|             // 2. close the associated environment | ||||
|             // 3. resize it | ||||
|             // 4. re-schedule tasks | ||||
|             Err(Error::Milli(milli::Error::UserError( | ||||
|                 milli::UserError::MaxDatabaseSizeReached, | ||||
|             ))) if index_uid.is_some() => { | ||||
|             Err(Error::Milli { | ||||
|                 error: milli::Error::UserError(milli::UserError::MaxDatabaseSizeReached), | ||||
|                 .. | ||||
|             }) if index_uid.is_some() => { | ||||
|                 // fixme: add index_uid to match to avoid the unwrap | ||||
|                 let index_uid = index_uid.unwrap(); | ||||
|                 // fixme: handle error more gracefully? not sure when this could happen | ||||
| @@ -1716,8 +1684,12 @@ impl IndexScheduler { | ||||
|             Err(err) => { | ||||
|                 #[cfg(test)] | ||||
|                 self.breakpoint(Breakpoint::ProcessBatchFailed); | ||||
|                 let (task_progress, task_progress_obj) = AtomicTaskStep::new(ids.len() as u32); | ||||
|                 progress.update_progress(task_progress_obj); | ||||
|  | ||||
|                 let error: ResponseError = err.into(); | ||||
|                 for id in ids.iter() { | ||||
|                     task_progress.fetch_add(1, Ordering::Relaxed); | ||||
|                     let mut task = self | ||||
|                         .get_task(&wtxn, id) | ||||
|                         .map_err(|e| Error::TaskDatabaseUpdate(Box::new(e)))? | ||||
| @@ -1738,11 +1710,8 @@ impl IndexScheduler { | ||||
|             } | ||||
|         } | ||||
|  | ||||
|         self.processing_tasks.write().unwrap().stop_processing(); | ||||
|         // We must re-add the canceled task so they're part of the same batch. | ||||
|         // processed.processing |= canceled; | ||||
|         ids |= canceled; | ||||
|  | ||||
|         self.write_batch(&mut wtxn, processing_batch, &ids)?; | ||||
|  | ||||
|         #[cfg(test)] | ||||
| @@ -1750,6 +1719,10 @@ impl IndexScheduler { | ||||
|  | ||||
|         wtxn.commit().map_err(Error::HeedTransaction)?; | ||||
|  | ||||
|         // We should stop processing AFTER everything is processed and written to disk otherwise, a batch (which only lives in RAM) may appear in the processing task | ||||
|         // and then become « not found » for some time until the commit everything is written and the final commit is made. | ||||
|         self.processing_tasks.write().unwrap().stop_processing(); | ||||
|  | ||||
|         // Once the tasks are committed, we should delete all the update files associated ASAP to avoid leaking files in case of a restart | ||||
|         tracing::debug!("Deleting the update files"); | ||||
|  | ||||
| @@ -1942,6 +1915,7 @@ impl IndexScheduler { | ||||
|     // TODO: consider using a type alias or a struct embedder/template | ||||
|     pub fn embedders( | ||||
|         &self, | ||||
|         index_uid: String, | ||||
|         embedding_configs: Vec<IndexEmbeddingConfig>, | ||||
|     ) -> Result<EmbeddingConfigs> { | ||||
|         let res: Result<_> = embedding_configs | ||||
| @@ -1952,8 +1926,12 @@ impl IndexScheduler { | ||||
|                      config: milli::vector::EmbeddingConfig { embedder_options, prompt, quantized }, | ||||
|                      .. | ||||
|                  }| { | ||||
|                     let prompt = | ||||
|                         Arc::new(prompt.try_into().map_err(meilisearch_types::milli::Error::from)?); | ||||
|                     let prompt = Arc::new( | ||||
|                         prompt | ||||
|                             .try_into() | ||||
|                             .map_err(meilisearch_types::milli::Error::from) | ||||
|                             .map_err(|err| Error::from_milli(err, Some(index_uid.clone())))?, | ||||
|                     ); | ||||
|                     // optimistically return existing embedder | ||||
|                     { | ||||
|                         let embedders = self.embedders.read().unwrap(); | ||||
| @@ -1969,7 +1947,9 @@ impl IndexScheduler { | ||||
|                     let embedder = Arc::new( | ||||
|                         Embedder::new(embedder_options.clone()) | ||||
|                             .map_err(meilisearch_types::milli::vector::Error::from) | ||||
|                             .map_err(meilisearch_types::milli::Error::from)?, | ||||
|                             .map_err(|err| { | ||||
|                                 Error::from_milli(err.into(), Some(index_uid.clone())) | ||||
|                             })?, | ||||
|                     ); | ||||
|                     { | ||||
|                         let mut embedders = self.embedders.write().unwrap(); | ||||
| @@ -2237,7 +2217,7 @@ mod tests { | ||||
|     use std::time::Instant; | ||||
|  | ||||
|     use big_s::S; | ||||
|     use crossbeam::channel::RecvTimeoutError; | ||||
|     use crossbeam_channel::RecvTimeoutError; | ||||
|     use file_store::File; | ||||
|     use insta::assert_json_snapshot; | ||||
|     use maplit::btreeset; | ||||
| @@ -2289,7 +2269,7 @@ mod tests { | ||||
|             configuration: impl Fn(&mut IndexSchedulerOptions), | ||||
|         ) -> (Self, IndexSchedulerHandle) { | ||||
|             let tempdir = TempDir::new().unwrap(); | ||||
|             let (sender, receiver) = crossbeam::channel::bounded(0); | ||||
|             let (sender, receiver) = crossbeam_channel::bounded(0); | ||||
|  | ||||
|             let indexer_config = IndexerConfig { skip_index_budget: true, ..Default::default() }; | ||||
|  | ||||
| @@ -2421,7 +2401,7 @@ mod tests { | ||||
|     pub struct IndexSchedulerHandle { | ||||
|         _tempdir: TempDir, | ||||
|         index_scheduler: IndexScheduler, | ||||
|         test_breakpoint_rcv: crossbeam::channel::Receiver<(Breakpoint, bool)>, | ||||
|         test_breakpoint_rcv: crossbeam_channel::Receiver<(Breakpoint, bool)>, | ||||
|         last_breakpoint: Breakpoint, | ||||
|     } | ||||
|  | ||||
| @@ -4318,10 +4298,35 @@ mod tests { | ||||
|         let proc = index_scheduler.processing_tasks.read().unwrap().clone(); | ||||
|  | ||||
|         let query = Query { statuses: Some(vec![Status::Processing]), ..Default::default() }; | ||||
|         let (batches, _) = index_scheduler | ||||
|             .get_batch_ids_from_authorized_indexes(&rtxn, &proc, &query, &AuthFilter::default()) | ||||
|         let (mut batches, _) = index_scheduler | ||||
|             .get_batches_from_authorized_indexes(query.clone(), &AuthFilter::default()) | ||||
|             .unwrap(); | ||||
|         snapshot!(snapshot_bitmap(&batches), @"[0,]"); // only the processing batch in the first tick | ||||
|         assert_eq!(batches.len(), 1); | ||||
|         batches[0].started_at = OffsetDateTime::UNIX_EPOCH; | ||||
|         // Insta cannot snapshot our batches because the batch stats contains an enum as key: https://github.com/mitsuhiko/insta/issues/689 | ||||
|         let batch = serde_json::to_string_pretty(&batches[0]).unwrap(); | ||||
|         snapshot!(batch, @r#" | ||||
|         { | ||||
|           "uid": 0, | ||||
|           "details": { | ||||
|             "primaryKey": "mouse" | ||||
|           }, | ||||
|           "stats": { | ||||
|             "totalNbTasks": 1, | ||||
|             "status": { | ||||
|               "processing": 1 | ||||
|             }, | ||||
|             "types": { | ||||
|               "indexCreation": 1 | ||||
|             }, | ||||
|             "indexUids": { | ||||
|               "catto": 1 | ||||
|             } | ||||
|           }, | ||||
|           "startedAt": "1970-01-01T00:00:00Z", | ||||
|           "finishedAt": null | ||||
|         } | ||||
|         "#); | ||||
|  | ||||
|         let query = Query { statuses: Some(vec![Status::Enqueued]), ..Default::default() }; | ||||
|         let (batches, _) = index_scheduler | ||||
| @@ -6145,7 +6150,7 @@ mod tests { | ||||
|             insta::assert_json_snapshot!(simple_hf_config.embedder_options); | ||||
|             let simple_hf_name = name.clone(); | ||||
|  | ||||
|             let configs = index_scheduler.embedders(configs).unwrap(); | ||||
|             let configs = index_scheduler.embedders("doggos".to_string(), configs).unwrap(); | ||||
|             let (hf_embedder, _, _) = configs.get(&simple_hf_name).unwrap(); | ||||
|             let beagle_embed = | ||||
|                 hf_embedder.embed_one(S("Intel the beagle best doggo"), None).unwrap(); | ||||
|   | ||||
							
								
								
									
										316
									
								
								crates/index-scheduler/src/processing.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										316
									
								
								crates/index-scheduler/src/processing.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,316 @@ | ||||
| use std::borrow::Cow; | ||||
| use std::sync::Arc; | ||||
|  | ||||
| use enum_iterator::Sequence; | ||||
| use meilisearch_types::milli::progress::{AtomicSubStep, NamedStep, Progress, ProgressView, Step}; | ||||
| use meilisearch_types::milli::{make_atomic_progress, make_enum_progress}; | ||||
| use roaring::RoaringBitmap; | ||||
|  | ||||
| use crate::utils::ProcessingBatch; | ||||
|  | ||||
| #[derive(Clone)] | ||||
| pub struct ProcessingTasks { | ||||
|     pub batch: Option<Arc<ProcessingBatch>>, | ||||
|     /// The list of tasks ids that are currently running. | ||||
|     pub processing: Arc<RoaringBitmap>, | ||||
|     /// The progress on processing tasks | ||||
|     pub progress: Option<Progress>, | ||||
| } | ||||
|  | ||||
| impl ProcessingTasks { | ||||
|     /// Creates an empty `ProcessingAt` struct. | ||||
|     pub fn new() -> ProcessingTasks { | ||||
|         ProcessingTasks { batch: None, processing: Arc::new(RoaringBitmap::new()), progress: None } | ||||
|     } | ||||
|  | ||||
|     pub fn get_progress_view(&self) -> Option<ProgressView> { | ||||
|         Some(self.progress.as_ref()?.as_progress_view()) | ||||
|     } | ||||
|  | ||||
|     /// Stores the currently processing tasks, and the date time at which it started. | ||||
|     pub fn start_processing( | ||||
|         &mut self, | ||||
|         processing_batch: ProcessingBatch, | ||||
|         processing: RoaringBitmap, | ||||
|     ) -> Progress { | ||||
|         self.batch = Some(Arc::new(processing_batch)); | ||||
|         self.processing = Arc::new(processing); | ||||
|         let progress = Progress::default(); | ||||
|         progress.update_progress(BatchProgress::ProcessingTasks); | ||||
|         self.progress = Some(progress.clone()); | ||||
|  | ||||
|         progress | ||||
|     } | ||||
|  | ||||
|     /// Set the processing tasks to an empty list | ||||
|     pub fn stop_processing(&mut self) -> Self { | ||||
|         self.progress = None; | ||||
|  | ||||
|         Self { | ||||
|             batch: std::mem::take(&mut self.batch), | ||||
|             processing: std::mem::take(&mut self.processing), | ||||
|             progress: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     /// Returns `true` if there, at least, is one task that is currently processing that we must stop. | ||||
|     pub fn must_cancel_processing_tasks(&self, canceled_tasks: &RoaringBitmap) -> bool { | ||||
|         !self.processing.is_disjoint(canceled_tasks) | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum BatchProgress { | ||||
|         ProcessingTasks, | ||||
|         WritingTasksToDisk, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum TaskCancelationProgress { | ||||
|         RetrievingTasks, | ||||
|         UpdatingTasks, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum TaskDeletionProgress { | ||||
|         DeletingTasksDateTime, | ||||
|         DeletingTasksMetadata, | ||||
|         DeletingTasks, | ||||
|         DeletingBatches, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum SnapshotCreationProgress { | ||||
|         StartTheSnapshotCreation, | ||||
|         SnapshotTheIndexScheduler, | ||||
|         SnapshotTheUpdateFiles, | ||||
|         SnapshotTheIndexes, | ||||
|         SnapshotTheApiKeys, | ||||
|         CreateTheTarball, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum DumpCreationProgress { | ||||
|         StartTheDumpCreation, | ||||
|         DumpTheApiKeys, | ||||
|         DumpTheTasks, | ||||
|         DumpTheIndexes, | ||||
|         DumpTheExperimentalFeatures, | ||||
|         CompressTheDump, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum CreateIndexProgress { | ||||
|         CreatingTheIndex, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum UpdateIndexProgress { | ||||
|         UpdatingTheIndex, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum DeleteIndexProgress { | ||||
|         DeletingTheIndex, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum SwappingTheIndexes { | ||||
|         EnsuringCorrectnessOfTheSwap, | ||||
|         SwappingTheIndexes, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum InnerSwappingTwoIndexes { | ||||
|         RetrieveTheTasks, | ||||
|         UpdateTheTasks, | ||||
|         UpdateTheIndexesMetadata, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum DocumentOperationProgress { | ||||
|         RetrievingConfig, | ||||
|         ComputingDocumentChanges, | ||||
|         Indexing, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum DocumentEditionProgress { | ||||
|         RetrievingConfig, | ||||
|         ComputingDocumentChanges, | ||||
|         Indexing, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum DocumentDeletionProgress { | ||||
|         RetrievingConfig, | ||||
|         DeleteDocuments, | ||||
|         Indexing, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_enum_progress! { | ||||
|     pub enum SettingsProgress { | ||||
|         RetrievingAndMergingTheSettings, | ||||
|         ApplyTheSettings, | ||||
|     } | ||||
| } | ||||
|  | ||||
| make_atomic_progress!(Task alias AtomicTaskStep => "task" ); | ||||
| make_atomic_progress!(Document alias AtomicDocumentStep => "document" ); | ||||
| make_atomic_progress!(Batch alias AtomicBatchStep => "batch" ); | ||||
| make_atomic_progress!(UpdateFile alias AtomicUpdateFileStep => "update file" ); | ||||
|  | ||||
| pub struct VariableNameStep { | ||||
|     name: String, | ||||
|     current: u32, | ||||
|     total: u32, | ||||
| } | ||||
|  | ||||
| impl VariableNameStep { | ||||
|     pub fn new(name: impl Into<String>, current: u32, total: u32) -> Self { | ||||
|         Self { name: name.into(), current, total } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Step for VariableNameStep { | ||||
|     fn name(&self) -> Cow<'static, str> { | ||||
|         self.name.clone().into() | ||||
|     } | ||||
|  | ||||
|     fn current(&self) -> u32 { | ||||
|         self.current | ||||
|     } | ||||
|  | ||||
|     fn total(&self) -> u32 { | ||||
|         self.total | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| mod test { | ||||
|     use std::sync::atomic::Ordering; | ||||
|  | ||||
|     use meili_snap::{json_string, snapshot}; | ||||
|  | ||||
|     use super::*; | ||||
|  | ||||
|     #[test] | ||||
|     fn one_level() { | ||||
|         let mut processing = ProcessingTasks::new(); | ||||
|         processing.start_processing(ProcessingBatch::new(0), RoaringBitmap::new()); | ||||
|         snapshot!(json_string!(processing.get_progress_view()), @r#" | ||||
|         { | ||||
|           "steps": [ | ||||
|             { | ||||
|               "currentStep": "processing tasks", | ||||
|               "finished": 0, | ||||
|               "total": 2 | ||||
|             } | ||||
|           ], | ||||
|           "percentage": 0.0 | ||||
|         } | ||||
|         "#); | ||||
|         processing.progress.as_ref().unwrap().update_progress(BatchProgress::WritingTasksToDisk); | ||||
|         snapshot!(json_string!(processing.get_progress_view()), @r#" | ||||
|         { | ||||
|           "steps": [ | ||||
|             { | ||||
|               "currentStep": "writing tasks to disk", | ||||
|               "finished": 1, | ||||
|               "total": 2 | ||||
|             } | ||||
|           ], | ||||
|           "percentage": 50.0 | ||||
|         } | ||||
|         "#); | ||||
|     } | ||||
|  | ||||
|     #[test] | ||||
|     fn task_progress() { | ||||
|         let mut processing = ProcessingTasks::new(); | ||||
|         processing.start_processing(ProcessingBatch::new(0), RoaringBitmap::new()); | ||||
|         let (atomic, tasks) = AtomicTaskStep::new(10); | ||||
|         processing.progress.as_ref().unwrap().update_progress(tasks); | ||||
|         snapshot!(json_string!(processing.get_progress_view()), @r#" | ||||
|         { | ||||
|           "steps": [ | ||||
|             { | ||||
|               "currentStep": "processing tasks", | ||||
|               "finished": 0, | ||||
|               "total": 2 | ||||
|             }, | ||||
|             { | ||||
|               "currentStep": "task", | ||||
|               "finished": 0, | ||||
|               "total": 10 | ||||
|             } | ||||
|           ], | ||||
|           "percentage": 0.0 | ||||
|         } | ||||
|         "#); | ||||
|         atomic.fetch_add(6, Ordering::Relaxed); | ||||
|         snapshot!(json_string!(processing.get_progress_view()), @r#" | ||||
|         { | ||||
|           "steps": [ | ||||
|             { | ||||
|               "currentStep": "processing tasks", | ||||
|               "finished": 0, | ||||
|               "total": 2 | ||||
|             }, | ||||
|             { | ||||
|               "currentStep": "task", | ||||
|               "finished": 6, | ||||
|               "total": 10 | ||||
|             } | ||||
|           ], | ||||
|           "percentage": 30.000002 | ||||
|         } | ||||
|         "#); | ||||
|         processing.progress.as_ref().unwrap().update_progress(BatchProgress::WritingTasksToDisk); | ||||
|         snapshot!(json_string!(processing.get_progress_view()), @r#" | ||||
|         { | ||||
|           "steps": [ | ||||
|             { | ||||
|               "currentStep": "writing tasks to disk", | ||||
|               "finished": 1, | ||||
|               "total": 2 | ||||
|             } | ||||
|           ], | ||||
|           "percentage": 50.0 | ||||
|         } | ||||
|         "#); | ||||
|         let (atomic, tasks) = AtomicTaskStep::new(5); | ||||
|         processing.progress.as_ref().unwrap().update_progress(tasks); | ||||
|         atomic.fetch_add(4, Ordering::Relaxed); | ||||
|         snapshot!(json_string!(processing.get_progress_view()), @r#" | ||||
|         { | ||||
|           "steps": [ | ||||
|             { | ||||
|               "currentStep": "writing tasks to disk", | ||||
|               "finished": 1, | ||||
|               "total": 2 | ||||
|             }, | ||||
|             { | ||||
|               "currentStep": "task", | ||||
|               "finished": 4, | ||||
|               "total": 5 | ||||
|             } | ||||
|           ], | ||||
|           "percentage": 90.0 | ||||
|         } | ||||
|         "#); | ||||
|     } | ||||
| } | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(1): | ||||
| [1,] | ||||
| {uid: 1, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"beavero":2}}, } | ||||
| {uid: 1, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"beavero":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(1): | ||||
| [1,] | ||||
| {uid: 1, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"beavero":2}}, } | ||||
| {uid: 1, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"beavero":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(0): | ||||
| [0,] | ||||
| {uid: 0, details: {"dumpUid":null}, stats: {"totalNbTasks":1,"status":{"enqueued":1},"types":{"dumpCreation":1},"indexUids":{}}, } | ||||
| {uid: 0, details: {"dumpUid":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"dumpCreation":1},"indexUids":{}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { dump_uid: None }, kind: DumpCreation { keys: [], instance_uid: None }} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(0): | ||||
| [0,] | ||||
| {uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"catto":2}}, } | ||||
| {uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"catto":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(0): | ||||
| [0,] | ||||
| {uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"catto":2}}, } | ||||
| {uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"catto":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(0): | ||||
| [0,] | ||||
| {uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"catto":2}}, } | ||||
| {uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"catto":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "catto", primary_key: None, method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(0): | ||||
| [0,] | ||||
| {uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"doggos":2}}, } | ||||
| {uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"doggos":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(0): | ||||
| [0,] | ||||
| {uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"doggos":2}}, } | ||||
| {uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"doggos":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| --- | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 3, indexed_documents: Some(3) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }} | ||||
| ---------------------------------------------------------------------- | ||||
| ### Status: | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| --- | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 1 {uid: 1, status: enqueued, details: { received_documents: 3, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }} | ||||
| ---------------------------------------------------------------------- | ||||
| ### Status: | ||||
|   | ||||
| @@ -1,17 +1,16 @@ | ||||
| --- | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 3, indexed_documents: Some(3) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }} | ||||
| 2 {uid: 2, batch_uid: 2, status: succeeded, details: { received_document_ids: 1, deleted_documents: Some(1) }, kind: DocumentDeletion { index_uid: "doggos", documents_ids: ["1"] }} | ||||
| 3 {uid: 3, batch_uid: 2, status: failed, error: ResponseError { code: 200, message: "Invalid type for filter subexpression: expected: String, Array, found: true.", error_code: "invalid_document_filter", error_type: "invalid_request", error_link: "https://docs.meilisearch.com/errors#invalid_document_filter" }, details: { original_filter: true, deleted_documents: Some(0) }, kind: DocumentDeletionByFilter { index_uid: "doggos", filter_expr: Bool(true) }} | ||||
| 4 {uid: 4, batch_uid: 2, status: failed, error: ResponseError { code: 200, message: "Attribute `id` is not filterable. Available filterable attributes are: `catto`.\n1:3 id = 2", error_code: "invalid_document_filter", error_type: "invalid_request", error_link: "https://docs.meilisearch.com/errors#invalid_document_filter" }, details: { original_filter: "id = 2", deleted_documents: Some(0) }, kind: DocumentDeletionByFilter { index_uid: "doggos", filter_expr: String("id = 2") }} | ||||
| 3 {uid: 3, batch_uid: 2, status: failed, error: ResponseError { code: 200, message: "Index `doggos`: Invalid type for filter subexpression: expected: String, Array, found: true.", error_code: "invalid_document_filter", error_type: "invalid_request", error_link: "https://docs.meilisearch.com/errors#invalid_document_filter" }, details: { original_filter: true, deleted_documents: Some(0) }, kind: DocumentDeletionByFilter { index_uid: "doggos", filter_expr: Bool(true) }} | ||||
| 4 {uid: 4, batch_uid: 2, status: failed, error: ResponseError { code: 200, message: "Index `doggos`: Attribute `id` is not filterable. Available filterable attributes are: `catto`.\n1:3 id = 2", error_code: "invalid_document_filter", error_type: "invalid_request", error_link: "https://docs.meilisearch.com/errors#invalid_document_filter" }, details: { original_filter: "id = 2", deleted_documents: Some(0) }, kind: DocumentDeletionByFilter { index_uid: "doggos", filter_expr: String("id = 2") }} | ||||
| 5 {uid: 5, batch_uid: 2, status: succeeded, details: { original_filter: "catto EXISTS", deleted_documents: Some(1) }, kind: DocumentDeletionByFilter { index_uid: "doggos", filter_expr: String("catto EXISTS") }} | ||||
| ---------------------------------------------------------------------- | ||||
| ### Status: | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| --- | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 3, indexed_documents: Some(3) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }} | ||||
| 2 {uid: 2, status: enqueued, details: { received_document_ids: 1, deleted_documents: None }, kind: DocumentDeletion { index_uid: "doggos", documents_ids: ["1"] }} | ||||
| 3 {uid: 3, status: enqueued, details: { original_filter: true, deleted_documents: None }, kind: DocumentDeletionByFilter { index_uid: "doggos", filter_expr: Bool(true) }} | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| --- | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, status: enqueued, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: Set({"catto"}), sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: NotSet, search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 1 {uid: 1, status: enqueued, details: { received_documents: 3, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 3, allow_index_creation: true }} | ||||
| ---------------------------------------------------------------------- | ||||
| ### Status: | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(0): | ||||
| [0,] | ||||
| {uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"doggos":2}}, } | ||||
| {uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"doggos":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(0): | ||||
| [0,] | ||||
| {uid: 0, details: {"receivedDocuments":2,"indexedDocuments":null}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"documentAdditionOrUpdate":2},"indexUids":{"doggos":2}}, } | ||||
| {uid: 0, details: {"receivedDocuments":1,"indexedDocuments":null}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"documentAdditionOrUpdate":1},"indexUids":{"doggos":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: ReplaceDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| --- | ||||
| source: crates/crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
| 2 {uid: 2, batch_uid: 2, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: None, method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000001, documents_count: 1, allow_index_creation: true }} | ||||
| ---------------------------------------------------------------------- | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| --- | ||||
| source: crates/crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
| 2 {uid: 2, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: None, method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000001, documents_count: 1, allow_index_creation: true }} | ||||
| ---------------------------------------------------------------------- | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| --- | ||||
| source: crates/crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 1 {uid: 1, batch_uid: 1, status: succeeded, details: { received_documents: 1, indexed_documents: Some(1) }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
| ---------------------------------------------------------------------- | ||||
| ### Status: | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| --- | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 1 {uid: 1, status: enqueued, details: { received_documents: 1, indexed_documents: None }, kind: DocumentAdditionOrUpdate { index_uid: "doggos", primary_key: Some("id"), method: UpdateDocuments, content_file: 00000000-0000-0000-0000-000000000000, documents_count: 1, allow_index_creation: true }} | ||||
| ---------------------------------------------------------------------- | ||||
| ### Status: | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| --- | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, status: enqueued, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| ---------------------------------------------------------------------- | ||||
| ### Status: | ||||
| enqueued [0,] | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| --- | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"A_fakerest": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(384), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet }), "B_small_hf": Set(EmbeddingSettings { source: Set(HuggingFace), model: Set("sentence-transformers/all-MiniLM-L6-v2"), revision: Set("e4ce9877abf3edfe10b0d82785e83bdcb973e22e"), api_key: NotSet, dimensions: NotSet, binary_quantized: NotSet, document_template: Set("{{doc.doggo}} the {{doc.breed}} best doggo"), document_template_max_bytes: NotSet, url: NotSet, request: NotSet, response: NotSet, headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| ---------------------------------------------------------------------- | ||||
| ### Status: | ||||
| enqueued [] | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(0): | ||||
| [0,] | ||||
| {uid: 0, details: {"primaryKey":"id"}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"indexCreation":2},"indexUids":{"index_a":2}}, } | ||||
| {uid: 0, details: {"primaryKey":"id"}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"indexCreation":1},"indexUids":{"index_a":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "index_a", primary_key: Some("id") }} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(0): | ||||
| [0,] | ||||
| {uid: 0, details: {"primaryKey":"id"}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"indexCreation":2},"indexUids":{"index_a":2}}, } | ||||
| {uid: 0, details: {"primaryKey":"id"}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"indexCreation":1},"indexUids":{"index_a":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "index_a", primary_key: Some("id") }} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(0): | ||||
| [0,] | ||||
| {uid: 0, details: {"primaryKey":"id"}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"indexCreation":2},"indexUids":{"index_a":2}}, } | ||||
| {uid: 0, details: {"primaryKey":"id"}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"indexCreation":1},"indexUids":{"index_a":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { primary_key: Some("id") }, kind: IndexCreation { index_uid: "index_a", primary_key: Some("id") }} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(1): | ||||
| [1,] | ||||
| {uid: 1, details: {"primaryKey":"sheep"}, stats: {"totalNbTasks":2,"status":{"enqueued":2},"types":{"indexCreation":2},"indexUids":{"doggo":2}}, } | ||||
| {uid: 1, details: {"primaryKey":"sheep"}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"indexCreation":1},"indexUids":{"doggo":1}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { primary_key: Some("mouse") }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }} | ||||
|   | ||||
| @@ -5,7 +5,7 @@ snapshot_kind: text | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch Some(0): | ||||
| [3,] | ||||
| {uid: 0, details: {"matchedTasks":2,"deletedTasks":null,"originalFilter":"test_query"}, stats: {"totalNbTasks":1,"status":{"enqueued":1},"types":{"taskDeletion":1},"indexUids":{}}, } | ||||
| {uid: 0, details: {"matchedTasks":2,"deletedTasks":null,"originalFilter":"test_query"}, stats: {"totalNbTasks":1,"status":{"processing":1},"types":{"taskDeletion":1},"indexUids":{}}, } | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { primary_key: Some("mouse") }, kind: IndexCreation { index_uid: "catto", primary_key: Some("mouse") }} | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| --- | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, status: enqueued, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, status: enqueued, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| ---------------------------------------------------------------------- | ||||
| ### Status: | ||||
| enqueued [0,] | ||||
|   | ||||
| @@ -1,13 +1,12 @@ | ||||
| --- | ||||
| source: crates/index-scheduler/src/lib.rs | ||||
| snapshot_kind: text | ||||
| --- | ||||
| ### Autobatching Enabled = true | ||||
| ### Processing batch None: | ||||
| [] | ||||
| ---------------------------------------------------------------------- | ||||
| ### All Tasks: | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| 0 {uid: 0, batch_uid: 0, status: succeeded, details: { settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> } }, kind: SettingsUpdate { index_uid: "doggos", new_settings: Settings { displayed_attributes: WildcardSetting(NotSet), searchable_attributes: WildcardSetting(NotSet), filterable_attributes: NotSet, sortable_attributes: NotSet, ranking_rules: NotSet, stop_words: NotSet, non_separator_tokens: NotSet, separator_tokens: NotSet, dictionary: NotSet, synonyms: NotSet, distinct_attribute: NotSet, proximity_precision: NotSet, typo_tolerance: NotSet, faceting: NotSet, pagination: NotSet, embedders: Set({"default": Set(EmbeddingSettings { source: Set(Rest), model: NotSet, revision: NotSet, api_key: Set("My super secret"), dimensions: Set(4), binary_quantized: NotSet, document_template: NotSet, document_template_max_bytes: NotSet, url: Set("http://localhost:7777"), request: Set(String("{{text}}")), response: Set(String("{{embedding}}")), headers: NotSet, distribution: NotSet })}), search_cutoff_ms: NotSet, localized_attributes: NotSet, facet_search: NotSet, prefix_search: NotSet, _kind: PhantomData<meilisearch_types::settings::Unchecked> }, is_deletion: false, allow_index_creation: true }} | ||||
| ---------------------------------------------------------------------- | ||||
| ### Status: | ||||
| enqueued [] | ||||
|   | ||||
| @@ -67,7 +67,7 @@ impl ProcessingBatch { | ||||
|             task.batch_uid = Some(self.uid); | ||||
|             // We don't store the statuses in the map since they're all enqueued but we must | ||||
|             // still store them in the stats since that can be displayed. | ||||
|             *self.stats.status.entry(task.status).or_default() += 1; | ||||
|             *self.stats.status.entry(Status::Processing).or_default() += 1; | ||||
|  | ||||
|             self.kinds.insert(task.kind.as_kind()); | ||||
|             *self.stats.types.entry(task.kind.as_kind()).or_default() += 1; | ||||
| @@ -106,7 +106,7 @@ impl ProcessingBatch { | ||||
|         self.stats.total_nb_tasks = 0; | ||||
|     } | ||||
|  | ||||
|     /// Update the timestamp of the tasks and the inner structure of this sturcture. | ||||
|     /// Update the timestamp of the tasks and the inner structure of this structure. | ||||
|     pub fn update(&mut self, task: &mut Task) { | ||||
|         // We must re-set this value in case we're dealing with a task that has been added between | ||||
|         // the `processing` and `finished` state | ||||
| @@ -134,6 +134,7 @@ impl ProcessingBatch { | ||||
|     pub fn to_batch(&self) -> Batch { | ||||
|         Batch { | ||||
|             uid: self.uid, | ||||
|             progress: None, | ||||
|             details: self.details.clone(), | ||||
|             stats: self.stats.clone(), | ||||
|             started_at: self.started_at, | ||||
| @@ -187,6 +188,7 @@ impl IndexScheduler { | ||||
|             &batch.uid, | ||||
|             &Batch { | ||||
|                 uid: batch.uid, | ||||
|                 progress: None, | ||||
|                 details: batch.details, | ||||
|                 stats: batch.stats, | ||||
|                 started_at: batch.started_at, | ||||
| @@ -273,7 +275,9 @@ impl IndexScheduler { | ||||
|             .into_iter() | ||||
|             .map(|batch_id| { | ||||
|                 if Some(batch_id) == processing.batch.as_ref().map(|batch| batch.uid) { | ||||
|                     Ok(processing.batch.as_ref().unwrap().to_batch()) | ||||
|                     let mut batch = processing.batch.as_ref().unwrap().to_batch(); | ||||
|                     batch.progress = processing.get_progress_view(); | ||||
|                     Ok(batch) | ||||
|                 } else { | ||||
|                     self.get_batch(rtxn, batch_id) | ||||
|                         .and_then(|task| task.ok_or(Error::CorruptedTaskQueue)) | ||||
|   | ||||
| @@ -17,7 +17,7 @@ hmac = "0.12.1" | ||||
| maplit = "1.0.2" | ||||
| meilisearch-types = { path = "../meilisearch-types" } | ||||
| rand = "0.8.5" | ||||
| roaring = { version = "0.10.6", features = ["serde"] } | ||||
| roaring = { version = "0.10.7", features = ["serde"] } | ||||
| serde = { version = "1.0.204", features = ["derive"] } | ||||
| serde_json = { version = "1.0.120", features = ["preserve_order"] } | ||||
| sha2 = "0.10.8" | ||||
|   | ||||
| @@ -24,8 +24,9 @@ flate2 = "1.0.30" | ||||
| fst = "0.4.7" | ||||
| memmap2 = "0.9.4" | ||||
| milli = { path = "../milli" } | ||||
| raw-collections = { git = "https://github.com/meilisearch/raw-collections.git", version = "0.1.0" } | ||||
| roaring = { version = "0.10.6", features = ["serde"] } | ||||
| bumparaw-collections = "0.1.2" | ||||
| roaring = { version = "0.10.7", features = ["serde"] } | ||||
| rustc-hash = "2.1.0" | ||||
| serde = { version = "1.0.204", features = ["derive"] } | ||||
| serde-cs = "0.2.4" | ||||
| serde_json = "1.0.120" | ||||
|   | ||||
| @@ -1,16 +1,16 @@ | ||||
| use milli::progress::ProgressView; | ||||
| use serde::Serialize; | ||||
| use time::{Duration, OffsetDateTime}; | ||||
|  | ||||
| use crate::{ | ||||
|     batches::{Batch, BatchId, BatchStats}, | ||||
|     task_view::DetailsView, | ||||
|     tasks::serialize_duration, | ||||
| }; | ||||
| use crate::batches::{Batch, BatchId, BatchStats}; | ||||
| use crate::task_view::DetailsView; | ||||
| use crate::tasks::serialize_duration; | ||||
|  | ||||
| #[derive(Debug, Clone, Serialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct BatchView { | ||||
|     pub uid: BatchId, | ||||
|     pub progress: Option<ProgressView>, | ||||
|     pub details: DetailsView, | ||||
|     pub stats: BatchStats, | ||||
|     #[serde(serialize_with = "serialize_duration", default)] | ||||
| @@ -25,6 +25,7 @@ impl BatchView { | ||||
|     pub fn from_batch(batch: &Batch) -> Self { | ||||
|         Self { | ||||
|             uid: batch.uid, | ||||
|             progress: batch.progress.clone(), | ||||
|             details: batch.details.clone(), | ||||
|             stats: batch.stats.clone(), | ||||
|             duration: batch.finished_at.map(|finished_at| finished_at - batch.started_at), | ||||
|   | ||||
| @@ -1,12 +1,11 @@ | ||||
| use std::collections::BTreeMap; | ||||
|  | ||||
| use milli::progress::ProgressView; | ||||
| use serde::{Deserialize, Serialize}; | ||||
| use time::OffsetDateTime; | ||||
|  | ||||
| use crate::{ | ||||
|     task_view::DetailsView, | ||||
|     tasks::{Kind, Status}, | ||||
| }; | ||||
| use crate::task_view::DetailsView; | ||||
| use crate::tasks::{Kind, Status}; | ||||
|  | ||||
| pub type BatchId = u32; | ||||
|  | ||||
| @@ -15,6 +14,8 @@ pub type BatchId = u32; | ||||
| pub struct Batch { | ||||
|     pub uid: BatchId, | ||||
|  | ||||
|     #[serde(skip)] | ||||
|     pub progress: Option<ProgressView>, | ||||
|     pub details: DetailsView, | ||||
|     pub stats: BatchStats, | ||||
|  | ||||
|   | ||||
| @@ -4,10 +4,11 @@ use std::io::{self, BufWriter}; | ||||
| use std::marker::PhantomData; | ||||
|  | ||||
| use bumpalo::Bump; | ||||
| use bumparaw_collections::RawMap; | ||||
| use memmap2::Mmap; | ||||
| use milli::documents::Error; | ||||
| use milli::Object; | ||||
| use raw_collections::RawMap; | ||||
| use rustc_hash::FxBuildHasher; | ||||
| use serde::de::{SeqAccess, Visitor}; | ||||
| use serde::{Deserialize, Deserializer}; | ||||
| use serde_json::error::Category; | ||||
| @@ -214,13 +215,13 @@ pub fn read_json(input: &File, output: impl io::Write) -> Result<u64> { | ||||
|     // We memory map to be able to deserialize into a RawMap that | ||||
|     // does not allocate when possible and only materialize the first/top level. | ||||
|     let input = unsafe { Mmap::map(input).map_err(DocumentFormatError::Io)? }; | ||||
|     let mut doc_alloc = Bump::with_capacity(1024 * 1024 * 1024); // 1MiB | ||||
|     let mut doc_alloc = Bump::with_capacity(1024 * 1024); // 1MiB | ||||
|  | ||||
|     let mut out = BufWriter::new(output); | ||||
|     let mut deserializer = serde_json::Deserializer::from_slice(&input); | ||||
|     let res = array_each(&mut deserializer, |obj: &RawValue| { | ||||
|         doc_alloc.reset(); | ||||
|         let map = RawMap::from_raw_value(obj, &doc_alloc)?; | ||||
|         let map = RawMap::from_raw_value_and_hasher(obj, FxBuildHasher, &doc_alloc)?; | ||||
|         to_writer(&mut out, &map) | ||||
|     }); | ||||
|     let count = match res { | ||||
| @@ -250,26 +251,25 @@ pub fn read_json(input: &File, output: impl io::Write) -> Result<u64> { | ||||
|     } | ||||
| } | ||||
|  | ||||
| /// Reads NDJSON from file and write it in NDJSON in a file checking it along the way. | ||||
| pub fn read_ndjson(input: &File, output: impl io::Write) -> Result<u64> { | ||||
| /// Reads NDJSON from file and checks it. | ||||
| pub fn read_ndjson(input: &File) -> Result<u64> { | ||||
|     // We memory map to be able to deserialize into a RawMap that | ||||
|     // does not allocate when possible and only materialize the first/top level. | ||||
|     let input = unsafe { Mmap::map(input).map_err(DocumentFormatError::Io)? }; | ||||
|     let mut output = BufWriter::new(output); | ||||
|  | ||||
|     let mut bump = Bump::with_capacity(1024 * 1024); | ||||
|  | ||||
|     let mut count = 0; | ||||
|     for result in serde_json::Deserializer::from_slice(&input).into_iter() { | ||||
|         bump.reset(); | ||||
|         count += 1; | ||||
|         result | ||||
|             .and_then(|raw: &RawValue| { | ||||
|         match result { | ||||
|             Ok(raw) => { | ||||
|                 // try to deserialize as a map | ||||
|                 let map = RawMap::from_raw_value(raw, &bump)?; | ||||
|                 to_writer(&mut output, &map) | ||||
|             }) | ||||
|             .map_err(|e| DocumentFormatError::from((PayloadType::Ndjson, e)))?; | ||||
|                 RawMap::from_raw_value_and_hasher(raw, FxBuildHasher, &bump) | ||||
|                     .map_err(|e| DocumentFormatError::from((PayloadType::Ndjson, e)))?; | ||||
|                 count += 1; | ||||
|             } | ||||
|             Err(e) => return Err(DocumentFormatError::from((PayloadType::Ndjson, e))), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Ok(count) | ||||
|   | ||||
| @@ -279,6 +279,7 @@ InvalidSearchPage                     , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidSearchQ                        , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidFacetSearchQuery               , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidFacetSearchName                , InvalidRequest       , BAD_REQUEST ; | ||||
| FacetSearchDisabled                   , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidSearchVector                   , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidSearchShowMatchesPosition      , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidSearchShowRankingScore         , InvalidRequest       , BAD_REQUEST ; | ||||
| @@ -290,6 +291,8 @@ InvalidSearchDistinct                 , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidSettingsDisplayedAttributes    , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidSettingsDistinctAttribute      , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidSettingsProximityPrecision     , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidSettingsFacetSearch            , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidSettingsPrefixSearch           , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidSettingsFaceting               , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidSettingsFilterableAttributes   , InvalidRequest       , BAD_REQUEST ; | ||||
| InvalidSettingsPagination             , InvalidRequest       , BAD_REQUEST ; | ||||
| @@ -547,7 +550,7 @@ impl fmt::Display for deserr_codes::InvalidSimilarId { | ||||
|             "the value of `id` is invalid. \ | ||||
|             A document identifier can be of type integer or string, \ | ||||
|             only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_), \ | ||||
|             and can not be more than 512 bytes." | ||||
|             and can not be more than 511 bytes." | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ use std::str::FromStr; | ||||
|  | ||||
| use deserr::{DeserializeError, Deserr, ErrorKind, MergeWithError, ValuePointerRef}; | ||||
| use fst::IntoStreamer; | ||||
| use milli::index::IndexEmbeddingConfig; | ||||
| use milli::index::{IndexEmbeddingConfig, PrefixSearch}; | ||||
| use milli::proximity::ProximityPrecision; | ||||
| use milli::update::Setting; | ||||
| use milli::{Criterion, CriterionError, Index, DEFAULT_VALUES_PER_FACET}; | ||||
| @@ -202,6 +202,12 @@ pub struct Settings<T> { | ||||
|     #[serde(default, skip_serializing_if = "Setting::is_not_set")] | ||||
|     #[deserr(default, error = DeserrJsonError<InvalidSettingsLocalizedAttributes>)] | ||||
|     pub localized_attributes: Setting<Vec<LocalizedAttributesRuleView>>, | ||||
|     #[serde(default, skip_serializing_if = "Setting::is_not_set")] | ||||
|     #[deserr(default, error = DeserrJsonError<InvalidSettingsFacetSearch>)] | ||||
|     pub facet_search: Setting<bool>, | ||||
|     #[serde(default, skip_serializing_if = "Setting::is_not_set")] | ||||
|     #[deserr(default, error = DeserrJsonError<InvalidSettingsPrefixSearch>)] | ||||
|     pub prefix_search: Setting<PrefixSearchSettings>, | ||||
|  | ||||
|     #[serde(skip)] | ||||
|     #[deserr(skip)] | ||||
| @@ -266,6 +272,8 @@ impl Settings<Checked> { | ||||
|             embedders: Setting::Reset, | ||||
|             search_cutoff_ms: Setting::Reset, | ||||
|             localized_attributes: Setting::Reset, | ||||
|             facet_search: Setting::Reset, | ||||
|             prefix_search: Setting::Reset, | ||||
|             _kind: PhantomData, | ||||
|         } | ||||
|     } | ||||
| @@ -290,6 +298,8 @@ impl Settings<Checked> { | ||||
|             embedders, | ||||
|             search_cutoff_ms, | ||||
|             localized_attributes: localized_attributes_rules, | ||||
|             facet_search, | ||||
|             prefix_search, | ||||
|             _kind, | ||||
|         } = self; | ||||
|  | ||||
| @@ -312,6 +322,8 @@ impl Settings<Checked> { | ||||
|             embedders, | ||||
|             search_cutoff_ms, | ||||
|             localized_attributes: localized_attributes_rules, | ||||
|             facet_search, | ||||
|             prefix_search, | ||||
|             _kind: PhantomData, | ||||
|         } | ||||
|     } | ||||
| @@ -360,6 +372,8 @@ impl Settings<Unchecked> { | ||||
|             embedders: self.embedders, | ||||
|             search_cutoff_ms: self.search_cutoff_ms, | ||||
|             localized_attributes: self.localized_attributes, | ||||
|             facet_search: self.facet_search, | ||||
|             prefix_search: self.prefix_search, | ||||
|             _kind: PhantomData, | ||||
|         } | ||||
|     } | ||||
| @@ -433,6 +447,8 @@ impl Settings<Unchecked> { | ||||
|                     Setting::Set(this) | ||||
|                 } | ||||
|             }, | ||||
|             prefix_search: other.prefix_search.or(self.prefix_search), | ||||
|             facet_search: other.facet_search.or(self.facet_search), | ||||
|             _kind: PhantomData, | ||||
|         } | ||||
|     } | ||||
| @@ -469,6 +485,8 @@ pub fn apply_settings_to_builder( | ||||
|         embedders, | ||||
|         search_cutoff_ms, | ||||
|         localized_attributes: localized_attributes_rules, | ||||
|         facet_search, | ||||
|         prefix_search, | ||||
|         _kind, | ||||
|     } = settings; | ||||
|  | ||||
| @@ -657,6 +675,20 @@ pub fn apply_settings_to_builder( | ||||
|         Setting::Reset => builder.reset_search_cutoff(), | ||||
|         Setting::NotSet => (), | ||||
|     } | ||||
|  | ||||
|     match prefix_search { | ||||
|         Setting::Set(prefix_search) => { | ||||
|             builder.set_prefix_search(PrefixSearch::from(*prefix_search)) | ||||
|         } | ||||
|         Setting::Reset => builder.reset_prefix_search(), | ||||
|         Setting::NotSet => (), | ||||
|     } | ||||
|  | ||||
|     match facet_search { | ||||
|         Setting::Set(facet_search) => builder.set_facet_search(*facet_search), | ||||
|         Setting::Reset => builder.reset_facet_search(), | ||||
|         Setting::NotSet => (), | ||||
|     } | ||||
| } | ||||
|  | ||||
| pub enum SecretPolicy { | ||||
| @@ -755,6 +787,10 @@ pub fn settings( | ||||
|  | ||||
|     let localized_attributes_rules = index.localized_attributes_rules(rtxn)?; | ||||
|  | ||||
|     let prefix_search = index.prefix_search(rtxn)?.map(PrefixSearchSettings::from); | ||||
|  | ||||
|     let facet_search = index.facet_search(rtxn)?; | ||||
|  | ||||
|     let mut settings = Settings { | ||||
|         displayed_attributes: match displayed_attributes { | ||||
|             Some(attrs) => Setting::Set(attrs), | ||||
| @@ -791,13 +827,14 @@ pub fn settings( | ||||
|             Some(rules) => Setting::Set(rules.into_iter().map(|r| r.into()).collect()), | ||||
|             None => Setting::Reset, | ||||
|         }, | ||||
|         prefix_search: Setting::Set(prefix_search.unwrap_or_default()), | ||||
|         facet_search: Setting::Set(facet_search), | ||||
|         _kind: PhantomData, | ||||
|     }; | ||||
|  | ||||
|     if let SecretPolicy::HideSecrets = secret_policy { | ||||
|         settings.hide_secrets() | ||||
|     } | ||||
|  | ||||
|     Ok(settings) | ||||
| } | ||||
|  | ||||
| @@ -964,6 +1001,32 @@ impl std::ops::Deref for WildcardSetting { | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Default, Debug, Clone, Copy, PartialEq, Eq, Deserr, Serialize, Deserialize)] | ||||
| #[serde(deny_unknown_fields, rename_all = "camelCase")] | ||||
| #[deserr(error = DeserrJsonError<InvalidSettingsPrefixSearch>, rename_all = camelCase, deny_unknown_fields)] | ||||
| pub enum PrefixSearchSettings { | ||||
|     #[default] | ||||
|     IndexingTime, | ||||
|     Disabled, | ||||
| } | ||||
|  | ||||
| impl From<PrefixSearch> for PrefixSearchSettings { | ||||
|     fn from(value: PrefixSearch) -> Self { | ||||
|         match value { | ||||
|             PrefixSearch::IndexingTime => PrefixSearchSettings::IndexingTime, | ||||
|             PrefixSearch::Disabled => PrefixSearchSettings::Disabled, | ||||
|         } | ||||
|     } | ||||
| } | ||||
| impl From<PrefixSearchSettings> for PrefixSearch { | ||||
|     fn from(value: PrefixSearchSettings) -> Self { | ||||
|         match value { | ||||
|             PrefixSearchSettings::IndexingTime => PrefixSearch::IndexingTime, | ||||
|             PrefixSearchSettings::Disabled => PrefixSearch::Disabled, | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[cfg(test)] | ||||
| pub(crate) mod test { | ||||
|     use super::*; | ||||
| @@ -990,6 +1053,8 @@ pub(crate) mod test { | ||||
|             embedders: Setting::NotSet, | ||||
|             localized_attributes: Setting::NotSet, | ||||
|             search_cutoff_ms: Setting::NotSet, | ||||
|             facet_search: Setting::NotSet, | ||||
|             prefix_search: Setting::NotSet, | ||||
|             _kind: PhantomData::<Unchecked>, | ||||
|         }; | ||||
|  | ||||
| @@ -1019,6 +1084,8 @@ pub(crate) mod test { | ||||
|             embedders: Setting::NotSet, | ||||
|             localized_attributes: Setting::NotSet, | ||||
|             search_cutoff_ms: Setting::NotSet, | ||||
|             facet_search: Setting::NotSet, | ||||
|             prefix_search: Setting::NotSet, | ||||
|             _kind: PhantomData::<Unchecked>, | ||||
|         }; | ||||
|  | ||||
|   | ||||
| @@ -4,7 +4,6 @@ use std::fmt::{Display, Write}; | ||||
| use std::str::FromStr; | ||||
|  | ||||
| use enum_iterator::Sequence; | ||||
| use milli::update::new::indexer::document_changes::Progress; | ||||
| use milli::update::IndexDocumentsMethod; | ||||
| use milli::Object; | ||||
| use roaring::RoaringBitmap; | ||||
| @@ -41,62 +40,6 @@ pub struct Task { | ||||
|     pub kind: KindWithContent, | ||||
| } | ||||
|  | ||||
| #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] | ||||
| #[serde(rename_all = "camelCase")] | ||||
| pub struct TaskProgress { | ||||
|     pub current_step: &'static str, | ||||
|     pub finished_steps: u16, | ||||
|     pub total_steps: u16, | ||||
|     pub finished_substeps: Option<u32>, | ||||
|     pub total_substeps: Option<u32>, | ||||
| } | ||||
|  | ||||
| impl Default for TaskProgress { | ||||
|     fn default() -> Self { | ||||
|         Self::new() | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl TaskProgress { | ||||
|     pub fn new() -> Self { | ||||
|         Self { | ||||
|             current_step: "start", | ||||
|             finished_steps: 0, | ||||
|             total_steps: 1, | ||||
|             finished_substeps: None, | ||||
|             total_substeps: None, | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     pub fn update(&mut self, progress: Progress) -> TaskProgress { | ||||
|         if self.finished_steps > progress.finished_steps { | ||||
|             return *self; | ||||
|         } | ||||
|  | ||||
|         if self.current_step != progress.step_name { | ||||
|             self.current_step = progress.step_name | ||||
|         } | ||||
|  | ||||
|         self.total_steps = progress.total_steps; | ||||
|  | ||||
|         if self.finished_steps < progress.finished_steps { | ||||
|             self.finished_substeps = None; | ||||
|             self.total_substeps = None; | ||||
|         } | ||||
|         self.finished_steps = progress.finished_steps; | ||||
|         if let Some((finished_substeps, total_substeps)) = progress.finished_total_substep { | ||||
|             if let Some(task_finished_substeps) = self.finished_substeps { | ||||
|                 if task_finished_substeps > finished_substeps { | ||||
|                     return *self; | ||||
|                 } | ||||
|             } | ||||
|             self.finished_substeps = Some(finished_substeps); | ||||
|             self.total_substeps = Some(total_substeps); | ||||
|         } | ||||
|         *self | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl Task { | ||||
|     pub fn index_uid(&self) -> Option<&str> { | ||||
|         use KindWithContent::*; | ||||
|   | ||||
| @@ -57,7 +57,7 @@ meilisearch-types = { path = "../meilisearch-types" } | ||||
| mimalloc = { version = "0.1.43", default-features = false } | ||||
| mime = "0.3.17" | ||||
| num_cpus = "1.16.0" | ||||
| obkv = { git = "https://github.com/kerollmops/obkv", branch = "unsized-kvreader" } | ||||
| obkv = "0.3.0" | ||||
| once_cell = "1.19.0" | ||||
| ordered-float = "4.2.1" | ||||
| parking_lot = "0.12.3" | ||||
| @@ -103,7 +103,7 @@ tracing-subscriber = { version = "0.3.18", features = ["json"] } | ||||
| tracing-trace = { version = "0.1.0", path = "../tracing-trace" } | ||||
| tracing-actix-web = "0.7.11" | ||||
| build-info = { version = "1.7.0", path = "../build-info" } | ||||
| roaring = "0.10.2" | ||||
| roaring = "0.10.7" | ||||
| mopa-maintained = "0.2.3" | ||||
|  | ||||
| [dev-dependencies] | ||||
| @@ -157,5 +157,5 @@ german = ["meilisearch-types/german"] | ||||
| turkish = ["meilisearch-types/turkish"] | ||||
|  | ||||
| [package.metadata.mini-dashboard] | ||||
| assets-url = "https://github.com/meilisearch/mini-dashboard/releases/download/v0.2.15/build.zip" | ||||
| sha1 = "d057600b4a839a2e0c0be7a372cd1b2683f3ca7e" | ||||
| assets-url = "https://github.com/meilisearch/mini-dashboard/releases/download/v0.2.16/build.zip" | ||||
| sha1 = "68f83438a114aabbe76bc9fe480071e741996662" | ||||
|   | ||||
| @@ -4,6 +4,7 @@ use byte_unit::{Byte, UnitType}; | ||||
| use meilisearch_types::document_formats::{DocumentFormatError, PayloadType}; | ||||
| use meilisearch_types::error::{Code, ErrorCode, ResponseError}; | ||||
| use meilisearch_types::index_uid::{IndexUid, IndexUidFormatError}; | ||||
| use meilisearch_types::milli; | ||||
| use meilisearch_types::milli::OrderBy; | ||||
| use serde_json::Value; | ||||
| use tokio::task::JoinError; | ||||
| @@ -62,8 +63,11 @@ pub enum MeilisearchHttpError { | ||||
|     HeedError(#[from] meilisearch_types::heed::Error), | ||||
|     #[error(transparent)] | ||||
|     IndexScheduler(#[from] index_scheduler::Error), | ||||
|     #[error(transparent)] | ||||
|     Milli(#[from] meilisearch_types::milli::Error), | ||||
|     #[error("{}", match .index_name { | ||||
|         Some(name) if !name.is_empty() => format!("Index `{}`: {error}", name), | ||||
|         _ => format!("{error}") | ||||
|     })] | ||||
|     Milli { error: milli::Error, index_name: Option<String> }, | ||||
|     #[error(transparent)] | ||||
|     Payload(#[from] PayloadError), | ||||
|     #[error(transparent)] | ||||
| @@ -76,6 +80,12 @@ pub enum MeilisearchHttpError { | ||||
|     MissingSearchHybrid, | ||||
| } | ||||
|  | ||||
| impl MeilisearchHttpError { | ||||
|     pub(crate) fn from_milli(error: milli::Error, index_name: Option<String>) -> Self { | ||||
|         Self::Milli { error, index_name } | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl ErrorCode for MeilisearchHttpError { | ||||
|     fn error_code(&self) -> Code { | ||||
|         match self { | ||||
| @@ -95,7 +105,7 @@ impl ErrorCode for MeilisearchHttpError { | ||||
|             MeilisearchHttpError::SerdeJson(_) => Code::Internal, | ||||
|             MeilisearchHttpError::HeedError(_) => Code::Internal, | ||||
|             MeilisearchHttpError::IndexScheduler(e) => e.error_code(), | ||||
|             MeilisearchHttpError::Milli(e) => e.error_code(), | ||||
|             MeilisearchHttpError::Milli { error, .. } => error.error_code(), | ||||
|             MeilisearchHttpError::Payload(e) => e.error_code(), | ||||
|             MeilisearchHttpError::FileStore(_) => Code::Internal, | ||||
|             MeilisearchHttpError::DocumentFormat(e) => e.error_code(), | ||||
|   | ||||
| @@ -397,6 +397,7 @@ fn import_dump( | ||||
|     for index_reader in dump_reader.indexes()? { | ||||
|         let mut index_reader = index_reader?; | ||||
|         let metadata = index_reader.metadata(); | ||||
|         let uid = metadata.uid.clone(); | ||||
|         tracing::info!("Importing index `{}`.", metadata.uid); | ||||
|  | ||||
|         let date = Some((metadata.created_at, metadata.updated_at)); | ||||
| @@ -434,7 +435,7 @@ fn import_dump( | ||||
|         let reader = DocumentsBatchReader::from_reader(reader)?; | ||||
|  | ||||
|         let embedder_configs = index.embedding_configs(&wtxn)?; | ||||
|         let embedders = index_scheduler.embedders(embedder_configs)?; | ||||
|         let embedders = index_scheduler.embedders(uid, embedder_configs)?; | ||||
|  | ||||
|         let builder = milli::update::IndexDocuments::new( | ||||
|             &mut wtxn, | ||||
|   | ||||
| @@ -20,14 +20,14 @@ use meilisearch::{ | ||||
|     LogStderrType, Opt, SubscriberForSecondLayer, | ||||
| }; | ||||
| use meilisearch_auth::{generate_master_key, AuthController, MASTER_KEY_MIN_SIZE}; | ||||
| use mimalloc::MiMalloc; | ||||
| use termcolor::{Color, ColorChoice, ColorSpec, StandardStream, WriteColor}; | ||||
| use tracing::level_filters::LevelFilter; | ||||
| use tracing_subscriber::layer::SubscriberExt as _; | ||||
| use tracing_subscriber::Layer; | ||||
|  | ||||
| #[cfg(not(windows))] | ||||
| #[global_allocator] | ||||
| static ALLOC: MiMalloc = MiMalloc; | ||||
| static ALLOC: mimalloc::MiMalloc = mimalloc::MiMalloc; | ||||
|  | ||||
| fn default_log_route_layer() -> LogRouteType { | ||||
|     None.with_filter(tracing_subscriber::filter::Targets::new().with_target("", LevelFilter::OFF)) | ||||
| @@ -129,6 +129,11 @@ async fn try_main() -> anyhow::Result<()> { | ||||
|  | ||||
|     print_launch_resume(&opt, analytics.clone(), config_read_from); | ||||
|  | ||||
|     tokio::spawn(async move { | ||||
|         tokio::signal::ctrl_c().await.unwrap(); | ||||
|         std::process::exit(130); | ||||
|     }); | ||||
|  | ||||
|     run_http(index_scheduler, auth_controller, opt, log_handle, Arc::new(analytics)).await?; | ||||
|  | ||||
|     Ok(()) | ||||
|   | ||||
| @@ -1,18 +1,18 @@ | ||||
| use actix_web::{ | ||||
|     web::{self, Data}, | ||||
|     HttpResponse, | ||||
| }; | ||||
| use actix_web::web::{self, Data}; | ||||
| use actix_web::HttpResponse; | ||||
| use deserr::actix_web::AwebQueryParameter; | ||||
| use index_scheduler::{IndexScheduler, Query}; | ||||
| use meilisearch_types::{ | ||||
|     batch_view::BatchView, batches::BatchId, deserr::DeserrQueryParamError, error::ResponseError, | ||||
|     keys::actions, | ||||
| }; | ||||
| use meilisearch_types::batch_view::BatchView; | ||||
| use meilisearch_types::batches::BatchId; | ||||
| use meilisearch_types::deserr::DeserrQueryParamError; | ||||
| use meilisearch_types::error::ResponseError; | ||||
| use meilisearch_types::keys::actions; | ||||
| use serde::Serialize; | ||||
|  | ||||
| use crate::extractors::{authentication::GuardedData, sequential_extractor::SeqHandler}; | ||||
|  | ||||
| use super::{tasks::TasksFilterQuery, ActionPolicy}; | ||||
| use super::tasks::TasksFilterQuery; | ||||
| use super::ActionPolicy; | ||||
| use crate::extractors::authentication::GuardedData; | ||||
| use crate::extractors::sequential_extractor::SeqHandler; | ||||
|  | ||||
| pub fn configure(cfg: &mut web::ServiceConfig) { | ||||
|     cfg.service(web::resource("").route(web::get().to(SeqHandler(get_batches)))) | ||||
|   | ||||
| @@ -1,5 +1,5 @@ | ||||
| use std::collections::HashSet; | ||||
| use std::io::ErrorKind; | ||||
| use std::io::{ErrorKind, Seek as _}; | ||||
| use std::marker::PhantomData; | ||||
|  | ||||
| use actix_web::http::header::CONTENT_TYPE; | ||||
| @@ -572,7 +572,7 @@ async fn document_addition( | ||||
|     index_uid: IndexUid, | ||||
|     primary_key: Option<String>, | ||||
|     csv_delimiter: Option<u8>, | ||||
|     mut body: Payload, | ||||
|     body: Payload, | ||||
|     method: IndexDocumentsMethod, | ||||
|     task_id: Option<TaskId>, | ||||
|     dry_run: bool, | ||||
| @@ -609,54 +609,60 @@ async fn document_addition( | ||||
|     }; | ||||
|  | ||||
|     let (uuid, mut update_file) = index_scheduler.create_update_file(dry_run)?; | ||||
|     let documents_count = match format { | ||||
|         PayloadType::Ndjson => { | ||||
|             let (path, file) = update_file.into_parts(); | ||||
|             let file = match file { | ||||
|                 Some(file) => { | ||||
|                     let (file, path) = file.into_parts(); | ||||
|                     let mut file = copy_body_to_file(file, body, format).await?; | ||||
|                     file.rewind().map_err(|e| { | ||||
|                         index_scheduler::Error::FileStore(file_store::Error::IoError(e)) | ||||
|                     })?; | ||||
|                     Some(tempfile::NamedTempFile::from_parts(file, path)) | ||||
|                 } | ||||
|                 None => None, | ||||
|             }; | ||||
|  | ||||
|     let temp_file = match tempfile() { | ||||
|         Ok(file) => file, | ||||
|         Err(e) => return Err(MeilisearchHttpError::Payload(ReceivePayload(Box::new(e)))), | ||||
|             let documents_count = tokio::task::spawn_blocking(move || { | ||||
|                 let documents_count = file.as_ref().map_or(Ok(0), |ntf| { | ||||
|                     read_ndjson(ntf.as_file()).map_err(MeilisearchHttpError::DocumentFormat) | ||||
|                 })?; | ||||
|  | ||||
|                 let update_file = file_store::File::from_parts(path, file); | ||||
|                 update_file.persist()?; | ||||
|  | ||||
|                 Ok(documents_count) | ||||
|             }) | ||||
|             .await?; | ||||
|  | ||||
|             Ok(documents_count) | ||||
|         } | ||||
|         PayloadType::Json | PayloadType::Csv { delimiter: _ } => { | ||||
|             let temp_file = match tempfile() { | ||||
|                 Ok(file) => file, | ||||
|                 Err(e) => return Err(MeilisearchHttpError::Payload(ReceivePayload(Box::new(e)))), | ||||
|             }; | ||||
|  | ||||
|             let read_file = copy_body_to_file(temp_file, body, format).await?; | ||||
|             tokio::task::spawn_blocking(move || { | ||||
|                 let documents_count = match format { | ||||
|                     PayloadType::Json => read_json(&read_file, &mut update_file)?, | ||||
|                     PayloadType::Csv { delimiter } => { | ||||
|                         read_csv(&read_file, &mut update_file, delimiter)? | ||||
|                     } | ||||
|                     PayloadType::Ndjson => { | ||||
|                         unreachable!("We already wrote the user content into the update file") | ||||
|                     } | ||||
|                 }; | ||||
|                 // we NEED to persist the file here because we moved the `udpate_file` in another task. | ||||
|                 update_file.persist()?; | ||||
|                 Ok(documents_count) | ||||
|             }) | ||||
|             .await | ||||
|         } | ||||
|     }; | ||||
|  | ||||
|     let async_file = File::from_std(temp_file); | ||||
|     let mut buffer = BufWriter::new(async_file); | ||||
|  | ||||
|     let mut buffer_write_size: usize = 0; | ||||
|     while let Some(result) = body.next().await { | ||||
|         let byte = result?; | ||||
|  | ||||
|         if byte.is_empty() && buffer_write_size == 0 { | ||||
|             return Err(MeilisearchHttpError::MissingPayload(format)); | ||||
|         } | ||||
|  | ||||
|         match buffer.write_all(&byte).await { | ||||
|             Ok(()) => buffer_write_size += 1, | ||||
|             Err(e) => return Err(MeilisearchHttpError::Payload(ReceivePayload(Box::new(e)))), | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     if let Err(e) = buffer.flush().await { | ||||
|         return Err(MeilisearchHttpError::Payload(ReceivePayload(Box::new(e)))); | ||||
|     } | ||||
|  | ||||
|     if buffer_write_size == 0 { | ||||
|         return Err(MeilisearchHttpError::MissingPayload(format)); | ||||
|     } | ||||
|  | ||||
|     if let Err(e) = buffer.seek(std::io::SeekFrom::Start(0)).await { | ||||
|         return Err(MeilisearchHttpError::Payload(ReceivePayload(Box::new(e)))); | ||||
|     } | ||||
|  | ||||
|     let read_file = buffer.into_inner().into_std().await; | ||||
|     let documents_count = tokio::task::spawn_blocking(move || { | ||||
|         let documents_count = match format { | ||||
|             PayloadType::Json => read_json(&read_file, &mut update_file)?, | ||||
|             PayloadType::Csv { delimiter } => read_csv(&read_file, &mut update_file, delimiter)?, | ||||
|             PayloadType::Ndjson => read_ndjson(&read_file, &mut update_file)?, | ||||
|         }; | ||||
|         // we NEED to persist the file here because we moved the `udpate_file` in another task. | ||||
|         update_file.persist()?; | ||||
|         Ok(documents_count) | ||||
|     }) | ||||
|     .await; | ||||
|  | ||||
|     let documents_count = match documents_count { | ||||
|         Ok(Ok(documents_count)) => documents_count, | ||||
|         // in this case the file has not possibly be persisted. | ||||
| @@ -703,6 +709,39 @@ async fn document_addition( | ||||
|     Ok(task.into()) | ||||
| } | ||||
|  | ||||
| async fn copy_body_to_file( | ||||
|     output: std::fs::File, | ||||
|     mut body: Payload, | ||||
|     format: PayloadType, | ||||
| ) -> Result<std::fs::File, MeilisearchHttpError> { | ||||
|     let async_file = File::from_std(output); | ||||
|     let mut buffer = BufWriter::new(async_file); | ||||
|     let mut buffer_write_size: usize = 0; | ||||
|     while let Some(result) = body.next().await { | ||||
|         let byte = result?; | ||||
|  | ||||
|         if byte.is_empty() && buffer_write_size == 0 { | ||||
|             return Err(MeilisearchHttpError::MissingPayload(format)); | ||||
|         } | ||||
|  | ||||
|         match buffer.write_all(&byte).await { | ||||
|             Ok(()) => buffer_write_size += 1, | ||||
|             Err(e) => return Err(MeilisearchHttpError::Payload(ReceivePayload(Box::new(e)))), | ||||
|         } | ||||
|     } | ||||
|     if let Err(e) = buffer.flush().await { | ||||
|         return Err(MeilisearchHttpError::Payload(ReceivePayload(Box::new(e)))); | ||||
|     } | ||||
|     if buffer_write_size == 0 { | ||||
|         return Err(MeilisearchHttpError::MissingPayload(format)); | ||||
|     } | ||||
|     if let Err(e) = buffer.seek(std::io::SeekFrom::Start(0)).await { | ||||
|         return Err(MeilisearchHttpError::Payload(ReceivePayload(Box::new(e)))); | ||||
|     } | ||||
|     let read_file = buffer.into_inner().into_std().await; | ||||
|     Ok(read_file) | ||||
| } | ||||
|  | ||||
| pub async fn delete_documents_batch( | ||||
|     index_scheduler: GuardedData<ActionPolicy<{ actions::DOCUMENTS_DELETE }>, Data<IndexScheduler>>, | ||||
|     index_uid: web::Path<String>, | ||||
|   | ||||
| @@ -185,7 +185,8 @@ pub async fn search( | ||||
|  | ||||
|     let index = index_scheduler.index(&index_uid)?; | ||||
|     let features = index_scheduler.features(); | ||||
|     let search_kind = search_kind(&search_query, &index_scheduler, &index, features)?; | ||||
|     let search_kind = | ||||
|         search_kind(&search_query, &index_scheduler, index_uid.to_string(), &index, features)?; | ||||
|     let permit = search_queue.try_get_search_permit().await?; | ||||
|     let search_result = tokio::task::spawn_blocking(move || { | ||||
|         perform_facet_search( | ||||
|   | ||||
| @@ -5,7 +5,7 @@ use actix_web::web::Data; | ||||
| use actix_web::{web, HttpRequest, HttpResponse}; | ||||
| use deserr::actix_web::{AwebJson, AwebQueryParameter}; | ||||
| use deserr::{DeserializeError, Deserr, ValuePointerRef}; | ||||
| use index_scheduler::IndexScheduler; | ||||
| use index_scheduler::{Error, IndexScheduler}; | ||||
| use meilisearch_types::deserr::query_params::Param; | ||||
| use meilisearch_types::deserr::{immutable_field_error, DeserrJsonError, DeserrQueryParamError}; | ||||
| use meilisearch_types::error::deserr_codes::*; | ||||
| @@ -109,7 +109,10 @@ pub async fn list_indexes( | ||||
|             if !filters.is_index_authorized(uid) { | ||||
|                 return Ok(None); | ||||
|             } | ||||
|             Ok(Some(IndexView::new(uid.to_string(), index)?)) | ||||
|             Ok(Some( | ||||
|                 IndexView::new(uid.to_string(), index) | ||||
|                     .map_err(|e| Error::from_milli(e, Some(uid.to_string())))?, | ||||
|             )) | ||||
|         })?; | ||||
|     // Won't cause to open all indexes because IndexView doesn't keep the `Index` opened. | ||||
|     let indexes: Vec<IndexView> = indexes.into_iter().flatten().collect(); | ||||
|   | ||||
| @@ -243,11 +243,19 @@ pub async fn search_with_url_query( | ||||
|     let index = index_scheduler.index(&index_uid)?; | ||||
|     let features = index_scheduler.features(); | ||||
|  | ||||
|     let search_kind = search_kind(&query, index_scheduler.get_ref(), &index, features)?; | ||||
|     let search_kind = | ||||
|         search_kind(&query, index_scheduler.get_ref(), index_uid.to_string(), &index, features)?; | ||||
|     let retrieve_vector = RetrieveVectors::new(query.retrieve_vectors, features)?; | ||||
|     let permit = search_queue.try_get_search_permit().await?; | ||||
|     let search_result = tokio::task::spawn_blocking(move || { | ||||
|         perform_search(&index, query, search_kind, retrieve_vector, index_scheduler.features()) | ||||
|         perform_search( | ||||
|             index_uid.to_string(), | ||||
|             &index, | ||||
|             query, | ||||
|             search_kind, | ||||
|             retrieve_vector, | ||||
|             index_scheduler.features(), | ||||
|         ) | ||||
|     }) | ||||
|     .await; | ||||
|     permit.drop().await; | ||||
| @@ -287,12 +295,20 @@ pub async fn search_with_post( | ||||
|  | ||||
|     let features = index_scheduler.features(); | ||||
|  | ||||
|     let search_kind = search_kind(&query, index_scheduler.get_ref(), &index, features)?; | ||||
|     let search_kind = | ||||
|         search_kind(&query, index_scheduler.get_ref(), index_uid.to_string(), &index, features)?; | ||||
|     let retrieve_vectors = RetrieveVectors::new(query.retrieve_vectors, features)?; | ||||
|  | ||||
|     let permit = search_queue.try_get_search_permit().await?; | ||||
|     let search_result = tokio::task::spawn_blocking(move || { | ||||
|         perform_search(&index, query, search_kind, retrieve_vectors, index_scheduler.features()) | ||||
|         perform_search( | ||||
|             index_uid.to_string(), | ||||
|             &index, | ||||
|             query, | ||||
|             search_kind, | ||||
|             retrieve_vectors, | ||||
|             index_scheduler.features(), | ||||
|         ) | ||||
|     }) | ||||
|     .await; | ||||
|     permit.drop().await; | ||||
| @@ -314,6 +330,7 @@ pub async fn search_with_post( | ||||
| pub fn search_kind( | ||||
|     query: &SearchQuery, | ||||
|     index_scheduler: &IndexScheduler, | ||||
|     index_uid: String, | ||||
|     index: &milli::Index, | ||||
|     features: RoFeatures, | ||||
| ) -> Result<SearchKind, ResponseError> { | ||||
| @@ -332,7 +349,7 @@ pub fn search_kind( | ||||
|         (None, _, None) => Ok(SearchKind::KeywordOnly), | ||||
|         // hybrid.semantic_ratio == 1.0 => vector | ||||
|         (_, Some(HybridQuery { semantic_ratio, embedder }), v) if **semantic_ratio == 1.0 => { | ||||
|             SearchKind::semantic(index_scheduler, index, embedder, v.map(|v| v.len())) | ||||
|             SearchKind::semantic(index_scheduler, index_uid, index, embedder, v.map(|v| v.len())) | ||||
|         } | ||||
|         // hybrid.semantic_ratio == 0.0 => keyword | ||||
|         (_, Some(HybridQuery { semantic_ratio, embedder: _ }), _) if **semantic_ratio == 0.0 => { | ||||
| @@ -340,13 +357,14 @@ pub fn search_kind( | ||||
|         } | ||||
|         // no query, hybrid, vector => semantic | ||||
|         (None, Some(HybridQuery { semantic_ratio: _, embedder }), Some(v)) => { | ||||
|             SearchKind::semantic(index_scheduler, index, embedder, Some(v.len())) | ||||
|             SearchKind::semantic(index_scheduler, index_uid, index, embedder, Some(v.len())) | ||||
|         } | ||||
|         // query, no hybrid, no vector => keyword | ||||
|         (Some(_), None, None) => Ok(SearchKind::KeywordOnly), | ||||
|         // query, hybrid, maybe vector => hybrid | ||||
|         (Some(_), Some(HybridQuery { semantic_ratio, embedder }), v) => SearchKind::hybrid( | ||||
|             index_scheduler, | ||||
|             index_uid, | ||||
|             index, | ||||
|             embedder, | ||||
|             **semantic_ratio, | ||||
|   | ||||
| @@ -17,6 +17,32 @@ use crate::extractors::authentication::GuardedData; | ||||
| use crate::routes::{get_task_id, is_dry_run, SummarizedTaskView}; | ||||
| use crate::Opt; | ||||
|  | ||||
| /// This macro generates the routes for the settings. | ||||
| /// | ||||
| /// It takes a list of settings and generates a module for each setting. | ||||
| /// Each module contains the `get`, `update` and `delete` routes for the setting. | ||||
| /// | ||||
| /// It also generates a `configure` function that configures the routes for the settings. | ||||
| macro_rules! make_setting_routes { | ||||
|     ($({route: $route:literal, update_verb: $update_verb:ident, value_type: $type:ty, err_type: $err_ty:ty, attr: $attr:ident, camelcase_attr: $camelcase_attr:literal, analytics: $analytics:ident},)*) => { | ||||
|         $( | ||||
|             make_setting_route!($route, $update_verb, $type, $err_ty, $attr, $camelcase_attr, $analytics); | ||||
|         )* | ||||
|  | ||||
|         pub fn configure(cfg: &mut web::ServiceConfig) { | ||||
|             use crate::extractors::sequential_extractor::SeqHandler; | ||||
|             cfg.service( | ||||
|                 web::resource("") | ||||
|                 .route(web::patch().to(SeqHandler(update_all))) | ||||
|                 .route(web::get().to(SeqHandler(get_all))) | ||||
|                 .route(web::delete().to(SeqHandler(delete_all)))) | ||||
|                 $(.service($attr::resources()))*; | ||||
|         } | ||||
|  | ||||
|         pub const ALL_SETTINGS_NAMES: &[&str] = &[$(stringify!($attr)),*]; | ||||
|     }; | ||||
| } | ||||
|  | ||||
| #[macro_export] | ||||
| macro_rules! make_setting_route { | ||||
|     ($route:literal, $update_verb:ident, $type:ty, $err_ty:ty, $attr:ident, $camelcase_attr:literal, $analytics:ident) => { | ||||
| @@ -153,255 +179,227 @@ macro_rules! make_setting_route { | ||||
|     }; | ||||
| } | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/filterable-attributes", | ||||
|     put, | ||||
|     std::collections::BTreeSet<String>, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsFilterableAttributes, | ||||
|     >, | ||||
|     filterable_attributes, | ||||
|     "filterableAttributes", | ||||
|     FilterableAttributesAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/sortable-attributes", | ||||
|     put, | ||||
|     std::collections::BTreeSet<String>, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsSortableAttributes, | ||||
|     >, | ||||
|     sortable_attributes, | ||||
|     "sortableAttributes", | ||||
|     SortableAttributesAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/displayed-attributes", | ||||
|     put, | ||||
|     Vec<String>, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsDisplayedAttributes, | ||||
|     >, | ||||
|     displayed_attributes, | ||||
|     "displayedAttributes", | ||||
|     DisplayedAttributesAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/typo-tolerance", | ||||
|     patch, | ||||
|     meilisearch_types::settings::TypoSettings, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsTypoTolerance, | ||||
|     >, | ||||
|     typo_tolerance, | ||||
|     "typoTolerance", | ||||
|     TypoToleranceAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/searchable-attributes", | ||||
|     put, | ||||
|     Vec<String>, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsSearchableAttributes, | ||||
|     >, | ||||
|     searchable_attributes, | ||||
|     "searchableAttributes", | ||||
|     SearchableAttributesAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/stop-words", | ||||
|     put, | ||||
|     std::collections::BTreeSet<String>, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsStopWords, | ||||
|     >, | ||||
|     stop_words, | ||||
|     "stopWords", | ||||
|     StopWordsAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/non-separator-tokens", | ||||
|     put, | ||||
|     std::collections::BTreeSet<String>, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsNonSeparatorTokens, | ||||
|     >, | ||||
|     non_separator_tokens, | ||||
|     "nonSeparatorTokens", | ||||
|     NonSeparatorTokensAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/separator-tokens", | ||||
|     put, | ||||
|     std::collections::BTreeSet<String>, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsSeparatorTokens, | ||||
|     >, | ||||
|     separator_tokens, | ||||
|     "separatorTokens", | ||||
|     SeparatorTokensAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/dictionary", | ||||
|     put, | ||||
|     std::collections::BTreeSet<String>, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsDictionary, | ||||
|     >, | ||||
|     dictionary, | ||||
|     "dictionary", | ||||
|     DictionaryAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/synonyms", | ||||
|     put, | ||||
|     std::collections::BTreeMap<String, Vec<String>>, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsSynonyms, | ||||
|     >, | ||||
|     synonyms, | ||||
|     "synonyms", | ||||
|     SynonymsAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/distinct-attribute", | ||||
|     put, | ||||
|     String, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsDistinctAttribute, | ||||
|     >, | ||||
|     distinct_attribute, | ||||
|     "distinctAttribute", | ||||
|     DistinctAttributeAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/proximity-precision", | ||||
|     put, | ||||
|     meilisearch_types::settings::ProximityPrecisionView, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsProximityPrecision, | ||||
|     >, | ||||
|     proximity_precision, | ||||
|     "proximityPrecision", | ||||
|     ProximityPrecisionAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/localized-attributes", | ||||
|     put, | ||||
|     Vec<meilisearch_types::locales::LocalizedAttributesRuleView>, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsLocalizedAttributes, | ||||
|     >, | ||||
|     localized_attributes, | ||||
|     "localizedAttributes", | ||||
|     LocalesAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/ranking-rules", | ||||
|     put, | ||||
|     Vec<meilisearch_types::settings::RankingRuleView>, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsRankingRules, | ||||
|     >, | ||||
|     ranking_rules, | ||||
|     "rankingRules", | ||||
|     RankingRulesAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/faceting", | ||||
|     patch, | ||||
|     meilisearch_types::settings::FacetingSettings, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsFaceting, | ||||
|     >, | ||||
|     faceting, | ||||
|     "faceting", | ||||
|     FacetingAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/pagination", | ||||
|     patch, | ||||
|     meilisearch_types::settings::PaginationSettings, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsPagination, | ||||
|     >, | ||||
|     pagination, | ||||
|     "pagination", | ||||
|     PaginationAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/embedders", | ||||
|     patch, | ||||
|     std::collections::BTreeMap<String, Setting<meilisearch_types::milli::vector::settings::EmbeddingSettings>>, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsEmbedders, | ||||
|     >, | ||||
|     embedders, | ||||
|     "embedders", | ||||
|     EmbeddersAnalytics | ||||
| ); | ||||
|  | ||||
| make_setting_route!( | ||||
|     "/search-cutoff-ms", | ||||
|     put, | ||||
|     u64, | ||||
|     meilisearch_types::deserr::DeserrJsonError< | ||||
|         meilisearch_types::error::deserr_codes::InvalidSettingsSearchCutoffMs, | ||||
|     >, | ||||
|     search_cutoff_ms, | ||||
|     "searchCutoffMs", | ||||
|     SearchCutoffMsAnalytics | ||||
| ); | ||||
|  | ||||
| macro_rules! generate_configure { | ||||
|     ($($mod:ident),*) => { | ||||
|         pub fn configure(cfg: &mut web::ServiceConfig) { | ||||
|             use crate::extractors::sequential_extractor::SeqHandler; | ||||
|             cfg.service( | ||||
|                 web::resource("") | ||||
|                 .route(web::patch().to(SeqHandler(update_all))) | ||||
|                 .route(web::get().to(SeqHandler(get_all))) | ||||
|                 .route(web::delete().to(SeqHandler(delete_all)))) | ||||
|                 $(.service($mod::resources()))*; | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| generate_configure!( | ||||
|     filterable_attributes, | ||||
|     sortable_attributes, | ||||
|     displayed_attributes, | ||||
|     localized_attributes, | ||||
|     searchable_attributes, | ||||
|     distinct_attribute, | ||||
|     proximity_precision, | ||||
|     stop_words, | ||||
|     separator_tokens, | ||||
|     non_separator_tokens, | ||||
|     dictionary, | ||||
|     synonyms, | ||||
|     ranking_rules, | ||||
|     typo_tolerance, | ||||
|     pagination, | ||||
|     faceting, | ||||
|     embedders, | ||||
|     search_cutoff_ms | ||||
| make_setting_routes!( | ||||
|     { | ||||
|         route: "/filterable-attributes", | ||||
|         update_verb: put, | ||||
|         value_type: std::collections::BTreeSet<String>, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsFilterableAttributes, | ||||
|         >, | ||||
|         attr: filterable_attributes, | ||||
|         camelcase_attr: "filterableAttributes", | ||||
|         analytics: FilterableAttributesAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/sortable-attributes", | ||||
|         update_verb: put, | ||||
|         value_type: std::collections::BTreeSet<String>, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsSortableAttributes, | ||||
|         >, | ||||
|         attr: sortable_attributes, | ||||
|         camelcase_attr: "sortableAttributes", | ||||
|         analytics: SortableAttributesAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/displayed-attributes", | ||||
|         update_verb: put, | ||||
|         value_type: Vec<String>, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsDisplayedAttributes, | ||||
|         >, | ||||
|         attr: displayed_attributes, | ||||
|         camelcase_attr: "displayedAttributes", | ||||
|         analytics: DisplayedAttributesAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/typo-tolerance", | ||||
|         update_verb: patch, | ||||
|         value_type: meilisearch_types::settings::TypoSettings, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsTypoTolerance, | ||||
|         >, | ||||
|         attr: typo_tolerance, | ||||
|         camelcase_attr: "typoTolerance", | ||||
|         analytics: TypoToleranceAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/searchable-attributes", | ||||
|         update_verb: put, | ||||
|         value_type: Vec<String>, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsSearchableAttributes, | ||||
|         >, | ||||
|         attr: searchable_attributes, | ||||
|         camelcase_attr: "searchableAttributes", | ||||
|         analytics: SearchableAttributesAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/stop-words", | ||||
|         update_verb: put, | ||||
|         value_type: std::collections::BTreeSet<String>, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsStopWords, | ||||
|         >, | ||||
|         attr: stop_words, | ||||
|         camelcase_attr: "stopWords", | ||||
|         analytics: StopWordsAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/non-separator-tokens", | ||||
|         update_verb: put, | ||||
|         value_type: std::collections::BTreeSet<String>, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsNonSeparatorTokens, | ||||
|         >, | ||||
|         attr: non_separator_tokens, | ||||
|         camelcase_attr: "nonSeparatorTokens", | ||||
|         analytics: NonSeparatorTokensAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/separator-tokens", | ||||
|         update_verb: put, | ||||
|         value_type: std::collections::BTreeSet<String>, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsSeparatorTokens, | ||||
|         >, | ||||
|         attr: separator_tokens, | ||||
|         camelcase_attr: "separatorTokens", | ||||
|         analytics: SeparatorTokensAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/dictionary", | ||||
|         update_verb: put, | ||||
|         value_type: std::collections::BTreeSet<String>, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsDictionary, | ||||
|         >, | ||||
|         attr: dictionary, | ||||
|         camelcase_attr: "dictionary", | ||||
|         analytics: DictionaryAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/synonyms", | ||||
|         update_verb: put, | ||||
|         value_type: std::collections::BTreeMap<String, Vec<String>>, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsSynonyms, | ||||
|         >, | ||||
|         attr: synonyms, | ||||
|         camelcase_attr: "synonyms", | ||||
|         analytics: SynonymsAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/distinct-attribute", | ||||
|         update_verb: put, | ||||
|         value_type: String, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsDistinctAttribute, | ||||
|         >, | ||||
|         attr: distinct_attribute, | ||||
|         camelcase_attr: "distinctAttribute", | ||||
|         analytics: DistinctAttributeAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/proximity-precision", | ||||
|         update_verb: put, | ||||
|         value_type: meilisearch_types::settings::ProximityPrecisionView, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsProximityPrecision, | ||||
|         >, | ||||
|         attr: proximity_precision, | ||||
|         camelcase_attr: "proximityPrecision", | ||||
|         analytics: ProximityPrecisionAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/localized-attributes", | ||||
|         update_verb: put, | ||||
|         value_type: Vec<meilisearch_types::locales::LocalizedAttributesRuleView>, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsLocalizedAttributes, | ||||
|         >, | ||||
|         attr: localized_attributes, | ||||
|         camelcase_attr: "localizedAttributes", | ||||
|         analytics: LocalesAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/ranking-rules", | ||||
|         update_verb: put, | ||||
|         value_type: Vec<meilisearch_types::settings::RankingRuleView>, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsRankingRules, | ||||
|         >, | ||||
|         attr: ranking_rules, | ||||
|         camelcase_attr: "rankingRules", | ||||
|         analytics: RankingRulesAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/faceting", | ||||
|         update_verb: patch, | ||||
|         value_type: meilisearch_types::settings::FacetingSettings, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsFaceting, | ||||
|         >, | ||||
|         attr: faceting, | ||||
|         camelcase_attr: "faceting", | ||||
|         analytics: FacetingAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/pagination", | ||||
|         update_verb: patch, | ||||
|         value_type: meilisearch_types::settings::PaginationSettings, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsPagination, | ||||
|         >, | ||||
|         attr: pagination, | ||||
|         camelcase_attr: "pagination", | ||||
|         analytics: PaginationAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/embedders", | ||||
|         update_verb: patch, | ||||
|         value_type: std::collections::BTreeMap<String, Setting<meilisearch_types::milli::vector::settings::EmbeddingSettings>>, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsEmbedders, | ||||
|         >, | ||||
|         attr: embedders, | ||||
|         camelcase_attr: "embedders", | ||||
|         analytics: EmbeddersAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/search-cutoff-ms", | ||||
|         update_verb: put, | ||||
|         value_type: u64, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsSearchCutoffMs, | ||||
|         >, | ||||
|         attr: search_cutoff_ms, | ||||
|         camelcase_attr: "searchCutoffMs", | ||||
|         analytics: SearchCutoffMsAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/facet-search", | ||||
|         update_verb: put, | ||||
|         value_type: bool, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsFacetSearch, | ||||
|         >, | ||||
|         attr: facet_search, | ||||
|         camelcase_attr: "facetSearch", | ||||
|         analytics: FacetSearchAnalytics | ||||
|     }, | ||||
|     { | ||||
|         route: "/prefix-search", | ||||
|         update_verb: put, | ||||
|         value_type: meilisearch_types::settings::PrefixSearchSettings, | ||||
|         err_type: meilisearch_types::deserr::DeserrJsonError< | ||||
|             meilisearch_types::error::deserr_codes::InvalidSettingsPrefixSearch, | ||||
|         >, | ||||
|         attr: prefix_search, | ||||
|         camelcase_attr: "prefixSearch", | ||||
|         analytics: PrefixSearchAnalytics | ||||
|     }, | ||||
| ); | ||||
|  | ||||
| pub async fn update_all( | ||||
| @@ -456,6 +454,8 @@ pub async fn update_all( | ||||
|             non_separator_tokens: NonSeparatorTokensAnalytics::new( | ||||
|                 new_settings.non_separator_tokens.as_ref().set(), | ||||
|             ), | ||||
|             facet_search: FacetSearchAnalytics::new(new_settings.facet_search.as_ref().set()), | ||||
|             prefix_search: PrefixSearchAnalytics::new(new_settings.prefix_search.as_ref().set()), | ||||
|         }, | ||||
|         &req, | ||||
|     ); | ||||
|   | ||||
| @@ -10,7 +10,8 @@ use meilisearch_types::locales::{Locale, LocalizedAttributesRuleView}; | ||||
| use meilisearch_types::milli::update::Setting; | ||||
| use meilisearch_types::milli::vector::settings::EmbeddingSettings; | ||||
| use meilisearch_types::settings::{ | ||||
|     FacetingSettings, PaginationSettings, ProximityPrecisionView, RankingRuleView, TypoSettings, | ||||
|     FacetingSettings, PaginationSettings, PrefixSearchSettings, ProximityPrecisionView, | ||||
|     RankingRuleView, TypoSettings, | ||||
| }; | ||||
| use serde::Serialize; | ||||
|  | ||||
| @@ -36,6 +37,8 @@ pub struct SettingsAnalytics { | ||||
|     pub dictionary: DictionaryAnalytics, | ||||
|     pub separator_tokens: SeparatorTokensAnalytics, | ||||
|     pub non_separator_tokens: NonSeparatorTokensAnalytics, | ||||
|     pub facet_search: FacetSearchAnalytics, | ||||
|     pub prefix_search: PrefixSearchAnalytics, | ||||
| } | ||||
|  | ||||
| impl Aggregate for SettingsAnalytics { | ||||
| @@ -183,6 +186,14 @@ impl Aggregate for SettingsAnalytics { | ||||
|             non_separator_tokens: NonSeparatorTokensAnalytics { | ||||
|                 total: new.non_separator_tokens.total.or(self.non_separator_tokens.total), | ||||
|             }, | ||||
|             facet_search: FacetSearchAnalytics { | ||||
|                 set: new.facet_search.set | self.facet_search.set, | ||||
|                 value: new.facet_search.value.or(self.facet_search.value), | ||||
|             }, | ||||
|             prefix_search: PrefixSearchAnalytics { | ||||
|                 set: new.prefix_search.set | self.prefix_search.set, | ||||
|                 value: new.prefix_search.value.or(self.prefix_search.value), | ||||
|             }, | ||||
|         }) | ||||
|     } | ||||
|  | ||||
| @@ -620,3 +631,35 @@ impl NonSeparatorTokensAnalytics { | ||||
|         SettingsAnalytics { non_separator_tokens: self, ..Default::default() } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Serialize, Default)] | ||||
| pub struct FacetSearchAnalytics { | ||||
|     pub set: bool, | ||||
|     pub value: Option<bool>, | ||||
| } | ||||
|  | ||||
| impl FacetSearchAnalytics { | ||||
|     pub fn new(settings: Option<&bool>) -> Self { | ||||
|         Self { set: settings.is_some(), value: settings.copied() } | ||||
|     } | ||||
|  | ||||
|     pub fn into_settings(self) -> SettingsAnalytics { | ||||
|         SettingsAnalytics { facet_search: self, ..Default::default() } | ||||
|     } | ||||
| } | ||||
|  | ||||
| #[derive(Serialize, Default)] | ||||
| pub struct PrefixSearchAnalytics { | ||||
|     pub set: bool, | ||||
|     pub value: Option<PrefixSearchSettings>, | ||||
| } | ||||
|  | ||||
| impl PrefixSearchAnalytics { | ||||
|     pub fn new(settings: Option<&PrefixSearchSettings>) -> Self { | ||||
|         Self { set: settings.is_some(), value: settings.cloned() } | ||||
|     } | ||||
|  | ||||
|     pub fn into_settings(self) -> SettingsAnalytics { | ||||
|         SettingsAnalytics { prefix_search: self, ..Default::default() } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -103,8 +103,13 @@ async fn similar( | ||||
|  | ||||
|     let index = index_scheduler.index(&index_uid)?; | ||||
|  | ||||
|     let (embedder_name, embedder, quantized) = | ||||
|         SearchKind::embedder(&index_scheduler, &index, &query.embedder, None)?; | ||||
|     let (embedder_name, embedder, quantized) = SearchKind::embedder( | ||||
|         &index_scheduler, | ||||
|         index_uid.to_string(), | ||||
|         &index, | ||||
|         &query.embedder, | ||||
|         None, | ||||
|     )?; | ||||
|  | ||||
|     tokio::task::spawn_blocking(move || { | ||||
|         perform_similar( | ||||
|   | ||||
| @@ -125,14 +125,28 @@ pub async fn multi_search_with_post( | ||||
|                         }) | ||||
|                         .with_index(query_index)?; | ||||
|  | ||||
|                     let search_kind = | ||||
|                         search_kind(&query, index_scheduler.get_ref(), &index, features) | ||||
|                             .with_index(query_index)?; | ||||
|                     let index_uid_str = index_uid.to_string(); | ||||
|  | ||||
|                     let search_kind = search_kind( | ||||
|                         &query, | ||||
|                         index_scheduler.get_ref(), | ||||
|                         index_uid_str.clone(), | ||||
|                         &index, | ||||
|                         features, | ||||
|                     ) | ||||
|                     .with_index(query_index)?; | ||||
|                     let retrieve_vector = RetrieveVectors::new(query.retrieve_vectors, features) | ||||
|                         .with_index(query_index)?; | ||||
|  | ||||
|                     let search_result = tokio::task::spawn_blocking(move || { | ||||
|                         perform_search(&index, query, search_kind, retrieve_vector, features) | ||||
|                         perform_search( | ||||
|                             index_uid_str.clone(), | ||||
|                             &index, | ||||
|                             query, | ||||
|                             search_kind, | ||||
|                             retrieve_vector, | ||||
|                             features, | ||||
|                         ) | ||||
|                     }) | ||||
|                     .await | ||||
|                     .with_index(query_index)?; | ||||
|   | ||||
| @@ -560,7 +560,8 @@ pub fn perform_federated_search( | ||||
|             // use an immediately invoked lambda to capture the result without returning from the function | ||||
|  | ||||
|             let res: Result<(), ResponseError> = (|| { | ||||
|                 let search_kind = search_kind(&query, index_scheduler, &index, features)?; | ||||
|                 let search_kind = | ||||
|                     search_kind(&query, index_scheduler, index_uid.to_string(), &index, features)?; | ||||
|  | ||||
|                 let canonicalization_kind = match (&search_kind, &query.q) { | ||||
|                     (SearchKind::SemanticOnly { .. }, _) => { | ||||
| @@ -636,7 +637,8 @@ pub fn perform_federated_search( | ||||
|                 search.offset(0); | ||||
|                 search.limit(required_hit_count); | ||||
|  | ||||
|                 let (result, _semantic_hit_count) = super::search_from_kind(search_kind, search)?; | ||||
|                 let (result, _semantic_hit_count) = | ||||
|                     super::search_from_kind(index_uid.to_string(), search_kind, search)?; | ||||
|                 let format = AttributesFormat { | ||||
|                     attributes_to_retrieve: query.attributes_to_retrieve, | ||||
|                     retrieve_vectors, | ||||
| @@ -670,7 +672,10 @@ pub fn perform_federated_search( | ||||
|  | ||||
|                 let formatter_builder = HitMaker::formatter_builder(matching_words, tokenizer); | ||||
|  | ||||
|                 let hit_maker = HitMaker::new(&index, &rtxn, format, formatter_builder)?; | ||||
|                 let hit_maker = | ||||
|                     HitMaker::new(&index, &rtxn, format, formatter_builder).map_err(|e| { | ||||
|                         MeilisearchHttpError::from_milli(e, Some(index_uid.to_string())) | ||||
|                     })?; | ||||
|  | ||||
|                 results_by_query.push(SearchResultByQuery { | ||||
|                     federation_options, | ||||
|   | ||||
| @@ -19,7 +19,9 @@ use meilisearch_types::locales::Locale; | ||||
| use meilisearch_types::milli::score_details::{ScoreDetails, ScoringStrategy}; | ||||
| use meilisearch_types::milli::vector::parsed_vectors::ExplicitVectors; | ||||
| use meilisearch_types::milli::vector::Embedder; | ||||
| use meilisearch_types::milli::{FacetValueHit, OrderBy, SearchForFacetValues, TimeBudget}; | ||||
| use meilisearch_types::milli::{ | ||||
|     FacetValueHit, InternalError, OrderBy, SearchForFacetValues, TimeBudget, | ||||
| }; | ||||
| use meilisearch_types::settings::DEFAULT_PAGINATION_MAX_TOTAL_HITS; | ||||
| use meilisearch_types::{milli, Document}; | ||||
| use milli::tokenizer::{Language, TokenizerBuilder}; | ||||
| @@ -283,35 +285,38 @@ pub enum SearchKind { | ||||
| impl SearchKind { | ||||
|     pub(crate) fn semantic( | ||||
|         index_scheduler: &index_scheduler::IndexScheduler, | ||||
|         index_uid: String, | ||||
|         index: &Index, | ||||
|         embedder_name: &str, | ||||
|         vector_len: Option<usize>, | ||||
|     ) -> Result<Self, ResponseError> { | ||||
|         let (embedder_name, embedder, quantized) = | ||||
|             Self::embedder(index_scheduler, index, embedder_name, vector_len)?; | ||||
|             Self::embedder(index_scheduler, index_uid, index, embedder_name, vector_len)?; | ||||
|         Ok(Self::SemanticOnly { embedder_name, embedder, quantized }) | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn hybrid( | ||||
|         index_scheduler: &index_scheduler::IndexScheduler, | ||||
|         index_uid: String, | ||||
|         index: &Index, | ||||
|         embedder_name: &str, | ||||
|         semantic_ratio: f32, | ||||
|         vector_len: Option<usize>, | ||||
|     ) -> Result<Self, ResponseError> { | ||||
|         let (embedder_name, embedder, quantized) = | ||||
|             Self::embedder(index_scheduler, index, embedder_name, vector_len)?; | ||||
|             Self::embedder(index_scheduler, index_uid, index, embedder_name, vector_len)?; | ||||
|         Ok(Self::Hybrid { embedder_name, embedder, quantized, semantic_ratio }) | ||||
|     } | ||||
|  | ||||
|     pub(crate) fn embedder( | ||||
|         index_scheduler: &index_scheduler::IndexScheduler, | ||||
|         index_uid: String, | ||||
|         index: &Index, | ||||
|         embedder_name: &str, | ||||
|         vector_len: Option<usize>, | ||||
|     ) -> Result<(String, Arc<Embedder>, bool), ResponseError> { | ||||
|         let embedder_configs = index.embedding_configs(&index.read_txn()?)?; | ||||
|         let embedders = index_scheduler.embedders(embedder_configs)?; | ||||
|         let embedders = index_scheduler.embedders(index_uid, embedder_configs)?; | ||||
|  | ||||
|         let (embedder, _, quantized) = embedders | ||||
|             .get(embedder_name) | ||||
| @@ -892,6 +897,7 @@ fn prepare_search<'t>( | ||||
| } | ||||
|  | ||||
| pub fn perform_search( | ||||
|     index_uid: String, | ||||
|     index: &Index, | ||||
|     query: SearchQuery, | ||||
|     search_kind: SearchKind, | ||||
| @@ -918,7 +924,7 @@ pub fn perform_search( | ||||
|             used_negative_operator, | ||||
|         }, | ||||
|         semantic_hit_count, | ||||
|     ) = search_from_kind(search_kind, search)?; | ||||
|     ) = search_from_kind(index_uid, search_kind, search)?; | ||||
|  | ||||
|     let SearchQuery { | ||||
|         q, | ||||
| @@ -1071,17 +1077,27 @@ fn compute_facet_distribution_stats<S: AsRef<str>>( | ||||
| } | ||||
|  | ||||
| pub fn search_from_kind( | ||||
|     index_uid: String, | ||||
|     search_kind: SearchKind, | ||||
|     search: milli::Search<'_>, | ||||
| ) -> Result<(milli::SearchResult, Option<u32>), MeilisearchHttpError> { | ||||
|     let (milli_result, semantic_hit_count) = match &search_kind { | ||||
|         SearchKind::KeywordOnly => (search.execute()?, None), | ||||
|         SearchKind::KeywordOnly => { | ||||
|             let results = search | ||||
|                 .execute() | ||||
|                 .map_err(|e| MeilisearchHttpError::from_milli(e, Some(index_uid.to_string())))?; | ||||
|             (results, None) | ||||
|         } | ||||
|         SearchKind::SemanticOnly { .. } => { | ||||
|             let results = search.execute()?; | ||||
|             let results = search | ||||
|                 .execute() | ||||
|                 .map_err(|e| MeilisearchHttpError::from_milli(e, Some(index_uid.to_string())))?; | ||||
|             let semantic_hit_count = results.document_scores.len() as u32; | ||||
|             (results, Some(semantic_hit_count)) | ||||
|         } | ||||
|         SearchKind::Hybrid { semantic_ratio, .. } => search.execute_hybrid(*semantic_ratio)?, | ||||
|         SearchKind::Hybrid { semantic_ratio, .. } => search | ||||
|             .execute_hybrid(*semantic_ratio) | ||||
|             .map_err(|e| MeilisearchHttpError::from_milli(e, Some(index_uid)))?, | ||||
|     }; | ||||
|     Ok((milli_result, semantic_hit_count)) | ||||
| } | ||||
| @@ -1183,7 +1199,7 @@ impl<'a> HitMaker<'a> { | ||||
|         rtxn: &'a RoTxn<'a>, | ||||
|         format: AttributesFormat, | ||||
|         mut formatter_builder: MatcherBuilder<'a>, | ||||
|     ) -> Result<Self, MeilisearchHttpError> { | ||||
|     ) -> milli::Result<Self> { | ||||
|         formatter_builder.crop_marker(format.crop_marker); | ||||
|         formatter_builder.highlight_prefix(format.highlight_pre_tag); | ||||
|         formatter_builder.highlight_suffix(format.highlight_post_tag); | ||||
| @@ -1278,11 +1294,7 @@ impl<'a> HitMaker<'a> { | ||||
|         }) | ||||
|     } | ||||
|  | ||||
|     pub fn make_hit( | ||||
|         &self, | ||||
|         id: u32, | ||||
|         score: &[ScoreDetails], | ||||
|     ) -> Result<SearchHit, MeilisearchHttpError> { | ||||
|     pub fn make_hit(&self, id: u32, score: &[ScoreDetails]) -> milli::Result<SearchHit> { | ||||
|         let (_, obkv) = | ||||
|             self.index.iter_documents(self.rtxn, std::iter::once(id))?.next().unwrap()?; | ||||
|  | ||||
| @@ -1325,7 +1337,10 @@ impl<'a> HitMaker<'a> { | ||||
|                     .is_some_and(|conf| conf.user_provided.contains(id)); | ||||
|                 let embeddings = | ||||
|                     ExplicitVectors { embeddings: Some(vector.into()), regenerate: !user_provided }; | ||||
|                 vectors.insert(name, serde_json::to_value(embeddings)?); | ||||
|                 vectors.insert( | ||||
|                     name, | ||||
|                     serde_json::to_value(embeddings).map_err(InternalError::SerdeJson)?, | ||||
|                 ); | ||||
|             } | ||||
|             document.insert("_vectors".into(), vectors.into()); | ||||
|         } | ||||
| @@ -1371,7 +1386,7 @@ fn make_hits<'a>( | ||||
|     format: AttributesFormat, | ||||
|     matching_words: milli::MatchingWords, | ||||
|     documents_ids_scores: impl Iterator<Item = (u32, &'a Vec<ScoreDetails>)> + 'a, | ||||
| ) -> Result<Vec<SearchHit>, MeilisearchHttpError> { | ||||
| ) -> milli::Result<Vec<SearchHit>> { | ||||
|     let mut documents = Vec::new(); | ||||
|  | ||||
|     let dictionary = index.dictionary(rtxn)?; | ||||
| @@ -1409,6 +1424,13 @@ pub fn perform_facet_search( | ||||
|         None => TimeBudget::default(), | ||||
|     }; | ||||
|  | ||||
|     if !index.facet_search(&rtxn)? { | ||||
|         return Err(ResponseError::from_msg( | ||||
|             "The facet search is disabled for this index".to_string(), | ||||
|             Code::FacetSearchDisabled, | ||||
|         )); | ||||
|     } | ||||
|  | ||||
|     // In the faceted search context, we want to use the intersection between the locales provided by the user | ||||
|     // and the locales of the facet string. | ||||
|     // If the facet string is not localized, we **ignore** the locales provided by the user because the facet data has no locale. | ||||
| @@ -1692,12 +1714,12 @@ fn make_document( | ||||
|     displayed_attributes: &BTreeSet<FieldId>, | ||||
|     field_ids_map: &FieldsIdsMap, | ||||
|     obkv: &obkv::KvReaderU16, | ||||
| ) -> Result<Document, MeilisearchHttpError> { | ||||
| ) -> milli::Result<Document> { | ||||
|     let mut document = serde_json::Map::new(); | ||||
|  | ||||
|     // recreate the original json | ||||
|     for (key, value) in obkv.iter() { | ||||
|         let value = serde_json::from_slice(value)?; | ||||
|         let value = serde_json::from_slice(value).map_err(InternalError::SerdeJson)?; | ||||
|         let key = field_ids_map.name(key).expect("Missing field name").to_string(); | ||||
|  | ||||
|         document.insert(key, value); | ||||
| @@ -1722,7 +1744,7 @@ fn format_fields( | ||||
|     displayable_ids: &BTreeSet<FieldId>, | ||||
|     locales: Option<&[Language]>, | ||||
|     localized_attributes: &[LocalizedAttributesRule], | ||||
| ) -> Result<(Option<MatchesPosition>, Document), MeilisearchHttpError> { | ||||
| ) -> milli::Result<(Option<MatchesPosition>, Document)> { | ||||
|     let mut matches_position = compute_matches.then(BTreeMap::new); | ||||
|     let mut document = document.clone(); | ||||
|  | ||||
| @@ -1900,5 +1922,5 @@ fn parse_filter_array(arr: &[Value]) -> Result<Option<Filter>, MeilisearchHttpEr | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     Ok(Filter::from_array(ands)?) | ||||
|     Filter::from_array(ands).map_err(|e| MeilisearchHttpError::from_milli(e, None)) | ||||
| } | ||||
|   | ||||
| @@ -223,7 +223,7 @@ async fn list_batches_status_and_type_filtered() { | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn get_batch_filter_error() { | ||||
| async fn list_batch_filter_error() { | ||||
|     let server = Server::new().await; | ||||
|  | ||||
|     let (response, code) = server.batches_filter("lol=pied").await; | ||||
| @@ -283,6 +283,7 @@ async fn test_summarized_document_addition_or_update() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 0, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "receivedDocuments": 1, | ||||
|         "indexedDocuments": 1 | ||||
| @@ -313,6 +314,7 @@ async fn test_summarized_document_addition_or_update() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 1, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "receivedDocuments": 1, | ||||
|         "indexedDocuments": 1 | ||||
| @@ -348,6 +350,7 @@ async fn test_summarized_delete_documents_by_batch() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 0, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "providedIds": 3, | ||||
|         "deletedDocuments": 0 | ||||
| @@ -379,6 +382,7 @@ async fn test_summarized_delete_documents_by_batch() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 2, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "providedIds": 1, | ||||
|         "deletedDocuments": 0 | ||||
| @@ -415,6 +419,7 @@ async fn test_summarized_delete_documents_by_filter() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 0, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "providedIds": 0, | ||||
|         "deletedDocuments": 0, | ||||
| @@ -447,6 +452,7 @@ async fn test_summarized_delete_documents_by_filter() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 2, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "providedIds": 0, | ||||
|         "deletedDocuments": 0, | ||||
| @@ -479,6 +485,7 @@ async fn test_summarized_delete_documents_by_filter() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 4, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "providedIds": 0, | ||||
|         "deletedDocuments": 0, | ||||
| @@ -515,6 +522,7 @@ async fn test_summarized_delete_document_by_id() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 0, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "providedIds": 1, | ||||
|         "deletedDocuments": 0 | ||||
| @@ -546,6 +554,7 @@ async fn test_summarized_delete_document_by_id() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 2, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "providedIds": 1, | ||||
|         "deletedDocuments": 0 | ||||
| @@ -593,6 +602,7 @@ async fn test_summarized_settings_update() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 0, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "displayedAttributes": [ | ||||
|           "doggos", | ||||
| @@ -637,6 +647,7 @@ async fn test_summarized_index_creation() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 0, | ||||
|       "progress": null, | ||||
|       "details": {}, | ||||
|       "stats": { | ||||
|         "totalNbTasks": 1, | ||||
| @@ -664,6 +675,7 @@ async fn test_summarized_index_creation() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 1, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "primaryKey": "doggos" | ||||
|       }, | ||||
| @@ -808,6 +820,7 @@ async fn test_summarized_index_update() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 0, | ||||
|       "progress": null, | ||||
|       "details": {}, | ||||
|       "stats": { | ||||
|         "totalNbTasks": 1, | ||||
| @@ -835,6 +848,7 @@ async fn test_summarized_index_update() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 1, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "primaryKey": "bones" | ||||
|       }, | ||||
| @@ -867,6 +881,7 @@ async fn test_summarized_index_update() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 3, | ||||
|       "progress": null, | ||||
|       "details": {}, | ||||
|       "stats": { | ||||
|         "totalNbTasks": 1, | ||||
| @@ -894,6 +909,7 @@ async fn test_summarized_index_update() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 4, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "primaryKey": "bones" | ||||
|       }, | ||||
| @@ -931,6 +947,7 @@ async fn test_summarized_index_swap() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 0, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "swaps": [ | ||||
|           { | ||||
| @@ -971,6 +988,7 @@ async fn test_summarized_index_swap() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 3, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "swaps": [ | ||||
|           { | ||||
| @@ -1013,6 +1031,7 @@ async fn test_summarized_batch_cancelation() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 1, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "matchedTasks": 1, | ||||
|         "canceledTasks": 0, | ||||
| @@ -1050,6 +1069,7 @@ async fn test_summarized_batch_deletion() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 1, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "matchedTasks": 1, | ||||
|         "deletedTasks": 1, | ||||
| @@ -1083,6 +1103,7 @@ async fn test_summarized_dump_creation() { | ||||
|         @r#" | ||||
|     { | ||||
|       "uid": 0, | ||||
|       "progress": null, | ||||
|       "details": { | ||||
|         "dumpUid": "[dumpUid]" | ||||
|       }, | ||||
|   | ||||
| @@ -52,6 +52,25 @@ impl Value { | ||||
|         } | ||||
|         self | ||||
|     } | ||||
|  | ||||
|     /// Return `true` if the `status` field is set to `failed`. | ||||
|     /// Panic if the `status` field doesn't exists. | ||||
|     #[track_caller] | ||||
|     pub fn is_fail(&self) -> bool { | ||||
|         if !self["status"].is_string() { | ||||
|             panic!("Called `is_fail` on {}", serde_json::to_string_pretty(&self.0).unwrap()); | ||||
|         } | ||||
|         self["status"] == serde_json::Value::String(String::from("failed")) | ||||
|     } | ||||
|  | ||||
|     // Panic if the json doesn't contain the `status` field set to "succeeded" | ||||
|     #[track_caller] | ||||
|     pub fn failed(&self) -> &Self { | ||||
|         if !self.is_fail() { | ||||
|             panic!("Called failed on {}", serde_json::to_string_pretty(&self.0).unwrap()); | ||||
|         } | ||||
|         self | ||||
|     } | ||||
| } | ||||
|  | ||||
| impl From<serde_json::Value> for Value { | ||||
|   | ||||
| @@ -1264,15 +1264,18 @@ async fn error_add_documents_bad_document_id() { | ||||
|     let server = Server::new().await; | ||||
|     let index = server.index("test"); | ||||
|     index.create(Some("docid")).await; | ||||
|  | ||||
|     // unsupported characters | ||||
|  | ||||
|     let documents = json!([ | ||||
|         { | ||||
|             "docid": "foo & bar", | ||||
|             "content": "foobar" | ||||
|         } | ||||
|     ]); | ||||
|     index.add_documents(documents, None).await; | ||||
|     index.wait_task(1).await; | ||||
|     let (response, code) = index.get_task(1).await; | ||||
|     let (value, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(value.uid()).await; | ||||
|     let (response, code) = index.get_task(value.uid()).await; | ||||
|     snapshot!(code, @"200 OK"); | ||||
|     snapshot!(json_string!(response, { ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" }), | ||||
|         @r###" | ||||
| @@ -1288,7 +1291,81 @@ async fn error_add_documents_bad_document_id() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Document identifier `\"foo & bar\"` is invalid. A document identifier can be of type integer or string, only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_), and can not be more than 512 bytes.", | ||||
|         "message": "Document identifier `\"foo & bar\"` is invalid. A document identifier can be of type integer or string, only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_), and can not be more than 511 bytes.", | ||||
|         "code": "invalid_document_id", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_id" | ||||
|       }, | ||||
|       "duration": "[duration]", | ||||
|       "enqueuedAt": "[date]", | ||||
|       "startedAt": "[date]", | ||||
|       "finishedAt": "[date]" | ||||
|     } | ||||
|     "###); | ||||
|  | ||||
|     // More than 512 bytes | ||||
|     let documents = json!([ | ||||
|         { | ||||
|             "docid": "a".repeat(600), | ||||
|             "content": "foobar" | ||||
|         } | ||||
|     ]); | ||||
|     let (value, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(value.uid()).await; | ||||
|     let (response, code) = index.get_task(value.uid()).await; | ||||
|     snapshot!(code, @"200 OK"); | ||||
|     snapshot!(json_string!(response, { ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" }), | ||||
|       @r###" | ||||
|     { | ||||
|       "uid": 2, | ||||
|       "batchUid": 2, | ||||
|       "indexUid": "test", | ||||
|       "status": "failed", | ||||
|       "type": "documentAdditionOrUpdate", | ||||
|       "canceledBy": null, | ||||
|       "details": { | ||||
|         "receivedDocuments": 1, | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Document identifier `\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"` is invalid. A document identifier can be of type integer or string, only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_), and can not be more than 511 bytes.", | ||||
|         "code": "invalid_document_id", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_id" | ||||
|       }, | ||||
|       "duration": "[duration]", | ||||
|       "enqueuedAt": "[date]", | ||||
|       "startedAt": "[date]", | ||||
|       "finishedAt": "[date]" | ||||
|     } | ||||
|     "###); | ||||
|  | ||||
|     // Exactly 512 bytes | ||||
|     let documents = json!([ | ||||
|         { | ||||
|             "docid": "a".repeat(512), | ||||
|             "content": "foobar" | ||||
|         } | ||||
|     ]); | ||||
|     let (value, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(value.uid()).await; | ||||
|     let (response, code) = index.get_task(value.uid()).await; | ||||
|     snapshot!(code, @"200 OK"); | ||||
|     snapshot!(json_string!(response, { ".duration" => "[duration]", ".enqueuedAt" => "[date]", ".startedAt" => "[date]", ".finishedAt" => "[date]" }), | ||||
|     @r###" | ||||
|     { | ||||
|       "uid": 3, | ||||
|       "batchUid": 3, | ||||
|       "indexUid": "test", | ||||
|       "status": "failed", | ||||
|       "type": "documentAdditionOrUpdate", | ||||
|       "canceledBy": null, | ||||
|       "details": { | ||||
|         "receivedDocuments": 1, | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Document identifier `\"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa\"` is invalid. A document identifier can be of type integer or string, only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_), and can not be more than 511 bytes.", | ||||
|         "code": "invalid_document_id", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_id" | ||||
| @@ -1681,7 +1758,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "The `_geo` field in the document with the id: `\"11\"` is not an object. Was expecting an object with the `_geo.lat` and `_geo.lng` fields but instead got `\"foobar\"`.", | ||||
|         "message": "Index `test`: The `_geo` field in the document with the id: `\"11\"` is not an object. Was expecting an object with the `_geo.lat` and `_geo.lng` fields but instead got `\"foobar\"`.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -1719,7 +1796,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not find latitude nor longitude in the document with the id: `\"11\"`. Was expecting `_geo.lat` and `_geo.lng` fields.", | ||||
|         "message": "Index `test`: Could not find latitude nor longitude in the document with the id: `\"11\"`. Was expecting `_geo.lat` and `_geo.lng` fields.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -1757,7 +1834,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not find latitude nor longitude in the document with the id: `\"11\"`. Was expecting `_geo.lat` and `_geo.lng` fields.", | ||||
|         "message": "Index `test`: Could not find latitude nor longitude in the document with the id: `\"11\"`. Was expecting `_geo.lat` and `_geo.lng` fields.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -1795,7 +1872,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not find longitude in the document with the id: `\"11\"`. Was expecting a `_geo.lng` field.", | ||||
|         "message": "Index `test`: Could not find longitude in the document with the id: `\"11\"`. Was expecting a `_geo.lng` field.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -1833,7 +1910,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not find latitude in the document with the id: `\"11\"`. Was expecting a `_geo.lat` field.", | ||||
|         "message": "Index `test`: Could not find latitude in the document with the id: `\"11\"`. Was expecting a `_geo.lat` field.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -1871,7 +1948,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not find longitude in the document with the id: `\"11\"`. Was expecting a `_geo.lng` field.", | ||||
|         "message": "Index `test`: Could not find longitude in the document with the id: `\"11\"`. Was expecting a `_geo.lng` field.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -1909,7 +1986,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not find latitude in the document with the id: `\"11\"`. Was expecting a `_geo.lat` field.", | ||||
|         "message": "Index `test`: Could not find latitude in the document with the id: `\"11\"`. Was expecting a `_geo.lat` field.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -1947,7 +2024,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not parse latitude nor longitude in the document with the id: `\"11\"`. Was expecting finite numbers but instead got `false` and `true`.", | ||||
|         "message": "Index `test`: Could not parse latitude nor longitude in the document with the id: `\"11\"`. Was expecting finite numbers but instead got `false` and `true`.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -1985,7 +2062,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not find longitude in the document with the id: `\"11\"`. Was expecting a `_geo.lng` field.", | ||||
|         "message": "Index `test`: Could not find longitude in the document with the id: `\"11\"`. Was expecting a `_geo.lng` field.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -2023,7 +2100,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not find latitude in the document with the id: `\"11\"`. Was expecting a `_geo.lat` field.", | ||||
|         "message": "Index `test`: Could not find latitude in the document with the id: `\"11\"`. Was expecting a `_geo.lat` field.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -2061,7 +2138,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not parse latitude nor longitude in the document with the id: `\"11\"`. Was expecting finite numbers but instead got `\"doggo\"` and `\"doggo\"`.", | ||||
|         "message": "Index `test`: Could not parse latitude nor longitude in the document with the id: `\"11\"`. Was expecting finite numbers but instead got `\"doggo\"` and `\"doggo\"`.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -2099,7 +2176,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "The `_geo` field in the document with the id: `\"11\"` contains the following unexpected fields: `{\"doggo\":\"are the best\"}`.", | ||||
|         "message": "Index `test`: The `_geo` field in the document with the id: `\"11\"` contains the following unexpected fields: `{\"doggo\":\"are the best\"}`.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -2138,7 +2215,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not parse longitude in the document with the id: `\"12\"`. Was expecting a finite number but instead got `null`.", | ||||
|         "message": "Index `test`: Could not parse longitude in the document with the id: `\"12\"`. Was expecting a finite number but instead got `null`.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -2175,7 +2252,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not parse latitude in the document with the id: `\"12\"`. Was expecting a finite number but instead got `null`.", | ||||
|         "message": "Index `test`: Could not parse latitude in the document with the id: `\"12\"`. Was expecting a finite number but instead got `null`.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -2212,7 +2289,7 @@ async fn add_documents_invalid_geo_field() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not parse latitude nor longitude in the document with the id: `\"13\"`. Was expecting finite numbers but instead got `null` and `null`.", | ||||
|         "message": "Index `test`: Could not parse latitude nor longitude in the document with the id: `\"13\"`. Was expecting finite numbers but instead got `null` and `null`.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
| @@ -2279,7 +2356,7 @@ async fn add_invalid_geo_and_then_settings() { | ||||
|         ] | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Could not parse latitude in the document with the id: `\"11\"`. Was expecting a finite number but instead got `null`.", | ||||
|         "message": "Index `test`: Could not parse latitude in the document with the id: `\"11\"`. Was expecting a finite number but instead got `null`.", | ||||
|         "code": "invalid_document_geo_field", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_geo_field" | ||||
|   | ||||
| @@ -604,7 +604,7 @@ async fn delete_document_by_filter() { | ||||
|         "originalFilter": "\"doggo = bernese\"" | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Attribute `doggo` is not filterable. This index does not have configured filterable attributes.\n1:6 doggo = bernese", | ||||
|         "message": "Index `EMPTY_INDEX`: Attribute `doggo` is not filterable. This index does not have configured filterable attributes.\n1:6 doggo = bernese", | ||||
|         "code": "invalid_document_filter", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_filter" | ||||
| @@ -636,7 +636,7 @@ async fn delete_document_by_filter() { | ||||
|         "originalFilter": "\"catto = jorts\"" | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Attribute `catto` is not filterable. Available filterable attributes are: `id`, `title`.\n1:6 catto = jorts", | ||||
|         "message": "Index `SHARED_DOCUMENTS`: Attribute `catto` is not filterable. Available filterable attributes are: `id`, `title`.\n1:6 catto = jorts", | ||||
|         "code": "invalid_document_filter", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_filter" | ||||
|   | ||||
| @@ -172,7 +172,7 @@ async fn error_update_documents_bad_document_id() { | ||||
|     assert_eq!( | ||||
|         response["error"]["message"], | ||||
|         json!( | ||||
|             r#"Document identifier `"foo & bar"` is invalid. A document identifier can be of type integer or string, only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_), and can not be more than 512 bytes."# | ||||
|             r#"Document identifier `"foo & bar"` is invalid. A document identifier can be of type integer or string, only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_), and can not be more than 511 bytes."# | ||||
|         ) | ||||
|     ); | ||||
|     assert_eq!(response["error"]["code"], json!("invalid_document_id")); | ||||
|   | ||||
| @@ -79,7 +79,9 @@ async fn import_dump_v1_movie_raw() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "### | ||||
|     ); | ||||
| @@ -242,7 +244,9 @@ async fn import_dump_v1_movie_with_settings() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "### | ||||
|     ); | ||||
| @@ -391,7 +395,9 @@ async fn import_dump_v1_rubygems_with_settings() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "### | ||||
|     ); | ||||
| @@ -526,7 +532,9 @@ async fn import_dump_v2_movie_raw() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "### | ||||
|     ); | ||||
| @@ -673,7 +681,9 @@ async fn import_dump_v2_movie_with_settings() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "### | ||||
|     ); | ||||
| @@ -819,7 +829,9 @@ async fn import_dump_v2_rubygems_with_settings() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "### | ||||
|     ); | ||||
| @@ -954,7 +966,9 @@ async fn import_dump_v3_movie_raw() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "### | ||||
|     ); | ||||
| @@ -1101,7 +1115,9 @@ async fn import_dump_v3_movie_with_settings() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "### | ||||
|     ); | ||||
| @@ -1247,7 +1263,9 @@ async fn import_dump_v3_rubygems_with_settings() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "### | ||||
|     ); | ||||
| @@ -1382,7 +1400,9 @@ async fn import_dump_v4_movie_raw() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "### | ||||
|     ); | ||||
| @@ -1529,7 +1549,9 @@ async fn import_dump_v4_movie_with_settings() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "### | ||||
|     ); | ||||
| @@ -1675,7 +1697,9 @@ async fn import_dump_v4_rubygems_with_settings() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "### | ||||
|     ); | ||||
| @@ -1922,7 +1946,9 @@ async fn import_dump_v6_containing_experimental_features() { | ||||
|         "maxTotalHits": 1000 | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "###); | ||||
|  | ||||
| @@ -2102,7 +2128,9 @@ async fn generate_and_import_dump_containing_vectors() { | ||||
|         } | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "###); | ||||
|  | ||||
|   | ||||
| @@ -95,7 +95,7 @@ async fn error_update_existing_primary_key() { | ||||
|     let response = index.wait_task(2).await; | ||||
|  | ||||
|     let expected_response = json!({ | ||||
|         "message": "Index already has a primary key: `id`.", | ||||
|         "message": "Index `test`: Index already has a primary key: `id`.", | ||||
|         "code": "index_primary_key_already_exists", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#index_primary_key_already_exists" | ||||
|   | ||||
| @@ -711,7 +711,7 @@ async fn filter_invalid_attribute_array() { | ||||
|     index.wait_task(task.uid()).await; | ||||
|  | ||||
|     let expected_response = json!({ | ||||
|         "message": "Attribute `many` is not filterable. Available filterable attributes are: `title`.\n1:5 many = Glass", | ||||
|         "message": format!("Index `{}`: Attribute `many` is not filterable. Available filterable attributes are: `title`.\n1:5 many = Glass", index.uid), | ||||
|         "code": "invalid_search_filter", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_search_filter" | ||||
| @@ -733,7 +733,7 @@ async fn filter_invalid_attribute_string() { | ||||
|     index.wait_task(task.uid()).await; | ||||
|  | ||||
|     let expected_response = json!({ | ||||
|         "message": "Attribute `many` is not filterable. Available filterable attributes are: `title`.\n1:5 many = Glass", | ||||
|         "message": format!("Index `{}`: Attribute `many` is not filterable. Available filterable attributes are: `title`.\n1:5 many = Glass", index.uid), | ||||
|         "code": "invalid_search_filter", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_search_filter" | ||||
| @@ -940,7 +940,7 @@ async fn sort_unsortable_attribute() { | ||||
|     index.wait_task(response.uid()).await.succeeded(); | ||||
|  | ||||
|     let expected_response = json!({ | ||||
|         "message": "Attribute `title` is not sortable. Available sortable attributes are: `id`.", | ||||
|         "message": format!("Index `{}`: Attribute `title` is not sortable. Available sortable attributes are: `id`.", index.uid), | ||||
|         "code": "invalid_search_sort", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_search_sort" | ||||
| @@ -998,7 +998,7 @@ async fn sort_unset_ranking_rule() { | ||||
|     index.wait_task(response.uid()).await.succeeded(); | ||||
|  | ||||
|     let expected_response = json!({ | ||||
|         "message": "You must specify where `sort` is listed in the rankingRules setting to use the sort parameter at search time.", | ||||
|         "message": format!("Index `{}`: You must specify where `sort` is listed in the rankingRules setting to use the sort parameter at search time.", index.uid), | ||||
|         "code": "invalid_search_sort", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_search_sort" | ||||
| @@ -1024,19 +1024,18 @@ async fn search_on_unknown_field() { | ||||
|         index.update_settings_searchable_attributes(json!(["id", "title"])).await; | ||||
|     index.wait_task(response.uid()).await.succeeded(); | ||||
|  | ||||
|     let expected_response = json!({ | ||||
|         "message": format!("Index `{}`: Attribute `unknown` is not searchable. Available searchable attributes are: `id, title`.", index.uid), | ||||
|         "code": "invalid_search_attributes_to_search_on", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_search_attributes_to_search_on" | ||||
|     }); | ||||
|     index | ||||
|         .search( | ||||
|             json!({"q": "Captain Marvel", "attributesToSearchOn": ["unknown"]}), | ||||
|             |response, code| { | ||||
|                 snapshot!(code, @"400 Bad Request"); | ||||
|                 snapshot!(json_string!(response), @r###" | ||||
|                 { | ||||
|                   "message": "Attribute `unknown` is not searchable. Available searchable attributes are: `id, title`.", | ||||
|                   "code": "invalid_search_attributes_to_search_on", | ||||
|                   "type": "invalid_request", | ||||
|                   "link": "https://docs.meilisearch.com/errors#invalid_search_attributes_to_search_on" | ||||
|                 } | ||||
|                 "###); | ||||
|                 assert_eq!(response, expected_response); | ||||
|                 assert_eq!(code, 400); | ||||
|             }, | ||||
|         ) | ||||
|         .await; | ||||
| @@ -1050,19 +1049,18 @@ async fn search_on_unknown_field_plus_joker() { | ||||
|         index.update_settings_searchable_attributes(json!(["id", "title"])).await; | ||||
|     index.wait_task(response.uid()).await.succeeded(); | ||||
|  | ||||
|     let expected_response = json!({ | ||||
|         "message": format!("Index `{}`: Attribute `unknown` is not searchable. Available searchable attributes are: `id, title`.", index.uid), | ||||
|         "code": "invalid_search_attributes_to_search_on", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_search_attributes_to_search_on" | ||||
|     }); | ||||
|     index | ||||
|         .search( | ||||
|             json!({"q": "Captain Marvel", "attributesToSearchOn": ["*", "unknown"]}), | ||||
|             |response, code| { | ||||
|                 snapshot!(code, @"400 Bad Request"); | ||||
|                 snapshot!(json_string!(response), @r###" | ||||
|                 { | ||||
|                   "message": "Attribute `unknown` is not searchable. Available searchable attributes are: `id, title`.", | ||||
|                   "code": "invalid_search_attributes_to_search_on", | ||||
|                   "type": "invalid_request", | ||||
|                   "link": "https://docs.meilisearch.com/errors#invalid_search_attributes_to_search_on" | ||||
|                 } | ||||
|                 "###); | ||||
|                 assert_eq!(response, expected_response); | ||||
|                 assert_eq!(code, 400); | ||||
|             }, | ||||
|         ) | ||||
|         .await; | ||||
| @@ -1071,15 +1069,8 @@ async fn search_on_unknown_field_plus_joker() { | ||||
|         .search( | ||||
|             json!({"q": "Captain Marvel", "attributesToSearchOn": ["unknown", "*"]}), | ||||
|             |response, code| { | ||||
|                 snapshot!(code, @"400 Bad Request"); | ||||
|                 snapshot!(json_string!(response), @r###" | ||||
|                 { | ||||
|                   "message": "Attribute `unknown` is not searchable. Available searchable attributes are: `id, title`.", | ||||
|                   "code": "invalid_search_attributes_to_search_on", | ||||
|                   "type": "invalid_request", | ||||
|                   "link": "https://docs.meilisearch.com/errors#invalid_search_attributes_to_search_on" | ||||
|                 } | ||||
|                 "###); | ||||
|                 assert_eq!(response, expected_response); | ||||
|                 assert_eq!(code, 400); | ||||
|             }, | ||||
|         ) | ||||
|         .await; | ||||
| @@ -1092,47 +1083,44 @@ async fn distinct_at_search_time() { | ||||
|     let (task, _) = index.create(None).await; | ||||
|     index.wait_task(task.uid()).await.succeeded(); | ||||
|  | ||||
|     let expected_response = json!({ | ||||
|         "message": format!("Index `{}`: Attribute `doggo.truc` is not filterable and thus, cannot be used as distinct attribute. This index does not have configured filterable attributes.", index.uid), | ||||
|         "code": "invalid_search_distinct", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_search_distinct" | ||||
|     }); | ||||
|     let (response, code) = | ||||
|         index.search_post(json!({"page": 0, "hitsPerPage": 2, "distinct": "doggo.truc"})).await; | ||||
|     snapshot!(code, @"400 Bad Request"); | ||||
|     snapshot!(response, @r###" | ||||
|     { | ||||
|       "message": "Attribute `doggo.truc` is not filterable and thus, cannot be used as distinct attribute. This index does not have configured filterable attributes.", | ||||
|       "code": "invalid_search_distinct", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#invalid_search_distinct" | ||||
|     } | ||||
|     "###); | ||||
|     assert_eq!(response, expected_response); | ||||
|     assert_eq!(code, 400); | ||||
|  | ||||
|     let (task, _) = index.update_settings_filterable_attributes(json!(["color", "machin"])).await; | ||||
|     index.wait_task(task.uid()).await; | ||||
|  | ||||
|     let expected_response = json!({ | ||||
|         "message": format!("Index `{}`: Attribute `doggo.truc` is not filterable and thus, cannot be used as distinct attribute. Available filterable attributes are: `color, machin`.", index.uid), | ||||
|         "code": "invalid_search_distinct", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_search_distinct" | ||||
|     }); | ||||
|     let (response, code) = | ||||
|         index.search_post(json!({"page": 0, "hitsPerPage": 2, "distinct": "doggo.truc"})).await; | ||||
|     snapshot!(code, @"400 Bad Request"); | ||||
|     snapshot!(response, @r###" | ||||
|     { | ||||
|       "message": "Attribute `doggo.truc` is not filterable and thus, cannot be used as distinct attribute. Available filterable attributes are: `color, machin`.", | ||||
|       "code": "invalid_search_distinct", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#invalid_search_distinct" | ||||
|     } | ||||
|     "###); | ||||
|     assert_eq!(response, expected_response); | ||||
|     assert_eq!(code, 400); | ||||
|  | ||||
|     let (task, _) = index.update_settings_displayed_attributes(json!(["color"])).await; | ||||
|     index.wait_task(task.uid()).await; | ||||
|  | ||||
|     let expected_response = json!({ | ||||
|         "message": format!("Index `{}`: Attribute `doggo.truc` is not filterable and thus, cannot be used as distinct attribute. Available filterable attributes are: `color, <..hidden-attributes>`.", index.uid), | ||||
|         "code": "invalid_search_distinct", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_search_distinct" | ||||
|     }); | ||||
|     let (response, code) = | ||||
|         index.search_post(json!({"page": 0, "hitsPerPage": 2, "distinct": "doggo.truc"})).await; | ||||
|     snapshot!(code, @"400 Bad Request"); | ||||
|     snapshot!(response, @r###" | ||||
|     { | ||||
|       "message": "Attribute `doggo.truc` is not filterable and thus, cannot be used as distinct attribute. Available filterable attributes are: `color, <..hidden-attributes>`.", | ||||
|       "code": "invalid_search_distinct", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#invalid_search_distinct" | ||||
|     } | ||||
|     "###); | ||||
|     assert_eq!(response, expected_response); | ||||
|     assert_eq!(code, 400); | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.search_post(json!({"page": 0, "hitsPerPage": 2, "distinct": true})).await; | ||||
|   | ||||
| @@ -41,8 +41,8 @@ async fn simple_facet_search() { | ||||
|  | ||||
|     let documents = DOCUMENTS.clone(); | ||||
|     index.update_settings_filterable_attributes(json!(["genres"])).await; | ||||
|     index.add_documents(documents, None).await; | ||||
|     index.wait_task(1).await; | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await; | ||||
| @@ -57,6 +57,116 @@ async fn simple_facet_search() { | ||||
|     assert_eq!(response["facetHits"].as_array().unwrap().len(), 1); | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn simple_facet_search_on_movies() { | ||||
|     let server = Server::new().await; | ||||
|     let index = server.index("test"); | ||||
|  | ||||
|     let documents = json!([ | ||||
|       { | ||||
|         "id": 1, | ||||
|         "title": "Carol", | ||||
|         "genres": [ | ||||
|           "Romance", | ||||
|           "Drama" | ||||
|         ], | ||||
|         "color": [ | ||||
|           "red" | ||||
|         ], | ||||
|         "platforms": [ | ||||
|           "MacOS", | ||||
|           "Linux", | ||||
|           "Windows" | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "id": 2, | ||||
|         "title": "Wonder Woman", | ||||
|         "genres": [ | ||||
|           "Action", | ||||
|           "Adventure" | ||||
|         ], | ||||
|         "color": [ | ||||
|           "green" | ||||
|         ], | ||||
|         "platforms": [ | ||||
|           "MacOS" | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "id": 3, | ||||
|         "title": "Life of Pi", | ||||
|         "genres": [ | ||||
|           "Adventure", | ||||
|           "Drama" | ||||
|         ], | ||||
|         "color": [ | ||||
|           "blue" | ||||
|         ], | ||||
|         "platforms": [ | ||||
|           "Windows" | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "id": 4, | ||||
|         "title": "Mad Max: Fury Road", | ||||
|         "genres": [ | ||||
|           "Adventure", | ||||
|           "Science Fiction" | ||||
|         ], | ||||
|         "color": [ | ||||
|           "red" | ||||
|         ], | ||||
|         "platforms": [ | ||||
|           "MacOS", | ||||
|           "Linux" | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "id": 5, | ||||
|         "title": "Moana", | ||||
|         "genres": [ | ||||
|           "Fantasy", | ||||
|           "Action" | ||||
|         ], | ||||
|         "color": [ | ||||
|           "red" | ||||
|         ], | ||||
|         "platforms": [ | ||||
|           "Windows" | ||||
|         ] | ||||
|       }, | ||||
|       { | ||||
|         "id": 6, | ||||
|         "title": "Philadelphia", | ||||
|         "genres": [ | ||||
|           "Drama" | ||||
|         ], | ||||
|         "color": [ | ||||
|           "blue" | ||||
|         ], | ||||
|         "platforms": [ | ||||
|           "MacOS", | ||||
|           "Linux", | ||||
|           "Windows" | ||||
|         ] | ||||
|       } | ||||
|     ]); | ||||
|     let (response, code) = | ||||
|         index.update_settings_filterable_attributes(json!(["genres", "color"])).await; | ||||
|     assert_eq!(202, code, "{:?}", response); | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetQuery": "", "facetName": "genres", "q": "" })).await; | ||||
|  | ||||
|     assert_eq!(code, 200, "{}", response); | ||||
|     snapshot!(response["facetHits"], @r###"[{"value":"Action","count":2},{"value":"Adventure","count":3},{"value":"Drama","count":3},{"value":"Fantasy","count":1},{"value":"Romance","count":1},{"value":"Science Fiction","count":1}]"###); | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn advanced_facet_search() { | ||||
|     let server = Server::new().await; | ||||
| @@ -65,8 +175,8 @@ async fn advanced_facet_search() { | ||||
|     let documents = DOCUMENTS.clone(); | ||||
|     index.update_settings_filterable_attributes(json!(["genres"])).await; | ||||
|     index.update_settings_typo_tolerance(json!({ "enabled": false })).await; | ||||
|     index.add_documents(documents, None).await; | ||||
|     index.wait_task(2).await; | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetName": "genres", "facetQuery": "adventre"})).await; | ||||
| @@ -89,8 +199,8 @@ async fn more_advanced_facet_search() { | ||||
|     let documents = DOCUMENTS.clone(); | ||||
|     index.update_settings_filterable_attributes(json!(["genres"])).await; | ||||
|     index.update_settings_typo_tolerance(json!({ "disableOnWords": ["adventre"] })).await; | ||||
|     index.add_documents(documents, None).await; | ||||
|     index.wait_task(2).await; | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetName": "genres", "facetQuery": "adventre"})).await; | ||||
| @@ -113,8 +223,8 @@ async fn simple_facet_search_with_max_values() { | ||||
|     let documents = DOCUMENTS.clone(); | ||||
|     index.update_settings_faceting(json!({ "maxValuesPerFacet": 1 })).await; | ||||
|     index.update_settings_filterable_attributes(json!(["genres"])).await; | ||||
|     index.add_documents(documents, None).await; | ||||
|     index.wait_task(2).await; | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await; | ||||
| @@ -135,8 +245,8 @@ async fn simple_facet_search_by_count_with_max_values() { | ||||
|         ) | ||||
|         .await; | ||||
|     index.update_settings_filterable_attributes(json!(["genres"])).await; | ||||
|     index.add_documents(documents, None).await; | ||||
|     index.wait_task(2).await; | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await; | ||||
| @@ -151,8 +261,8 @@ async fn non_filterable_facet_search_error() { | ||||
|     let index = server.index("test"); | ||||
|  | ||||
|     let documents = DOCUMENTS.clone(); | ||||
|     index.add_documents(documents, None).await; | ||||
|     index.wait_task(0).await; | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await; | ||||
| @@ -170,8 +280,8 @@ async fn facet_search_dont_support_words() { | ||||
|  | ||||
|     let documents = DOCUMENTS.clone(); | ||||
|     index.update_settings_filterable_attributes(json!(["genres"])).await; | ||||
|     index.add_documents(documents, None).await; | ||||
|     index.wait_task(1).await; | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetName": "genres", "facetQuery": "words"})).await; | ||||
| @@ -188,8 +298,8 @@ async fn simple_facet_search_with_sort_by_count() { | ||||
|     let documents = DOCUMENTS.clone(); | ||||
|     index.update_settings_faceting(json!({ "sortFacetValuesBy": { "*": "count" } })).await; | ||||
|     index.update_settings_filterable_attributes(json!(["genres"])).await; | ||||
|     index.add_documents(documents, None).await; | ||||
|     index.wait_task(2).await; | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await; | ||||
| @@ -200,3 +310,129 @@ async fn simple_facet_search_with_sort_by_count() { | ||||
|     assert_eq!(hits[0], json!({ "value": "Action", "count": 3 })); | ||||
|     assert_eq!(hits[1], json!({ "value": "Adventure", "count": 2 })); | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn add_documents_and_deactivate_facet_search() { | ||||
|     let server = Server::new().await; | ||||
|     let index = server.index("test"); | ||||
|  | ||||
|     let documents = DOCUMENTS.clone(); | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "facetSearch": false, | ||||
|             "filterableAttributes": ["genres"], | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await; | ||||
|  | ||||
|     assert_eq!(code, 400, "{}", response); | ||||
|     snapshot!(response, @r###" | ||||
|     { | ||||
|       "message": "The facet search is disabled for this index", | ||||
|       "code": "facet_search_disabled", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#facet_search_disabled" | ||||
|     } | ||||
|     "###); | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn deactivate_facet_search_and_add_documents() { | ||||
|     let server = Server::new().await; | ||||
|     let index = server.index("test"); | ||||
|  | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "facetSearch": false, | ||||
|             "filterableAttributes": ["genres"], | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(response.uid()).await; | ||||
|     let documents = DOCUMENTS.clone(); | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await; | ||||
|  | ||||
|     assert_eq!(code, 400, "{}", response); | ||||
|     snapshot!(response, @r###" | ||||
|     { | ||||
|       "message": "The facet search is disabled for this index", | ||||
|       "code": "facet_search_disabled", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#facet_search_disabled" | ||||
|     } | ||||
|     "###); | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn deactivate_facet_search_add_documents_and_activate_facet_search() { | ||||
|     let server = Server::new().await; | ||||
|     let index = server.index("test"); | ||||
|  | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "facetSearch": false, | ||||
|             "filterableAttributes": ["genres"], | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(response.uid()).await; | ||||
|     let documents = DOCUMENTS.clone(); | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "facetSearch": true, | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await; | ||||
|  | ||||
|     assert_eq!(code, 200, "{}", response); | ||||
|     assert_eq!(dbg!(response)["facetHits"].as_array().unwrap().len(), 2); | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn deactivate_facet_search_add_documents_and_reset_facet_search() { | ||||
|     let server = Server::new().await; | ||||
|     let index = server.index("test"); | ||||
|  | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "facetSearch": false, | ||||
|             "filterableAttributes": ["genres"], | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(response.uid()).await; | ||||
|     let documents = DOCUMENTS.clone(); | ||||
|     let (response, _code) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "facetSearch": serde_json::Value::Null, | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = | ||||
|         index.facet_search(json!({"facetName": "genres", "facetQuery": "a"})).await; | ||||
|  | ||||
|     assert_eq!(code, 200, "{}", response); | ||||
|     assert_eq!(dbg!(response)["facetHits"].as_array().unwrap().len(), 2); | ||||
| } | ||||
|   | ||||
| @@ -4,6 +4,58 @@ use super::*; | ||||
| use crate::common::Server; | ||||
| use crate::json; | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn search_formatted_from_sdk() { | ||||
|     let server = Server::new_shared(); | ||||
|     let index = server.unique_index(); | ||||
|  | ||||
|     index | ||||
|         .update_settings( | ||||
|             json!({ "filterableAttributes": ["genre"], "searchableAttributes": ["title"] }), | ||||
|         ) | ||||
|         .await; | ||||
|  | ||||
|     let documents = json!([ | ||||
|       { "id": 123,  "title": "Pride and Prejudice",                     "genre": "romance" }, | ||||
|       { "id": 456,  "title": "Le Petit Prince",                         "genre": "adventure" }, | ||||
|       { "id": 1,    "title": "Alice In Wonderland",                     "genre": "adventure" }, | ||||
|       { "id": 2,    "title": "Le Rouge et le Noir",                     "genre": "romance" }, | ||||
|       { "id": 1344, "title": "The Hobbit",                              "genre": "adventure" }, | ||||
|       { "id": 4,    "title": "Harry Potter and the Half-Blood Prince",  "genre": "fantasy" }, | ||||
|       { "id": 7,    "title": "Harry Potter and the Chamber of Secrets", "genre": "fantasy" }, | ||||
|       { "id": 42,   "title": "The Hitchhiker's Guide to the Galaxy" } | ||||
|     ]); | ||||
|     let (response, _) = index.add_documents(documents, None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     index | ||||
|         .search( | ||||
|             json!({ "q":"prince", | ||||
|               "attributesToCrop": ["title"], | ||||
|               "cropLength": 2, | ||||
|               "filter": "genre = adventure", | ||||
|               "attributesToHighlight": ["title"], | ||||
|               "attributesToRetrieve": ["title"] | ||||
|             }), | ||||
|             |response, code| { | ||||
|                 assert_eq!(code, 200, "{}", response); | ||||
|                 allow_duplicates! { | ||||
|                   assert_json_snapshot!(response["hits"][0], | ||||
|                         { "._rankingScore" => "[score]" }, | ||||
|                         @r###" | ||||
|                   { | ||||
|                     "title": "Le Petit Prince", | ||||
|                     "_formatted": { | ||||
|                       "title": "…Petit <em>Prince</em>" | ||||
|                     } | ||||
|                   } | ||||
|                   "###); | ||||
|                 } | ||||
|             }, | ||||
|         ) | ||||
|         .await; | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn formatted_contain_wildcard() { | ||||
|     let server = Server::new_shared(); | ||||
|   | ||||
| @@ -15,6 +15,7 @@ mod pagination; | ||||
| mod restrict_searchable; | ||||
| mod search_queue; | ||||
|  | ||||
| use meili_snap::{json_string, snapshot}; | ||||
| use meilisearch::Opt; | ||||
| use tempfile::TempDir; | ||||
|  | ||||
| @@ -62,6 +63,71 @@ async fn simple_search() { | ||||
|         .await; | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn search_with_stop_word() { | ||||
|     // related to https://github.com/meilisearch/meilisearch/issues/4984 | ||||
|     let server = Server::new().await; | ||||
|     let index = server.index("test"); | ||||
|  | ||||
|     let (_, code) = index | ||||
|         .update_settings(json!({"stopWords": ["the", "The", "a", "an", "to", "in", "of"]})) | ||||
|         .await; | ||||
|     meili_snap::snapshot!(code, @"202 Accepted"); | ||||
|  | ||||
|     let documents = DOCUMENTS.clone(); | ||||
|     index.add_documents(documents, None).await; | ||||
|     index.wait_task(1).await; | ||||
|  | ||||
|     // prefix search | ||||
|     index | ||||
|         .search(json!({"q": "to the", "attributesToHighlight": ["title"], "attributesToRetrieve": ["title"] }), |response, code| { | ||||
|             assert_eq!(code, 200, "{}", response); | ||||
|             snapshot!(json_string!(response["hits"]), @"[]"); | ||||
|         }) | ||||
|         .await; | ||||
|  | ||||
|     // non-prefix search | ||||
|     index | ||||
|           .search(json!({"q": "to the ", "attributesToHighlight": ["title"], "attributesToRetrieve": ["title"] }), |response, code| { | ||||
|               assert_eq!(code, 200, "{}", response); | ||||
|               snapshot!(json_string!(response["hits"]), @r###" | ||||
|               [ | ||||
|                 { | ||||
|                   "title": "Shazam!", | ||||
|                   "_formatted": { | ||||
|                     "title": "Shazam!" | ||||
|                   } | ||||
|                 }, | ||||
|                 { | ||||
|                   "title": "Captain Marvel", | ||||
|                   "_formatted": { | ||||
|                     "title": "Captain Marvel" | ||||
|                   } | ||||
|                 }, | ||||
|                 { | ||||
|                   "title": "Escape Room", | ||||
|                   "_formatted": { | ||||
|                     "title": "Escape Room" | ||||
|                   } | ||||
|                 }, | ||||
|                 { | ||||
|                   "title": "How to Train Your Dragon: The Hidden World", | ||||
|                   "_formatted": { | ||||
|                     "title": "How to Train Your Dragon: The Hidden World" | ||||
|                   } | ||||
|                 }, | ||||
|                 { | ||||
|                   "title": "Gläss", | ||||
|                   "_formatted": { | ||||
|                     "title": "Gläss" | ||||
|                   } | ||||
|                 } | ||||
|               ] | ||||
|               "###); | ||||
|           }) | ||||
|           .await; | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn phrase_search_with_stop_word() { | ||||
|     // related to https://github.com/meilisearch/meilisearch/issues/3521 | ||||
|   | ||||
| @@ -1070,7 +1070,7 @@ async fn federation_one_query_error() { | ||||
|     snapshot!(code, @"400 Bad Request"); | ||||
|     snapshot!(json_string!(response), @r###" | ||||
|     { | ||||
|       "message": "Inside `.queries[1]`: Attribute `title` is not filterable. This index does not have configured filterable attributes.\n1:6 title = toto", | ||||
|       "message": "Inside `.queries[1]`: Index `nested`: Attribute `title` is not filterable. This index does not have configured filterable attributes.\n1:6 title = toto", | ||||
|       "code": "invalid_search_filter", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#invalid_search_filter" | ||||
| @@ -1102,7 +1102,7 @@ async fn federation_one_query_sort_error() { | ||||
|     snapshot!(code, @"400 Bad Request"); | ||||
|     snapshot!(json_string!(response), @r###" | ||||
|     { | ||||
|       "message": "Inside `.queries[1]`: Attribute `doggos` is not sortable. This index does not have configured sortable attributes.", | ||||
|       "message": "Inside `.queries[1]`: Index `nested`: Attribute `doggos` is not sortable. This index does not have configured sortable attributes.", | ||||
|       "code": "invalid_search_sort", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#invalid_search_sort" | ||||
| @@ -1166,7 +1166,7 @@ async fn federation_multiple_query_errors() { | ||||
|     snapshot!(code, @"400 Bad Request"); | ||||
|     snapshot!(json_string!(response), @r###" | ||||
|     { | ||||
|       "message": "Inside `.queries[0]`: Attribute `title` is not filterable. This index does not have configured filterable attributes.\n1:6 title = toto", | ||||
|       "message": "Inside `.queries[0]`: Index `test`: Attribute `title` is not filterable. This index does not have configured filterable attributes.\n1:6 title = toto", | ||||
|       "code": "invalid_search_filter", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#invalid_search_filter" | ||||
| @@ -1198,7 +1198,7 @@ async fn federation_multiple_query_sort_errors() { | ||||
|     snapshot!(code, @"400 Bad Request"); | ||||
|     snapshot!(json_string!(response), @r###" | ||||
|     { | ||||
|       "message": "Inside `.queries[0]`: Attribute `title` is not sortable. This index does not have configured sortable attributes.", | ||||
|       "message": "Inside `.queries[0]`: Index `test`: Attribute `title` is not sortable. This index does not have configured sortable attributes.", | ||||
|       "code": "invalid_search_sort", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#invalid_search_sort" | ||||
| @@ -1231,7 +1231,7 @@ async fn federation_multiple_query_errors_interleaved() { | ||||
|     snapshot!(code, @"400 Bad Request"); | ||||
|     snapshot!(json_string!(response), @r###" | ||||
|     { | ||||
|       "message": "Inside `.queries[1]`: Attribute `doggos` is not filterable. This index does not have configured filterable attributes.\n1:7 doggos IN [intel, kefir]", | ||||
|       "message": "Inside `.queries[1]`: Index `nested`: Attribute `doggos` is not filterable. This index does not have configured filterable attributes.\n1:7 doggos IN [intel, kefir]", | ||||
|       "code": "invalid_search_filter", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#invalid_search_filter" | ||||
| @@ -1264,7 +1264,7 @@ async fn federation_multiple_query_sort_errors_interleaved() { | ||||
|     snapshot!(code, @"400 Bad Request"); | ||||
|     snapshot!(json_string!(response), @r###" | ||||
|     { | ||||
|       "message": "Inside `.queries[1]`: Attribute `doggos` is not sortable. This index does not have configured sortable attributes.", | ||||
|       "message": "Inside `.queries[1]`: Index `nested`: Attribute `doggos` is not sortable. This index does not have configured sortable attributes.", | ||||
|       "code": "invalid_search_sort", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#invalid_search_sort" | ||||
|   | ||||
| @@ -367,3 +367,50 @@ async fn search_on_exact_field() { | ||||
|         }) | ||||
|         .await; | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn phrase_search_on_title() { | ||||
|     let server = Server::new().await; | ||||
|     let documents = json!([ | ||||
|       { "id": 8, "desc": "Document Review", "title": "Document Review Specialist II" }, | ||||
|       { "id": 5, "desc": "Document Review", "title": "Document Review Attorney" }, | ||||
|       { "id": 4, "desc": "Document Review", "title": "Document Review Manager - Cyber Incident Response (Remote)" }, | ||||
|       { "id": 3, "desc": "Document Review", "title": "Document Review Paralegal" }, | ||||
|       { "id": 2, "desc": "Document Review", "title": "Document Controller (Saudi National)" }, | ||||
|       { "id": 1, "desc": "Document Review", "title": "Document Reviewer" }, | ||||
|       { "id": 7, "desc": "Document Review", "title": "Document Review Specialist II" }, | ||||
|       { "id": 6, "desc": "Document Review", "title": "Document Review (Entry Level)" } | ||||
|     ]); | ||||
|     let index = index_with_documents(&server, &documents).await; | ||||
|  | ||||
|     index | ||||
|         .search( | ||||
|             json!({"q": "\"Document Review\"", "attributesToSearchOn": ["title"], "attributesToRetrieve": ["title"]}), | ||||
|             |response, code| { | ||||
|                 snapshot!(code, @"200 OK"); | ||||
|                 snapshot!(json_string!(response["hits"]), @r###" | ||||
|                 [ | ||||
|                   { | ||||
|                     "title": "Document Review Specialist II" | ||||
|                   }, | ||||
|                   { | ||||
|                     "title": "Document Review Attorney" | ||||
|                   }, | ||||
|                   { | ||||
|                     "title": "Document Review Manager - Cyber Incident Response (Remote)" | ||||
|                   }, | ||||
|                   { | ||||
|                     "title": "Document Review Paralegal" | ||||
|                   }, | ||||
|                   { | ||||
|                     "title": "Document Review Specialist II" | ||||
|                   }, | ||||
|                   { | ||||
|                     "title": "Document Review (Entry Level)" | ||||
|                   } | ||||
|                 ] | ||||
|                 "###); | ||||
|             }, | ||||
|         ) | ||||
|         .await; | ||||
| } | ||||
|   | ||||
| @@ -1,44 +1,185 @@ | ||||
| use std::collections::HashMap; | ||||
|  | ||||
| use once_cell::sync::Lazy; | ||||
|  | ||||
| use crate::common::{Server, Value}; | ||||
| use crate::common::Server; | ||||
| use crate::json; | ||||
|  | ||||
| static DEFAULT_SETTINGS_VALUES: Lazy<HashMap<&'static str, Value>> = Lazy::new(|| { | ||||
|     let mut map = HashMap::new(); | ||||
|     map.insert("displayed_attributes", json!(["*"])); | ||||
|     map.insert("searchable_attributes", json!(["*"])); | ||||
|     map.insert("localized_attributes", json!(null)); | ||||
|     map.insert("filterable_attributes", json!([])); | ||||
|     map.insert("distinct_attribute", json!(null)); | ||||
|     map.insert( | ||||
|         "ranking_rules", | ||||
|         json!(["words", "typo", "proximity", "attribute", "sort", "exactness"]), | ||||
|     ); | ||||
|     map.insert("stop_words", json!([])); | ||||
|     map.insert("non_separator_tokens", json!([])); | ||||
|     map.insert("separator_tokens", json!([])); | ||||
|     map.insert("dictionary", json!([])); | ||||
|     map.insert("synonyms", json!({})); | ||||
|     map.insert( | ||||
|         "faceting", | ||||
|         json!({ | ||||
|             "maxValuesPerFacet": json!(100), | ||||
|             "sortFacetValuesBy": { | ||||
|                 "*": "alpha" | ||||
| macro_rules! test_setting_routes { | ||||
|     ($({setting: $setting:ident, update_verb: $update_verb:ident, default_value: $default_value:tt},) *) => { | ||||
|         $( | ||||
|             mod $setting { | ||||
|                 use crate::common::Server; | ||||
|  | ||||
|                 #[actix_rt::test] | ||||
|                 async fn get_unexisting_index() { | ||||
|                     let server = Server::new().await; | ||||
|                     let url = format!("/indexes/test/settings/{}", | ||||
|                         stringify!($setting) | ||||
|                         .chars() | ||||
|                         .map(|c| if c == '_' { '-' } else { c }) | ||||
|                         .collect::<String>()); | ||||
|                     let (_response, code) = server.service.get(url).await; | ||||
|                     assert_eq!(code, 404); | ||||
|                 } | ||||
|  | ||||
|                 #[actix_rt::test] | ||||
|                 async fn update_unexisting_index() { | ||||
|                     let server = Server::new().await; | ||||
|                     let url = format!("/indexes/test/settings/{}", | ||||
|                         stringify!($setting) | ||||
|                         .chars() | ||||
|                         .map(|c| if c == '_' { '-' } else { c }) | ||||
|                         .collect::<String>()); | ||||
|                     let (response, code) = server.service.$update_verb(url, serde_json::Value::Null.into()).await; | ||||
|                     assert_eq!(code, 202, "{}", response); | ||||
|                     server.index("").wait_task(0).await; | ||||
|                     let (response, code) = server.index("test").get().await; | ||||
|                     assert_eq!(code, 200, "{}", response); | ||||
|                 } | ||||
|  | ||||
|                 #[actix_rt::test] | ||||
|                 async fn delete_unexisting_index() { | ||||
|                     let server = Server::new().await; | ||||
|                     let url = format!("/indexes/test/settings/{}", | ||||
|                         stringify!($setting) | ||||
|                         .chars() | ||||
|                         .map(|c| if c == '_' { '-' } else { c }) | ||||
|                         .collect::<String>()); | ||||
|                     let (_, code) = server.service.delete(url).await; | ||||
|                     assert_eq!(code, 202); | ||||
|                     let response = server.index("").wait_task(0).await; | ||||
|                     assert_eq!(response["status"], "failed"); | ||||
|                 } | ||||
|  | ||||
|                 #[actix_rt::test] | ||||
|                 async fn get_default() { | ||||
|                     let server = Server::new().await; | ||||
|                     let index = server.index("test"); | ||||
|                     let (response, code) = index.create(None).await; | ||||
|                     assert_eq!(code, 202, "{}", response); | ||||
|                     index.wait_task(0).await; | ||||
|                     let url = format!("/indexes/test/settings/{}", | ||||
|                         stringify!($setting) | ||||
|                         .chars() | ||||
|                         .map(|c| if c == '_' { '-' } else { c }) | ||||
|                         .collect::<String>()); | ||||
|                     let (response, code) = server.service.get(url).await; | ||||
|                     assert_eq!(code, 200, "{}", response); | ||||
|                     let expected = crate::json!($default_value); | ||||
|                     assert_eq!(expected, response); | ||||
|                 } | ||||
|             } | ||||
|         }), | ||||
|     ); | ||||
|     map.insert( | ||||
|         "pagination", | ||||
|         json!({ | ||||
|             "maxTotalHits": json!(1000), | ||||
|         }), | ||||
|     ); | ||||
|     map.insert("search_cutoff_ms", json!(null)); | ||||
|     map | ||||
| }); | ||||
|         )* | ||||
|  | ||||
|         #[actix_rt::test] | ||||
|         async fn all_setting_tested() { | ||||
|             let expected = std::collections::BTreeSet::from_iter(meilisearch::routes::indexes::settings::ALL_SETTINGS_NAMES.iter()); | ||||
|             let tested = std::collections::BTreeSet::from_iter([$(stringify!($setting)),*].iter()); | ||||
|             let diff: Vec<_> = expected.difference(&tested).collect(); | ||||
|             assert!(diff.is_empty(), "Not all settings were tested, please add the following settings to the `test_setting_routes!` macro: {:?}", diff); | ||||
|         } | ||||
|     }; | ||||
| } | ||||
|  | ||||
| test_setting_routes!( | ||||
|     { | ||||
|         setting: filterable_attributes, | ||||
|         update_verb: put, | ||||
|         default_value: [] | ||||
|     }, | ||||
|     { | ||||
|         setting: displayed_attributes, | ||||
|         update_verb: put, | ||||
|         default_value: ["*"] | ||||
|     }, | ||||
|     { | ||||
|         setting: localized_attributes, | ||||
|         update_verb: put, | ||||
|         default_value: null | ||||
|     }, | ||||
|     { | ||||
|         setting: searchable_attributes, | ||||
|         update_verb: put, | ||||
|         default_value: ["*"] | ||||
|     }, | ||||
|     { | ||||
|         setting: distinct_attribute, | ||||
|         update_verb: put, | ||||
|         default_value: null | ||||
|     }, | ||||
|     { | ||||
|         setting: stop_words, | ||||
|         update_verb: put, | ||||
|         default_value: [] | ||||
|     }, | ||||
|     { | ||||
|         setting: separator_tokens, | ||||
|         update_verb: put, | ||||
|         default_value: [] | ||||
|     }, | ||||
|     { | ||||
|         setting: non_separator_tokens, | ||||
|         update_verb: put, | ||||
|         default_value: [] | ||||
|     }, | ||||
|     { | ||||
|         setting: dictionary, | ||||
|         update_verb: put, | ||||
|         default_value: [] | ||||
|     }, | ||||
|     { | ||||
|         setting: ranking_rules, | ||||
|         update_verb: put, | ||||
|         default_value: ["words", "typo", "proximity", "attribute", "sort", "exactness"] | ||||
|     }, | ||||
|     { | ||||
|         setting: synonyms, | ||||
|         update_verb: put, | ||||
|         default_value: {} | ||||
|     }, | ||||
|     { | ||||
|         setting: pagination, | ||||
|         update_verb: patch, | ||||
|         default_value: {"maxTotalHits": 1000} | ||||
|     }, | ||||
|     { | ||||
|         setting: faceting, | ||||
|         update_verb: patch, | ||||
|         default_value: {"maxValuesPerFacet": 100, "sortFacetValuesBy": {"*": "alpha"}} | ||||
|     }, | ||||
|     { | ||||
|         setting: search_cutoff_ms, | ||||
|         update_verb: put, | ||||
|         default_value: null | ||||
|     }, | ||||
|     { | ||||
|         setting: embedders, | ||||
|         update_verb: patch, | ||||
|         default_value: null | ||||
|     }, | ||||
|     { | ||||
|         setting: facet_search, | ||||
|         update_verb: put, | ||||
|         default_value: true | ||||
|     }, | ||||
|     { | ||||
|         setting: prefix_search, | ||||
|         update_verb: put, | ||||
|         default_value: "indexingTime" | ||||
|     }, | ||||
|     { | ||||
|         setting: proximity_precision, | ||||
|         update_verb: put, | ||||
|         default_value: "byWord" | ||||
|     }, | ||||
|     { | ||||
|         setting: sortable_attributes, | ||||
|         update_verb: put, | ||||
|         default_value: [] | ||||
|     }, | ||||
|     { | ||||
|         setting: typo_tolerance, | ||||
|         update_verb: patch, | ||||
|         default_value: {"enabled": true, "minWordSizeForTypos": {"oneTypo": 5, "twoTypos": 9}, "disableOnWords": [], "disableOnAttributes": []} | ||||
|     }, | ||||
| ); | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn get_settings_unexisting_index() { | ||||
| @@ -56,7 +197,7 @@ async fn get_settings() { | ||||
|     let (response, code) = index.settings().await; | ||||
|     assert_eq!(code, 200); | ||||
|     let settings = response.as_object().unwrap(); | ||||
|     assert_eq!(settings.keys().len(), 17); | ||||
|     assert_eq!(settings.keys().len(), 19); | ||||
|     assert_eq!(settings["displayedAttributes"], json!(["*"])); | ||||
|     assert_eq!(settings["searchableAttributes"], json!(["*"])); | ||||
|     assert_eq!(settings["filterableAttributes"], json!([])); | ||||
| @@ -87,6 +228,8 @@ async fn get_settings() { | ||||
|     ); | ||||
|     assert_eq!(settings["proximityPrecision"], json!("byWord")); | ||||
|     assert_eq!(settings["searchCutoffMs"], json!(null)); | ||||
|     assert_eq!(settings["prefixSearch"], json!("indexingTime")); | ||||
|     assert_eq!(settings["facetSearch"], json!(true)); | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| @@ -199,7 +342,9 @@ async fn secrets_are_hidden_in_settings() { | ||||
|         } | ||||
|       }, | ||||
|       "searchCutoffMs": null, | ||||
|       "localizedAttributes": null | ||||
|       "localizedAttributes": null, | ||||
|       "facetSearch": true, | ||||
|       "prefixSearch": "indexingTime" | ||||
|     } | ||||
|     "###); | ||||
|  | ||||
| @@ -338,93 +483,6 @@ async fn error_update_setting_unexisting_index_invalid_uid() { | ||||
|     "###); | ||||
| } | ||||
|  | ||||
| macro_rules! test_setting_routes { | ||||
|     ($($setting:ident $write_method:ident), *) => { | ||||
|         $( | ||||
|             mod $setting { | ||||
|                 use crate::common::Server; | ||||
|                 use super::DEFAULT_SETTINGS_VALUES; | ||||
|  | ||||
|                 #[actix_rt::test] | ||||
|                 async fn get_unexisting_index() { | ||||
|                     let server = Server::new().await; | ||||
|                     let url = format!("/indexes/test/settings/{}", | ||||
|                         stringify!($setting) | ||||
|                         .chars() | ||||
|                         .map(|c| if c == '_' { '-' } else { c }) | ||||
|                         .collect::<String>()); | ||||
|                     let (_response, code) = server.service.get(url).await; | ||||
|                     assert_eq!(code, 404); | ||||
|                 } | ||||
|  | ||||
|                 #[actix_rt::test] | ||||
|                 async fn update_unexisting_index() { | ||||
|                     let server = Server::new().await; | ||||
|                     let url = format!("/indexes/test/settings/{}", | ||||
|                         stringify!($setting) | ||||
|                         .chars() | ||||
|                         .map(|c| if c == '_' { '-' } else { c }) | ||||
|                         .collect::<String>()); | ||||
|                     let (response, code) = server.service.$write_method(url, serde_json::Value::Null.into()).await; | ||||
|                     assert_eq!(code, 202, "{}", response); | ||||
|                     server.index("").wait_task(0).await; | ||||
|                     let (response, code) = server.index("test").get().await; | ||||
|                     assert_eq!(code, 200, "{}", response); | ||||
|                 } | ||||
|  | ||||
|                 #[actix_rt::test] | ||||
|                 async fn delete_unexisting_index() { | ||||
|                     let server = Server::new().await; | ||||
|                     let url = format!("/indexes/test/settings/{}", | ||||
|                         stringify!($setting) | ||||
|                         .chars() | ||||
|                         .map(|c| if c == '_' { '-' } else { c }) | ||||
|                         .collect::<String>()); | ||||
|                     let (_, code) = server.service.delete(url).await; | ||||
|                     assert_eq!(code, 202); | ||||
|                     let response = server.index("").wait_task(0).await; | ||||
|                     assert_eq!(response["status"], "failed"); | ||||
|                 } | ||||
|  | ||||
|                 #[actix_rt::test] | ||||
|                 async fn get_default() { | ||||
|                     let server = Server::new().await; | ||||
|                     let index = server.index("test"); | ||||
|                     let (response, code) = index.create(None).await; | ||||
|                     assert_eq!(code, 202, "{}", response); | ||||
|                     index.wait_task(0).await; | ||||
|                     let url = format!("/indexes/test/settings/{}", | ||||
|                         stringify!($setting) | ||||
|                         .chars() | ||||
|                         .map(|c| if c == '_' { '-' } else { c }) | ||||
|                         .collect::<String>()); | ||||
|                     let (response, code) = server.service.get(url).await; | ||||
|                     assert_eq!(code, 200, "{}", response); | ||||
|                     let expected = DEFAULT_SETTINGS_VALUES.get(stringify!($setting)).unwrap(); | ||||
|                     assert_eq!(expected, &response); | ||||
|                 } | ||||
|             } | ||||
|         )* | ||||
|     }; | ||||
| } | ||||
|  | ||||
| test_setting_routes!( | ||||
|     filterable_attributes put, | ||||
|     displayed_attributes put, | ||||
|     localized_attributes put, | ||||
|     searchable_attributes put, | ||||
|     distinct_attribute put, | ||||
|     stop_words put, | ||||
|     separator_tokens put, | ||||
|     non_separator_tokens put, | ||||
|     dictionary put, | ||||
|     ranking_rules put, | ||||
|     synonyms put, | ||||
|     pagination patch, | ||||
|     faceting patch, | ||||
|     search_cutoff_ms put | ||||
| ); | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn error_set_invalid_ranking_rules() { | ||||
|     let server = Server::new().await; | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| mod distinct; | ||||
| mod errors; | ||||
| mod get_settings; | ||||
| mod prefix_search_settings; | ||||
| mod proximity_settings; | ||||
| mod tokenizer_customization; | ||||
|   | ||||
							
								
								
									
										458
									
								
								crates/meilisearch/tests/settings/prefix_search_settings.rs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										458
									
								
								crates/meilisearch/tests/settings/prefix_search_settings.rs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,458 @@ | ||||
| use meili_snap::{json_string, snapshot}; | ||||
| use once_cell::sync::Lazy; | ||||
|  | ||||
| use crate::common::Server; | ||||
| use crate::json; | ||||
|  | ||||
| static DOCUMENTS: Lazy<crate::common::Value> = Lazy::new(|| { | ||||
|     json!([ | ||||
|         { | ||||
|             "id": 1, | ||||
|             "a": "Soup of the day", | ||||
|             "b": "manythefishou", | ||||
|         }, | ||||
|         { | ||||
|             "id": 2, | ||||
|             "a": "Soup of day so", | ||||
|             "b": "manythe manythelazyfish", | ||||
|         }, | ||||
|         { | ||||
|             "id": 3, | ||||
|             "a": "the Soup of day", | ||||
|             "b": "manythelazyfish", | ||||
|         }, | ||||
|     ]) | ||||
| }); | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn add_docs_and_disable() { | ||||
|     let server = Server::new().await; | ||||
|     let index = server.index("test"); | ||||
|  | ||||
|     let (response, _code) = index.add_documents(DOCUMENTS.clone(), None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "prefixSearch": "disabled", | ||||
|             "rankingRules": ["words", "typo", "proximity"], | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     // only 1 document should match | ||||
|     index | ||||
|         .search(json!({"q": "so", "attributesToHighlight": ["a", "b"]}), |response, code| { | ||||
|             snapshot!(code, @"200 OK"); | ||||
|             snapshot!(json_string!(response["hits"]), @r###" | ||||
|             [ | ||||
|               { | ||||
|                 "id": 2, | ||||
|                 "a": "Soup of day so", | ||||
|                 "b": "manythe manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "2", | ||||
|                   "a": "Soup of day <em>so</em>", | ||||
|                   "b": "manythe manythelazyfish" | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|             "###); | ||||
|         }) | ||||
|         .await; | ||||
|  | ||||
|     // only 1 document should match | ||||
|     index | ||||
|         .search(json!({"q": "manythe", "attributesToHighlight": ["a", "b"]}), |response, code| { | ||||
|             snapshot!(code, @"200 OK"); | ||||
|             snapshot!(json_string!(response["hits"]), @r###" | ||||
|             [ | ||||
|               { | ||||
|                 "id": 2, | ||||
|                 "a": "Soup of day so", | ||||
|                 "b": "manythe manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "2", | ||||
|                   "a": "Soup of day so", | ||||
|                   "b": "<em>manythe</em> manythelazyfish" | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|             "###); | ||||
|         }) | ||||
|         .await; | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn disable_and_add_docs() { | ||||
|     let server = Server::new().await; | ||||
|     let index = server.index("test"); | ||||
|  | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "prefixSearch": "disabled", | ||||
|             "rankingRules": ["words", "typo", "proximity"], | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, _code) = index.add_documents(DOCUMENTS.clone(), None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     // only 1 document should match | ||||
|     index | ||||
|         .search(json!({"q": "so", "attributesToHighlight": ["a", "b"]}), |response, code| { | ||||
|             snapshot!(code, @"200 OK"); | ||||
|             snapshot!(json_string!(response["hits"]), @r###" | ||||
|             [ | ||||
|               { | ||||
|                 "id": 2, | ||||
|                 "a": "Soup of day so", | ||||
|                 "b": "manythe manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "2", | ||||
|                   "a": "Soup of day <em>so</em>", | ||||
|                   "b": "manythe manythelazyfish" | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|             "###); | ||||
|         }) | ||||
|         .await; | ||||
|  | ||||
|     index | ||||
|         .search(json!({"q": "manythe", "attributesToHighlight": ["a", "b"]}), |response, code| { | ||||
|             snapshot!(code, @"200 OK"); | ||||
|             snapshot!(json_string!(response["hits"]), @r###" | ||||
|             [ | ||||
|               { | ||||
|                 "id": 2, | ||||
|                 "a": "Soup of day so", | ||||
|                 "b": "manythe manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "2", | ||||
|                   "a": "Soup of day so", | ||||
|                   "b": "<em>manythe</em> manythelazyfish" | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|             "###); | ||||
|         }) | ||||
|         .await; | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn disable_add_docs_and_enable() { | ||||
|     let server = Server::new().await; | ||||
|     let index = server.index("test"); | ||||
|  | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "prefixSearch": "disabled", | ||||
|             "rankingRules": ["words", "typo", "proximity"], | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, _code) = index.add_documents(DOCUMENTS.clone(), None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "prefixSearch": "indexingTime", | ||||
|             "rankingRules": ["words", "typo", "proximity"], | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(2).await; | ||||
|  | ||||
|     // all documents should match | ||||
|     index | ||||
|         .search(json!({"q": "so", "attributesToHighlight": ["a", "b"]}), |response, code| { | ||||
|             snapshot!(code, @"200 OK"); | ||||
|             snapshot!(json_string!(response["hits"]), @r###" | ||||
|             [ | ||||
|               { | ||||
|                 "id": 1, | ||||
|                 "a": "Soup of the day", | ||||
|                 "b": "manythefishou", | ||||
|                 "_formatted": { | ||||
|                   "id": "1", | ||||
|                   "a": "<em>So</em>up of the day", | ||||
|                   "b": "manythefishou" | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "id": 2, | ||||
|                 "a": "Soup of day so", | ||||
|                 "b": "manythe manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "2", | ||||
|                   "a": "<em>So</em>up of day <em>so</em>", | ||||
|                   "b": "manythe manythelazyfish" | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "id": 3, | ||||
|                 "a": "the Soup of day", | ||||
|                 "b": "manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "3", | ||||
|                   "a": "the <em>So</em>up of day", | ||||
|                   "b": "manythelazyfish" | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|             "###); | ||||
|         }) | ||||
|         .await; | ||||
|  | ||||
|     index | ||||
|         .search(json!({"q": "manythe", "attributesToHighlight": ["a", "b"]}), |response, code| { | ||||
|             snapshot!(code, @"200 OK"); | ||||
|             snapshot!(json_string!(response["hits"]), @r###" | ||||
|             [ | ||||
|               { | ||||
|                 "id": 1, | ||||
|                 "a": "Soup of the day", | ||||
|                 "b": "manythefishou", | ||||
|                 "_formatted": { | ||||
|                   "id": "1", | ||||
|                   "a": "Soup of the day", | ||||
|                   "b": "<em>manythe</em>fishou" | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "id": 2, | ||||
|                 "a": "Soup of day so", | ||||
|                 "b": "manythe manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "2", | ||||
|                   "a": "Soup of day so", | ||||
|                   "b": "<em>manythe</em> <em>manythe</em>lazyfish" | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "id": 3, | ||||
|                 "a": "the Soup of day", | ||||
|                 "b": "manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "3", | ||||
|                   "a": "the Soup of day", | ||||
|                   "b": "<em>manythe</em>lazyfish" | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|             "###); | ||||
|         }) | ||||
|         .await; | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn disable_add_docs_and_reset() { | ||||
|     let server = Server::new().await; | ||||
|     let index = server.index("test"); | ||||
|  | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "prefixSearch": "disabled", | ||||
|             "rankingRules": ["words", "typo", "proximity"], | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, _code) = index.add_documents(DOCUMENTS.clone(), None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "prefixSearch": serde_json::Value::Null, | ||||
|             "rankingRules": ["words", "typo", "proximity"], | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(2).await; | ||||
|  | ||||
|     // all documents should match | ||||
|     index | ||||
|         .search(json!({"q": "so", "attributesToHighlight": ["a", "b"]}), |response, code| { | ||||
|             snapshot!(code, @"200 OK"); | ||||
|             snapshot!(json_string!(response["hits"]), @r###" | ||||
|             [ | ||||
|               { | ||||
|                 "id": 1, | ||||
|                 "a": "Soup of the day", | ||||
|                 "b": "manythefishou", | ||||
|                 "_formatted": { | ||||
|                   "id": "1", | ||||
|                   "a": "<em>So</em>up of the day", | ||||
|                   "b": "manythefishou" | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "id": 2, | ||||
|                 "a": "Soup of day so", | ||||
|                 "b": "manythe manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "2", | ||||
|                   "a": "<em>So</em>up of day <em>so</em>", | ||||
|                   "b": "manythe manythelazyfish" | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "id": 3, | ||||
|                 "a": "the Soup of day", | ||||
|                 "b": "manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "3", | ||||
|                   "a": "the <em>So</em>up of day", | ||||
|                   "b": "manythelazyfish" | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|             "###); | ||||
|         }) | ||||
|         .await; | ||||
|  | ||||
|     index | ||||
|         .search(json!({"q": "manythe", "attributesToHighlight": ["a", "b"]}), |response, code| { | ||||
|             snapshot!(code, @"200 OK"); | ||||
|             snapshot!(json_string!(response["hits"]), @r###" | ||||
|             [ | ||||
|               { | ||||
|                 "id": 1, | ||||
|                 "a": "Soup of the day", | ||||
|                 "b": "manythefishou", | ||||
|                 "_formatted": { | ||||
|                   "id": "1", | ||||
|                   "a": "Soup of the day", | ||||
|                   "b": "<em>manythe</em>fishou" | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "id": 2, | ||||
|                 "a": "Soup of day so", | ||||
|                 "b": "manythe manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "2", | ||||
|                   "a": "Soup of day so", | ||||
|                   "b": "<em>manythe</em> <em>manythe</em>lazyfish" | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "id": 3, | ||||
|                 "a": "the Soup of day", | ||||
|                 "b": "manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "3", | ||||
|                   "a": "the Soup of day", | ||||
|                   "b": "<em>manythe</em>lazyfish" | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|             "###); | ||||
|         }) | ||||
|         .await; | ||||
| } | ||||
|  | ||||
| #[actix_rt::test] | ||||
| async fn default_behavior() { | ||||
|     let server = Server::new().await; | ||||
|     let index = server.index("test"); | ||||
|  | ||||
|     let (response, code) = index | ||||
|         .update_settings(json!({ | ||||
|             "rankingRules": ["words", "typo", "proximity"], | ||||
|         })) | ||||
|         .await; | ||||
|     assert_eq!("202", code.as_str(), "{:?}", response); | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     let (response, _code) = index.add_documents(DOCUMENTS.clone(), None).await; | ||||
|     index.wait_task(response.uid()).await; | ||||
|  | ||||
|     // all documents should match | ||||
|     index | ||||
|         .search(json!({"q": "so", "attributesToHighlight": ["a", "b"]}), |response, code| { | ||||
|             snapshot!(code, @"200 OK"); | ||||
|             snapshot!(json_string!(response["hits"]), @r###" | ||||
|             [ | ||||
|               { | ||||
|                 "id": 1, | ||||
|                 "a": "Soup of the day", | ||||
|                 "b": "manythefishou", | ||||
|                 "_formatted": { | ||||
|                   "id": "1", | ||||
|                   "a": "<em>So</em>up of the day", | ||||
|                   "b": "manythefishou" | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "id": 2, | ||||
|                 "a": "Soup of day so", | ||||
|                 "b": "manythe manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "2", | ||||
|                   "a": "<em>So</em>up of day <em>so</em>", | ||||
|                   "b": "manythe manythelazyfish" | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "id": 3, | ||||
|                 "a": "the Soup of day", | ||||
|                 "b": "manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "3", | ||||
|                   "a": "the <em>So</em>up of day", | ||||
|                   "b": "manythelazyfish" | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|             "###); | ||||
|         }) | ||||
|         .await; | ||||
|  | ||||
|     index | ||||
|         .search(json!({"q": "manythe", "attributesToHighlight": ["a", "b"]}), |response, code| { | ||||
|             snapshot!(code, @"200 OK"); | ||||
|             snapshot!(json_string!(response["hits"]), @r###" | ||||
|             [ | ||||
|               { | ||||
|                 "id": 1, | ||||
|                 "a": "Soup of the day", | ||||
|                 "b": "manythefishou", | ||||
|                 "_formatted": { | ||||
|                   "id": "1", | ||||
|                   "a": "Soup of the day", | ||||
|                   "b": "<em>manythe</em>fishou" | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "id": 2, | ||||
|                 "a": "Soup of day so", | ||||
|                 "b": "manythe manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "2", | ||||
|                   "a": "Soup of day so", | ||||
|                   "b": "<em>manythe</em> <em>manythe</em>lazyfish" | ||||
|                 } | ||||
|               }, | ||||
|               { | ||||
|                 "id": 3, | ||||
|                 "a": "the Soup of day", | ||||
|                 "b": "manythelazyfish", | ||||
|                 "_formatted": { | ||||
|                   "id": "3", | ||||
|                   "a": "the Soup of day", | ||||
|                   "b": "<em>manythe</em>lazyfish" | ||||
|                 } | ||||
|               } | ||||
|             ] | ||||
|             "###); | ||||
|         }) | ||||
|         .await; | ||||
| } | ||||
| @@ -79,7 +79,7 @@ async fn similar_bad_id() { | ||||
|     snapshot!(code, @"400 Bad Request"); | ||||
|     snapshot!(json_string!(response), @r###" | ||||
|     { | ||||
|       "message": "Invalid value at `.id`: the value of `id` is invalid. A document identifier can be of type integer or string, only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_), and can not be more than 512 bytes.", | ||||
|       "message": "Invalid value at `.id`: the value of `id` is invalid. A document identifier can be of type integer or string, only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_), and can not be more than 511 bytes.", | ||||
|       "code": "invalid_similar_id", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#invalid_similar_id" | ||||
| @@ -172,7 +172,7 @@ async fn similar_invalid_id() { | ||||
|     snapshot!(code, @"400 Bad Request"); | ||||
|     snapshot!(json_string!(response), @r###" | ||||
|     { | ||||
|       "message": "Invalid value at `.id`: the value of `id` is invalid. A document identifier can be of type integer or string, only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_), and can not be more than 512 bytes.", | ||||
|       "message": "Invalid value at `.id`: the value of `id` is invalid. A document identifier can be of type integer or string, only composed of alphanumeric characters (a-z A-Z 0-9), hyphens (-) and underscores (_), and can not be more than 511 bytes.", | ||||
|       "code": "invalid_similar_id", | ||||
|       "type": "invalid_request", | ||||
|       "link": "https://docs.meilisearch.com/errors#invalid_similar_id" | ||||
|   | ||||
| @@ -129,11 +129,11 @@ async fn perform_on_demand_snapshot() { | ||||
|  | ||||
|     index.load_test_set().await; | ||||
|  | ||||
|     server.index("doggo").create(Some("bone")).await; | ||||
|     index.wait_task(2).await; | ||||
|     let (task, _) = server.index("doggo").create(Some("bone")).await; | ||||
|     index.wait_task(task.uid()).await.succeeded(); | ||||
|  | ||||
|     server.index("doggo").create(Some("bone")).await; | ||||
|     index.wait_task(2).await; | ||||
|     let (task, _) = server.index("doggo").create(Some("bone")).await; | ||||
|     index.wait_task(task.uid()).await.failed(); | ||||
|  | ||||
|     let (task, code) = server.create_snapshot().await; | ||||
|     snapshot!(code, @"202 Accepted"); | ||||
|   | ||||
| @@ -448,7 +448,7 @@ async fn test_summarized_delete_documents_by_filter() { | ||||
|         "originalFilter": "\"doggo = bernese\"" | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Attribute `doggo` is not filterable. This index does not have configured filterable attributes.\n1:6 doggo = bernese", | ||||
|         "message": "Index `test`: Attribute `doggo` is not filterable. This index does not have configured filterable attributes.\n1:6 doggo = bernese", | ||||
|         "code": "invalid_document_filter", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_document_filter" | ||||
|   | ||||
| @@ -318,7 +318,7 @@ async fn try_to_disable_binary_quantization() { | ||||
|         } | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "`.embedders.manual.binaryQuantized`: Cannot disable the binary quantization.\n - Note: Binary quantization is a lossy operation that cannot be reverted.\n - Hint: Add a new embedder that is non-quantized and regenerate the vectors.", | ||||
|         "message": "Index `doggo`: `.embedders.manual.binaryQuantized`: Cannot disable the binary quantization.\n - Note: Binary quantization is a lossy operation that cannot be reverted.\n - Hint: Add a new embedder that is non-quantized and regenerate the vectors.", | ||||
|         "code": "invalid_settings_embedders", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_settings_embedders" | ||||
|   | ||||
| @@ -250,7 +250,7 @@ async fn user_provided_embeddings_error() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Bad embedder configuration in the document with id: `0`. Missing field `._vectors.manual.regenerate`\n  - note: `._vectors.manual` must be an array of floats, an array of arrays of floats, or an object with field `regenerate`", | ||||
|         "message": "Index `doggo`: Bad embedder configuration in the document with id: `0`. Missing field `._vectors.manual.regenerate`\n  - note: `._vectors.manual` must be an array of floats, an array of arrays of floats, or an object with field `regenerate`", | ||||
|         "code": "invalid_vectors_type", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_vectors_type" | ||||
| @@ -280,7 +280,7 @@ async fn user_provided_embeddings_error() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Bad embedder configuration in the document with id: `0`. Missing field `._vectors.manual.regenerate`\n  - note: `._vectors.manual` must be an array of floats, an array of arrays of floats, or an object with field `regenerate`", | ||||
|         "message": "Index `doggo`: Bad embedder configuration in the document with id: `0`. Missing field `._vectors.manual.regenerate`\n  - note: `._vectors.manual` must be an array of floats, an array of arrays of floats, or an object with field `regenerate`", | ||||
|         "code": "invalid_vectors_type", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_vectors_type" | ||||
| @@ -311,7 +311,7 @@ async fn user_provided_embeddings_error() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Bad embedder configuration in the document with id: `0`. Could not parse `._vectors.manual.regenerate`: invalid type: string \"yes please\", expected a boolean at line 1 column 26", | ||||
|         "message": "Index `doggo`: Bad embedder configuration in the document with id: `0`. Could not parse `._vectors.manual.regenerate`: invalid type: string \"yes please\", expected a boolean at line 1 column 26", | ||||
|         "code": "invalid_vectors_type", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_vectors_type" | ||||
| @@ -340,7 +340,7 @@ async fn user_provided_embeddings_error() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings`: expected null or an array, but found a boolean: `true`", | ||||
|         "message": "Index `doggo`: Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings`: expected null or an array, but found a boolean: `true`", | ||||
|         "code": "invalid_vectors_type", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_vectors_type" | ||||
| @@ -369,7 +369,7 @@ async fn user_provided_embeddings_error() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[0]`: expected a number or an array, but found a boolean: `true`", | ||||
|         "message": "Index `doggo`: Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[0]`: expected a number or an array, but found a boolean: `true`", | ||||
|         "code": "invalid_vectors_type", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_vectors_type" | ||||
| @@ -398,7 +398,7 @@ async fn user_provided_embeddings_error() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[0][0]`: expected a number, but found a boolean: `true`", | ||||
|         "message": "Index `doggo`: Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[0][0]`: expected a number, but found a boolean: `true`", | ||||
|         "code": "invalid_vectors_type", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_vectors_type" | ||||
| @@ -440,7 +440,7 @@ async fn user_provided_embeddings_error() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[1]`: expected a number, but found an array: `[0.2,0.3]`", | ||||
|         "message": "Index `doggo`: Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[1]`: expected a number, but found an array: `[0.2,0.3]`", | ||||
|         "code": "invalid_vectors_type", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_vectors_type" | ||||
| @@ -469,7 +469,7 @@ async fn user_provided_embeddings_error() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[1]`: expected an array, but found a number: `0.3`", | ||||
|         "message": "Index `doggo`: Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[1]`: expected an array, but found a number: `0.3`", | ||||
|         "code": "invalid_vectors_type", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_vectors_type" | ||||
| @@ -498,7 +498,7 @@ async fn user_provided_embeddings_error() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[0][1]`: expected a number, but found a boolean: `true`", | ||||
|         "message": "Index `doggo`: Bad embedder configuration in the document with id: `0`. Invalid value type at `._vectors.manual.embeddings[0][1]`: expected a number, but found a boolean: `true`", | ||||
|         "code": "invalid_vectors_type", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#invalid_vectors_type" | ||||
| @@ -539,7 +539,7 @@ async fn user_provided_vectors_error() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "While embedding documents for embedder `manual`: no vectors provided for document `40` and at least 4 other document(s)\n- Note: `manual` has `source: userProvided`, so documents must provide embeddings as an array in `_vectors.manual`.\n- Hint: opt-out for a document with `_vectors.manual: null`", | ||||
|         "message": "Index `doggo`: While embedding documents for embedder `manual`: no vectors provided for document `40` and at least 4 other document(s)\n- Note: `manual` has `source: userProvided`, so documents must provide embeddings as an array in `_vectors.manual`.\n- Hint: opt-out for a document with `_vectors.manual: null`", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -569,7 +569,7 @@ async fn user_provided_vectors_error() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "While embedding documents for embedder `manual`: no vectors provided for document `42`\n- Note: `manual` has `source: userProvided`, so documents must provide embeddings as an array in `_vectors.manual`.\n- Hint: try replacing `_vector` by `_vectors` in 1 document(s).", | ||||
|         "message": "Index `doggo`: While embedding documents for embedder `manual`: no vectors provided for document `42`\n- Note: `manual` has `source: userProvided`, so documents must provide embeddings as an array in `_vectors.manual`.\n- Hint: try replacing `_vector` by `_vectors` in 1 document(s).", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -599,7 +599,7 @@ async fn user_provided_vectors_error() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "While embedding documents for embedder `manual`: no vectors provided for document `42`\n- Note: `manual` has `source: userProvided`, so documents must provide embeddings as an array in `_vectors.manual`.\n- Hint: try replacing `_vectors.manaul` by `_vectors.manual` in 1 document(s).", | ||||
|         "message": "Index `doggo`: While embedding documents for embedder `manual`: no vectors provided for document `42`\n- Note: `manual` has `source: userProvided`, so documents must provide embeddings as an array in `_vectors.manual`.\n- Hint: try replacing `_vectors.manaul` by `_vectors.manual` in 1 document(s).", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
|   | ||||
| @@ -713,7 +713,7 @@ async fn bad_api_key() { | ||||
|         } | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "While embedding documents for embedder `default`: user error: could not authenticate against OpenAI server\n  - server replied with `{\"error\":{\"message\":\"Incorrect API key provided: Bearer doggo. You can find your API key at https://platform.openai.com/account/api-keys.\",\"type\":\"invalid_request_error\",\"param\":null,\"code\":\"invalid_api_key\"}}`\n  - Hint: Check the `apiKey` parameter in the embedder configuration, and the `MEILI_OPENAI_API_KEY` and `OPENAI_API_KEY` environment variables", | ||||
|         "message": "Index `doggo`: While embedding documents for embedder `default`: user error: could not authenticate against OpenAI server\n  - server replied with `{\"error\":{\"message\":\"Incorrect API key provided: Bearer doggo. You can find your API key at https://platform.openai.com/account/api-keys.\",\"type\":\"invalid_request_error\",\"param\":null,\"code\":\"invalid_api_key\"}}`\n  - Hint: Check the `apiKey` parameter in the embedder configuration, and the `MEILI_OPENAI_API_KEY` and `OPENAI_API_KEY` environment variables", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -757,7 +757,7 @@ async fn bad_api_key() { | ||||
|         } | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "While embedding documents for embedder `default`: user error: could not authenticate against OpenAI server\n  - server replied with `{\"error\":{\"message\":\"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\",\"type\":\"invalid_request_error\",\"param\":null,\"code\":null}}`\n  - Hint: Check the `apiKey` parameter in the embedder configuration, and the `MEILI_OPENAI_API_KEY` and `OPENAI_API_KEY` environment variables", | ||||
|         "message": "Index `doggo`: While embedding documents for embedder `default`: user error: could not authenticate against OpenAI server\n  - server replied with `{\"error\":{\"message\":\"You didn't provide an API key. You need to provide your API key in an Authorization header using Bearer auth (i.e. Authorization: Bearer YOUR_KEY), or as the password field (with blank username) if you're accessing the API from your browser and are prompted for a username and password. You can obtain an API key from https://platform.openai.com/account/api-keys.\",\"type\":\"invalid_request_error\",\"param\":null,\"code\":null}}`\n  - Hint: Check the `apiKey` parameter in the embedder configuration, and the `MEILI_OPENAI_API_KEY` and `OPENAI_API_KEY` environment variables", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
|   | ||||
| @@ -985,7 +985,7 @@ async fn bad_settings() { | ||||
|         } | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with runtime error: error extracting embeddings from the response:\n  - in `response`, while extracting a single \"{{embedding}}\", expected `response` to be an array of numbers, but failed to parse server response:\n  - invalid type: map, expected a sequence", | ||||
|         "message": "Index `doggo`: Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with runtime error: error extracting embeddings from the response:\n  - in `response`, while extracting a single \"{{embedding}}\", expected `response` to be an array of numbers, but failed to parse server response:\n  - invalid type: map, expected a sequence", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -1025,7 +1025,7 @@ async fn bad_settings() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "While embedding documents for embedder `rest`: runtime error: was expecting embeddings of dimension `2`, got embeddings of dimensions `3`", | ||||
|         "message": "Index `doggo`: While embedding documents for embedder `rest`: runtime error: was expecting embeddings of dimension `2`, got embeddings of dimensions `3`", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -1178,7 +1178,7 @@ async fn server_returns_bad_request() { | ||||
|         } | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with user error: sent a bad request to embedding server\n  - Hint: check that the `request` in the embedder configuration matches the remote server's API\n  - server replied with `{\"error\":\"Invalid request: invalid type: string \\\"test\\\", expected struct MultipleRequest at line 1 column 6\"}`", | ||||
|         "message": "Index `doggo`: Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with user error: sent a bad request to embedding server\n  - Hint: check that the `request` in the embedder configuration matches the remote server's API\n  - server replied with `{\"error\":\"Invalid request: invalid type: string \\\"test\\\", expected struct MultipleRequest at line 1 column 6\"}`", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -1247,7 +1247,7 @@ async fn server_returns_bad_request() { | ||||
|         "indexedDocuments": 0 | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "While embedding documents for embedder `rest`: user error: sent a bad request to embedding server\n  - Hint: check that the `request` in the embedder configuration matches the remote server's API\n  - server replied with `{\"error\":\"Invalid request: invalid type: string \\\"name: kefir\\\\n\\\", expected struct MultipleRequest at line 1 column 15\"}`", | ||||
|         "message": "Index `doggo`: While embedding documents for embedder `rest`: user error: sent a bad request to embedding server\n  - Hint: check that the `request` in the embedder configuration matches the remote server's API\n  - server replied with `{\"error\":\"Invalid request: invalid type: string \\\"name: kefir\\\\n\\\", expected struct MultipleRequest at line 1 column 15\"}`", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -1306,7 +1306,7 @@ async fn server_returns_bad_response() { | ||||
|         } | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with runtime error: error extracting embeddings from the response:\n  - in `response`, while extracting the array of \"{{embedding}}\"s, configuration expects `response` to be an array with at least 1 item(s) but server sent an object with 1 field(s)", | ||||
|         "message": "Index `doggo`: Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with runtime error: error extracting embeddings from the response:\n  - in `response`, while extracting the array of \"{{embedding}}\"s, configuration expects `response` to be an array with at least 1 item(s) but server sent an object with 1 field(s)", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -1362,7 +1362,7 @@ async fn server_returns_bad_response() { | ||||
|         } | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with runtime error: error extracting embeddings from the response:\n  - in `response`, while extracting item #0 from the array of \"{{embedding}}\"s, expected `response` to be an array of numbers, but failed to parse server response:\n  - invalid type: map, expected a sequence", | ||||
|         "message": "Index `doggo`: Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with runtime error: error extracting embeddings from the response:\n  - in `response`, while extracting item #0 from the array of \"{{embedding}}\"s, expected `response` to be an array of numbers, but failed to parse server response:\n  - invalid type: map, expected a sequence", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -1414,7 +1414,7 @@ async fn server_returns_bad_response() { | ||||
|         } | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with runtime error: error extracting embeddings from the response:\n  - in `response.output`, while extracting a single \"{{embedding}}\", expected `output` to be an array of numbers, but failed to parse server response:\n  - invalid type: map, expected f32", | ||||
|         "message": "Index `doggo`: Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with runtime error: error extracting embeddings from the response:\n  - in `response.output`, while extracting a single \"{{embedding}}\", expected `output` to be an array of numbers, but failed to parse server response:\n  - invalid type: map, expected f32", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -1478,7 +1478,7 @@ async fn server_returns_bad_response() { | ||||
|         } | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with runtime error: error extracting embeddings from the response:\n  - in `response.embedding`, while extracting item #0 from the array of \"{{embedding}}\"s, configuration expects `embedding` to be an object with key `data` but server sent an array of size 3", | ||||
|         "message": "Index `doggo`: Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with runtime error: error extracting embeddings from the response:\n  - in `response.embedding`, while extracting item #0 from the array of \"{{embedding}}\"s, configuration expects `embedding` to be an object with key `data` but server sent an array of size 3", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -1542,7 +1542,7 @@ async fn server_returns_bad_response() { | ||||
|         } | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with runtime error: error extracting embeddings from the response:\n  - in `response.output[0]`, while extracting a single \"{{embedding}}\", configuration expects key \"embeddings\", which is missing in response\n  - Hint: item #0 inside `output` has key `embedding`, did you mean `response.output[0].embedding` in embedder configuration?", | ||||
|         "message": "Index `doggo`: Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with runtime error: error extracting embeddings from the response:\n  - in `response.output[0]`, while extracting a single \"{{embedding}}\", configuration expects key \"embeddings\", which is missing in response\n  - Hint: item #0 inside `output` has key `embedding`, did you mean `response.output[0].embedding` in embedder configuration?", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -1908,7 +1908,7 @@ async fn server_custom_header() { | ||||
|         } | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with user error: could not authenticate against embedding server\n  - server replied with `{\"error\":\"missing header 'my-nonstandard-auth'\"}`\n  - Hint: Check the `apiKey` parameter in the embedder configuration", | ||||
|         "message": "Index `doggo`: Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with user error: could not authenticate against embedding server\n  - server replied with `{\"error\":\"missing header 'my-nonstandard-auth'\"}`\n  - Hint: Check the `apiKey` parameter in the embedder configuration", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -1951,7 +1951,7 @@ async fn server_custom_header() { | ||||
|         } | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with user error: could not authenticate against embedding server\n  - server replied with `{\"error\":\"thou shall not pass, Balrog\"}`\n  - Hint: Check the `apiKey` parameter in the embedder configuration", | ||||
|         "message": "Index `doggo`: Error while generating embeddings: runtime error: could not determine model dimensions:\n  - test embedding failed with user error: could not authenticate against embedding server\n  - server replied with `{\"error\":\"thou shall not pass, Balrog\"}`\n  - Hint: Check the `apiKey` parameter in the embedder configuration", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
| @@ -2099,7 +2099,7 @@ async fn searchable_reindex() { | ||||
|         ] | ||||
|       }, | ||||
|       "error": { | ||||
|         "message": "While embedding documents for embedder `rest`: error: received unexpected HTTP 404 from embedding server\n  - server replied with `{\"error\":\"text not found\",\"text\":\"breed: patou\\n\"}`", | ||||
|         "message": "Index `doggo`: While embedding documents for embedder `rest`: error: received unexpected HTTP 404 from embedding server\n  - server replied with `{\"error\":\"text not found\",\"text\":\"breed: patou\\n\"}`", | ||||
|         "code": "vector_embedding_error", | ||||
|         "type": "invalid_request", | ||||
|         "link": "https://docs.meilisearch.com/errors#vector_embedding_error" | ||||
|   | ||||
| @@ -10,12 +10,15 @@ license.workspace = true | ||||
|  | ||||
| [dependencies] | ||||
| anyhow = "1.0.86" | ||||
| arroy_v04_to_v05 = { package = "arroy", git = "https://github.com/meilisearch/arroy/", tag = "DO-NOT-DELETE-upgrade-v04-to-v05" } | ||||
| clap = { version = "4.5.9", features = ["derive"] } | ||||
| dump = { path = "../dump" } | ||||
| file-store = { path = "../file-store" } | ||||
| indexmap = {version = "2.7.0", features = ["serde"]} | ||||
| meilisearch-auth = { path = "../meilisearch-auth" } | ||||
| meilisearch-types = { path = "../meilisearch-types" } | ||||
| serde = { version = "1.0.209", features = ["derive"] } | ||||
| serde_json = {version = "1.0.133", features = ["preserve_order"]} | ||||
| tempfile = "3.14.0" | ||||
| time = { version = "0.3.36", features = ["formatting", "parsing", "alloc"] } | ||||
| uuid = { version = "1.10.0", features = ["v4"], default-features = false } | ||||
| arroy_v04_to_v05 = { package = "arroy", git = "https://github.com/meilisearch/arroy/", tag = "DO-NOT-DELETE-upgrade-v04-to-v05" } | ||||
|   | ||||
| @@ -73,7 +73,7 @@ enum Command { | ||||
|     /// | ||||
|     /// Supported upgrade paths: | ||||
|     /// | ||||
|     /// - v1.9.x -> v1.10.x -> v1.11.x | ||||
|     /// - v1.9.x -> v1.10.x -> v1.11.x -> v1.12.x | ||||
|     OfflineUpgrade { | ||||
|         #[arg(long)] | ||||
|         target_version: String, | ||||
|   | ||||
| @@ -1,13 +1,14 @@ | ||||
| mod v1_10; | ||||
| mod v1_11; | ||||
| mod v1_12; | ||||
| mod v1_9; | ||||
|  | ||||
| use std::path::{Path, PathBuf}; | ||||
|  | ||||
| use anyhow::{bail, Context}; | ||||
| use meilisearch_types::versioning::create_version_file; | ||||
|  | ||||
| use v1_10::v1_9_to_v1_10; | ||||
| use v1_12::v1_11_to_v1_12; | ||||
|  | ||||
| use crate::upgrade::v1_11::v1_10_to_v1_11; | ||||
|  | ||||
| @@ -22,6 +23,7 @@ impl OfflineUpgrade { | ||||
|         let upgrade_list = [ | ||||
|             (v1_9_to_v1_10 as fn(&Path) -> Result<(), anyhow::Error>, "1", "10", "0"), | ||||
|             (v1_10_to_v1_11, "1", "11", "0"), | ||||
|             (v1_11_to_v1_12, "1", "12", "0"), | ||||
|         ]; | ||||
|  | ||||
|         let (current_major, current_minor, current_patch) = &self.current_version; | ||||
| @@ -33,6 +35,7 @@ impl OfflineUpgrade { | ||||
|         ) { | ||||
|             ("1", "9", _) => 0, | ||||
|             ("1", "10", _) => 1, | ||||
|             ("1", "11", _) => 2, | ||||
|             _ => { | ||||
|                 bail!("Unsupported current version {current_major}.{current_minor}.{current_patch}. Can only upgrade from v1.9 and v1.10") | ||||
|             } | ||||
| @@ -43,6 +46,7 @@ impl OfflineUpgrade { | ||||
|         let ends_at = match (target_major.as_str(), target_minor.as_str(), target_patch.as_str()) { | ||||
|             ("1", "10", _) => 0, | ||||
|             ("1", "11", _) => 1, | ||||
|             ("1", "12", _) => 2, | ||||
|             (major, _, _) if major.starts_with('v') => { | ||||
|                 bail!("Target version must not starts with a `v`. Instead of writing `v1.9.0` write `1.9.0` for example.") | ||||
|             } | ||||
|   | ||||
| @@ -1,18 +1,13 @@ | ||||
| use anyhow::bail; | ||||
| use std::path::Path; | ||||
|  | ||||
| use anyhow::Context; | ||||
| use meilisearch_types::{ | ||||
|     heed::{ | ||||
|         types::{SerdeJson, Str}, | ||||
|         Database, Env, EnvOpenOptions, RoTxn, RwTxn, Unspecified, | ||||
|     }, | ||||
|     milli::index::{db_name, main_key}, | ||||
| }; | ||||
|  | ||||
| use crate::{try_opening_database, try_opening_poly_database, uuid_codec::UuidCodec}; | ||||
| use anyhow::{bail, Context}; | ||||
| use meilisearch_types::heed::types::{SerdeJson, Str}; | ||||
| use meilisearch_types::heed::{Database, Env, EnvOpenOptions, RoTxn, RwTxn, Unspecified}; | ||||
| use meilisearch_types::milli::index::{db_name, main_key}; | ||||
|  | ||||
| use super::v1_9; | ||||
| use crate::uuid_codec::UuidCodec; | ||||
| use crate::{try_opening_database, try_opening_poly_database}; | ||||
|  | ||||
| pub type FieldDistribution = std::collections::BTreeMap<String, u64>; | ||||
|  | ||||
|   | ||||
| @@ -7,12 +7,12 @@ | ||||
| use std::path::Path; | ||||
|  | ||||
| use anyhow::Context; | ||||
| use meilisearch_types::{ | ||||
|     heed::{types::Str, Database, EnvOpenOptions}, | ||||
|     milli::index::db_name, | ||||
| }; | ||||
| use meilisearch_types::heed::types::Str; | ||||
| use meilisearch_types::heed::{Database, EnvOpenOptions}; | ||||
| use meilisearch_types::milli::index::db_name; | ||||
|  | ||||
| use crate::{try_opening_database, try_opening_poly_database, uuid_codec::UuidCodec}; | ||||
| use crate::uuid_codec::UuidCodec; | ||||
| use crate::{try_opening_database, try_opening_poly_database}; | ||||
|  | ||||
| pub fn v1_10_to_v1_11(db_path: &Path) -> anyhow::Result<()> { | ||||
|     println!("Upgrading from v1.10.0 to v1.11.0"); | ||||
|   | ||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user