1#[cfg(feature = "native")]
11use crate::dsl::Schema;
12#[cfg(feature = "native")]
13use crate::error::Result;
14#[cfg(feature = "native")]
15use std::sync::Arc;
16
17mod searcher;
18pub use searcher::Searcher;
19
20#[cfg(feature = "native")]
21mod primary_key;
22#[cfg(feature = "native")]
23mod reader;
24#[cfg(feature = "native")]
25mod vector_builder;
26#[cfg(all(feature = "wasm", not(feature = "native")))]
27mod wasm_writer;
28#[cfg(feature = "native")]
29mod writer;
30#[cfg(feature = "native")]
31pub use primary_key::PrimaryKeyIndex;
32#[cfg(feature = "native")]
33pub use reader::IndexReader;
34#[cfg(all(feature = "wasm", not(feature = "native")))]
35pub use wasm_writer::IndexWriter as WasmIndexWriter;
36#[cfg(feature = "native")]
37pub use writer::{IndexWriter, PreparedCommit};
38
39mod metadata;
40pub use metadata::{FieldVectorMeta, INDEX_META_FILENAME, IndexMetadata, VectorIndexState};
41
42#[cfg(feature = "native")]
43mod helpers;
44#[cfg(feature = "native")]
45pub use helpers::{
46 IndexingStats, SchemaConfig, SchemaFieldConfig, create_index_at_path, create_index_from_sdl,
47 index_documents_from_reader, index_json_document, parse_schema,
48};
49
50pub const SLICE_CACHE_FILENAME: &str = "index.slicecache";
52
53#[derive(Debug, Clone)]
55pub struct IndexConfig {
56 pub num_threads: usize,
58 pub num_indexing_threads: usize,
60 pub num_compression_threads: usize,
62 pub term_cache_blocks: usize,
64 pub store_cache_blocks: usize,
66 pub max_indexing_memory_bytes: usize,
68 pub merge_policy: Box<dyn crate::merge::MergePolicy>,
70 pub optimization: crate::structures::IndexOptimization,
72 pub reload_interval_ms: u64,
74 pub max_concurrent_merges: usize,
76 #[cfg(feature = "native")]
80 pub background_merge_permits: Arc<tokio::sync::Semaphore>,
81 pub merge_bp_time_budget: Option<std::time::Duration>,
88 pub bp_memory_budget_bytes: usize,
95 #[cfg(feature = "native")]
101 pub background_reorder_permits: Arc<tokio::sync::Semaphore>,
102 #[cfg(feature = "native")]
106 pub background_reorder_pool: Option<Arc<rayon::ThreadPool>>,
107}
108
109impl Default for IndexConfig {
110 fn default() -> Self {
111 #[cfg(feature = "native")]
112 let indexing_threads = crate::default_indexing_threads();
113 #[cfg(not(feature = "native"))]
114 let indexing_threads = 1;
115
116 #[cfg(feature = "native")]
117 let compression_threads = crate::default_compression_threads();
118 #[cfg(not(feature = "native"))]
119 let compression_threads = 1;
120
121 Self {
122 num_threads: indexing_threads,
123 num_indexing_threads: 1, num_compression_threads: compression_threads,
125 term_cache_blocks: 256,
126 store_cache_blocks: 32,
127 max_indexing_memory_bytes: 256 * 1024 * 1024, merge_policy: Box::new(crate::merge::TieredMergePolicy::large_scale()),
133 optimization: crate::structures::IndexOptimization::default(),
134 reload_interval_ms: 1000, max_concurrent_merges: 4,
136 #[cfg(feature = "native")]
137 background_merge_permits: Arc::new(tokio::sync::Semaphore::new(4)),
138 merge_bp_time_budget: Some(std::time::Duration::from_secs(600)),
139 #[cfg(target_pointer_width = "64")]
148 bp_memory_budget_bytes: 24 * 1024 * 1024 * 1024,
149 #[cfg(not(target_pointer_width = "64"))]
150 bp_memory_budget_bytes: usize::MAX,
151 #[cfg(feature = "native")]
152 background_reorder_permits: Arc::new(tokio::sync::Semaphore::new(2)),
153 #[cfg(feature = "native")]
154 background_reorder_pool: None,
155 }
156 }
157}
158
159#[cfg(feature = "native")]
168pub struct Index<D: crate::directories::DirectoryWriter + 'static> {
169 directory: Arc<D>,
170 schema: Arc<Schema>,
171 config: IndexConfig,
172 segment_manager: Arc<crate::merge::SegmentManager<D>>,
174 cached_reader: tokio::sync::OnceCell<IndexReader<D>>,
176}
177
178#[cfg(feature = "native")]
179impl<D: crate::directories::DirectoryWriter + 'static> Index<D> {
180 pub async fn create(directory: D, schema: Schema, config: IndexConfig) -> Result<Self> {
182 let directory = Arc::new(directory);
183 let schema = Arc::new(schema);
184 directory.set_index_label(schema.index_label());
186 let metadata = IndexMetadata::new((*schema).clone());
187
188 let segment_manager = Arc::new(crate::merge::SegmentManager::new(
189 Arc::clone(&directory),
190 Arc::clone(&schema),
191 metadata,
192 config.merge_policy.clone_box(),
193 config.term_cache_blocks,
194 config.max_concurrent_merges,
195 Arc::clone(&config.background_merge_permits),
196 config.merge_bp_time_budget,
197 config.bp_memory_budget_bytes,
198 Arc::clone(&config.background_reorder_permits),
199 config.background_reorder_pool.clone(),
200 ));
201
202 segment_manager.update_metadata(|_| {}).await?;
204
205 Ok(Self {
206 directory,
207 schema,
208 config,
209 segment_manager,
210 cached_reader: tokio::sync::OnceCell::new(),
211 })
212 }
213
214 pub async fn open(directory: D, config: IndexConfig) -> Result<Self> {
216 let directory = Arc::new(directory);
217
218 let metadata = IndexMetadata::load(directory.as_ref()).await?;
220 let schema = Arc::new(metadata.schema.clone());
221 directory.set_index_label(schema.index_label());
223
224 let segment_manager = Arc::new(crate::merge::SegmentManager::new(
225 Arc::clone(&directory),
226 Arc::clone(&schema),
227 metadata,
228 config.merge_policy.clone_box(),
229 config.term_cache_blocks,
230 config.max_concurrent_merges,
231 Arc::clone(&config.background_merge_permits),
232 config.merge_bp_time_budget,
233 config.bp_memory_budget_bytes,
234 Arc::clone(&config.background_reorder_permits),
235 config.background_reorder_pool.clone(),
236 ));
237
238 segment_manager.load_and_publish_trained().await;
240
241 Ok(Self {
242 directory,
243 schema,
244 config,
245 segment_manager,
246 cached_reader: tokio::sync::OnceCell::new(),
247 })
248 }
249
250 pub fn schema(&self) -> &Schema {
252 &self.schema
253 }
254
255 pub fn schema_arc(&self) -> &Arc<Schema> {
257 &self.schema
258 }
259
260 pub fn directory(&self) -> &D {
262 &self.directory
263 }
264
265 pub fn segment_manager(&self) -> &Arc<crate::merge::SegmentManager<D>> {
267 &self.segment_manager
268 }
269
270 pub async fn reader(&self) -> Result<&IndexReader<D>> {
275 self.cached_reader
276 .get_or_try_init(|| async {
277 IndexReader::from_segment_manager(
278 Arc::clone(&self.schema),
279 Arc::clone(&self.segment_manager),
280 self.config.term_cache_blocks,
281 self.config.reload_interval_ms,
282 )
283 .await
284 })
285 .await
286 }
287
288 pub fn config(&self) -> &IndexConfig {
290 &self.config
291 }
292
293 pub async fn segment_readers(&self) -> Result<Vec<Arc<crate::segment::SegmentReader>>> {
295 let reader = self.reader().await?;
296 let searcher = reader.searcher().await?;
297 Ok(searcher.segment_readers().to_vec())
298 }
299
300 pub async fn num_docs(&self) -> Result<u32> {
302 let reader = self.reader().await?;
303 let searcher = reader.searcher().await?;
304 Ok(searcher.num_docs())
305 }
306
307 pub fn default_fields(&self) -> Vec<crate::Field> {
309 if !self.schema.default_fields().is_empty() {
310 self.schema.default_fields().to_vec()
311 } else {
312 self.schema
313 .fields()
314 .filter(|(_, entry)| {
315 entry.indexed && entry.field_type == crate::dsl::FieldType::Text
316 })
317 .map(|(field, _)| field)
318 .collect()
319 }
320 }
321
322 pub fn tokenizers(&self) -> Arc<crate::tokenizer::TokenizerRegistry> {
324 Arc::new(crate::tokenizer::TokenizerRegistry::default())
325 }
326
327 pub fn query_parser(&self) -> crate::dsl::QueryLanguageParser {
329 let default_fields = self.default_fields();
330 let tokenizers = self.tokenizers();
331
332 let query_routers = self.schema.query_routers();
333 if !query_routers.is_empty()
334 && let Ok(router) = crate::dsl::QueryFieldRouter::from_rules(query_routers)
335 {
336 return crate::dsl::QueryLanguageParser::with_router(
337 Arc::clone(&self.schema),
338 default_fields,
339 tokenizers,
340 router,
341 );
342 }
343
344 crate::dsl::QueryLanguageParser::new(Arc::clone(&self.schema), default_fields, tokenizers)
345 }
346
347 pub async fn query(
349 &self,
350 query_str: &str,
351 limit: usize,
352 ) -> Result<crate::query::SearchResponse> {
353 self.query_offset(query_str, limit, 0).await
354 }
355
356 pub async fn query_offset(
358 &self,
359 query_str: &str,
360 limit: usize,
361 offset: usize,
362 ) -> Result<crate::query::SearchResponse> {
363 let parser = self.query_parser();
364 let query = parser
365 .parse(query_str)
366 .map_err(crate::error::Error::Query)?;
367 self.search_offset(query.as_ref(), limit, offset).await
368 }
369
370 pub async fn search(
372 &self,
373 query: &dyn crate::query::Query,
374 limit: usize,
375 ) -> Result<crate::query::SearchResponse> {
376 self.search_offset(query, limit, 0).await
377 }
378
379 pub async fn search_offset(
381 &self,
382 query: &dyn crate::query::Query,
383 limit: usize,
384 offset: usize,
385 ) -> Result<crate::query::SearchResponse> {
386 let reader = self.reader().await?;
387 let searcher = reader.searcher().await?;
388
389 #[cfg(feature = "sync")]
390 let (results, total_seen) = {
391 let runtime_flavor = tokio::runtime::Handle::current().runtime_flavor();
395 if runtime_flavor == tokio::runtime::RuntimeFlavor::MultiThread {
396 tokio::task::block_in_place(|| {
397 searcher.search_with_offset_and_count_sync(query, limit, offset)
398 })?
399 } else {
400 searcher.search_with_offset_and_count_sync(query, limit, offset)?
401 }
402 };
403
404 #[cfg(not(feature = "sync"))]
405 let (results, total_seen) = {
406 searcher
407 .search_with_offset_and_count(query, limit, offset)
408 .await?
409 };
410
411 let total_hits = total_seen;
412 let hits: Vec<crate::query::SearchHit> = results
413 .into_iter()
414 .map(|result| crate::query::SearchHit {
415 address: crate::query::DocAddress::new(result.segment_id, result.doc_id),
416 score: result.score,
417 matched_fields: result.extract_ordinals(),
418 })
419 .collect();
420
421 Ok(crate::query::SearchResponse { hits, total_hits })
422 }
423
424 pub async fn get_document(
426 &self,
427 address: &crate::query::DocAddress,
428 ) -> Result<Option<crate::dsl::Document>> {
429 let reader = self.reader().await?;
430 let searcher = reader.searcher().await?;
431 searcher.get_document(address).await
432 }
433
434 pub async fn get_postings(
436 &self,
437 field: crate::Field,
438 term: &[u8],
439 ) -> Result<
440 Vec<(
441 Arc<crate::segment::SegmentReader>,
442 crate::structures::BlockPostingList,
443 )>,
444 > {
445 let segments = self.segment_readers().await?;
446 let mut results = Vec::new();
447
448 for segment in segments {
449 if let Some(postings) = segment.get_postings(field, term).await? {
450 results.push((segment, postings));
451 }
452 }
453
454 Ok(results)
455 }
456}
457
458#[cfg(feature = "native")]
460impl<D: crate::directories::DirectoryWriter + 'static> Index<D> {
461 pub fn writer(&self) -> writer::IndexWriter<D> {
463 writer::IndexWriter::from_index(self)
464 }
465}
466
467#[cfg(test)]
468mod tests;
469
470