Repository: TabbyML/tabby
File: crates/tabby-index/src/indexer.rs (Rust)

Code before:
                        if doc.get_first(schema.field_chunk_id).is_none() {
                            let id = get_text(&doc, schema.field_id);
                            yield id.to_owned();
                        }
                    }
                    doc_id = postings.advance();
                }
            }
        }
    }

    pub fn is_indexed_after(&self, id: &str, time: chrono::DateTime<chrono::Utc>) -> bool {
        let schema = IndexSchema::instance();
        let query = schema.doc_indexed_after(&self.corpus, id, time);
        let Ok(docs) = self.searcher.search(&query, &TopDocs::with_limit(1)) else {
            return false;
        };

        !docs.is_empty()
    }

    /// Get the failed_chunks_count field for a document.
    /// tracks the number of embedding indexing failed chunks for a document.
    ///
    /// return 0 if the field is not found.
    pub fn failed_chunks_count(&self, id: &str) -> u64 {
        let schema = IndexSchema::instance();
        let query = schema.doc_query(&self.corpus, id);
        let Ok(docs) = self.searcher.search(&query, &TopDocs::with_limit(1)) else {
            return 0;
        };

        if let Some((_score, doc_address)) = docs.first() {
            if let Ok(doc) = self.searcher.doc::<TantivyDocument>(*doc_address) {
                if let Some(value) = doc
                    .get_first(schema.field_failed_chunks_count)
                    .and_then(|v| v.as_u64())
                {
                    return value;
                }
            }
        }

        0
    }
}

pub struct IndexGarbageCollector {
    searcher: Searcher,
    writer: IndexWriter,
}

Review comment (bug):
```suggestion
    pub fn has_failed_chunks(&self, id: &str) -> bool {
```

Code after:

                        // Skip chunks, as we only want to iterate over the main docs
                        if doc.get_first(schema.field_chunk_id).is_none() {
                            let id = get_text(&doc, schema.field_id);
                            yield id.to_owned();
                        }
                    }
                    doc_id = postings.advance();
                }
            }
        }
    }

    pub fn is_indexed_after(&self, id: &str, time: chrono::DateTime<chrono::Utc>) -> bool {
        let schema = IndexSchema::instance();
        let query = schema.doc_indexed_after(&self.corpus, id, time);
        let Ok(docs) = self.searcher.search(&query, &TopDocs::with_limit(1)) else {
            return false;
        };

        !docs.is_empty()
    }

    /// Get the failed_chunks_count field for a document.
    /// tracks the number of embedding indexing failed chunks for a document.
    ///
    /// return 0 if the field is not found.
    pub fn has_failed_chunks(&self, id: &str) -> bool {
        let schema = IndexSchema::instance();
        let query = schema.doc_has_failed_chunks(&self.corpus, id);
        let Ok(docs) = self.searcher.search(&query, &TopDocs::with_limit(1)) else {
            return false;
        };

        !docs.is_empty()
    }
}

pub struct IndexGarbageCollector {
    searcher: Searcher,
    writer: IndexWriter,
}

impl IndexGarbageCollector {
    pub fn new() -> Self {
        let doc = IndexSchema::instance();
        let (_, index) = open_or_create_index(&doc.schema, &path::index_dir());
        let writer = index
            .writer(150_000_000)
            .expect("Failed to create index writer");
        let reader = index.reader().expect("Failed to create index reader");