Refactor search result to include total count

This commit refactors the search result to include the total count of matches found, alongside the limited search results (page) for better pagination support. Changes made include:

- Modified the return type from `Vec<SearchResult>` to `SearchResults` structure, to include a total count.
- Updated the search function implementation to handle the new data structure.
- Adapted the test cases to account for the updated search result format.

In addition to the previous changes, this commit addresses the issue of not knowing the total number of search results when using pagination, which can now be easily obtained from the `SearchResults` structure.
This commit is contained in:
Nikita Galaiko 2023-03-23 14:31:43 +01:00
parent d2fdecc752
commit e4f1edfc83
6 changed files with 53 additions and 40 deletions

View File

@ -152,7 +152,7 @@ async fn search(
offset: Option<usize>,
timestamp_ms_gte: Option<u64>,
timestamp_ms_lt: Option<u64>,
) -> Result<Vec<search::SearchResult>, Error> {
) -> Result<search::SearchResults, Error> {
let app_state = handle.state::<App>();
let query = search::SearchQuery {

View File

@ -88,7 +88,7 @@ impl Deltas {
})
}
pub fn search(&self, query: &SearchQuery) -> Result<Vec<SearchResult>> {
pub fn search(&self, query: &SearchQuery) -> Result<SearchResults> {
search(&self.index, &self.reader, query)
}
@ -171,6 +171,13 @@ pub struct SearchResult {
pub highlighted: Vec<String>,
}
#[derive(Debug, Serialize)]
#[serde(rename_all = "camelCase")]
pub struct SearchResults {
pub page: Vec<SearchResult>,
pub total: usize,
}
fn index_session(
index: &tantivy::Index,
writer: &mut IndexWriter,
@ -292,7 +299,7 @@ pub fn search(
index: &tantivy::Index,
reader: &tantivy::IndexReader,
q: &SearchQuery,
) -> Result<Vec<SearchResult>> {
) -> Result<SearchResults> {
let query = tantivy::query::QueryParser::for_index(
index,
vec![
@ -311,12 +318,13 @@ pub fn search(
reader.reload()?;
let searcher = reader.searcher();
let top_docs = searcher.search(
&query,
&collector::TopDocs::with_limit(q.limit)
let mut collectors = collector::MultiCollector::new();
let top_docs_handle = collectors.add_collector(
collector::TopDocs::with_limit(q.limit)
.and_offset(q.offset.unwrap_or(0))
.order_by_u64_field(index.schema().get_field("timestamp_ms").unwrap()),
)?;
);
let count_handle = collectors.add_collector(collector::Count);
let snippet_generator = tantivy::SnippetGenerator::create(
&searcher,
@ -324,7 +332,11 @@ pub fn search(
index.schema().get_field("diff").unwrap(),
)?;
let results = top_docs
let mut result = searcher.search(&query, &collectors)?;
let count = count_handle.extract(&mut result);
let top_docs = top_docs_handle.extract(&mut result);
let page = top_docs
.iter()
.map(|(_score, doc_address)| {
let retrieved_doc = searcher.doc(*doc_address)?;
@ -366,5 +378,5 @@ pub fn search(
})
.collect::<Result<Vec<SearchResult>>>()?;
Ok(results)
Ok(SearchResults { page, total: count })
}

View File

@ -68,8 +68,8 @@ fn test_filter_by_timestamp() {
});
assert!(search_result_from.is_ok());
let search_result_from = search_result_from.unwrap();
assert_eq!(search_result_from.len(), 1);
assert_eq!(search_result_from[0].index, 2);
assert_eq!(search_result_from.total, 1);
assert_eq!(search_result_from.page[0].index, 2);
let search_result_to = searcher.search(&super::SearchQuery {
project_id: repository.project.id.clone(),
@ -80,8 +80,8 @@ fn test_filter_by_timestamp() {
});
assert!(search_result_to.is_ok());
let search_result_to = search_result_to.unwrap();
assert_eq!(search_result_to.len(), 1);
assert_eq!(search_result_to[0].index, 0);
assert_eq!(search_result_to.total, 1);
assert_eq!(search_result_to.page[0].index, 0);
let search_result_from_to = searcher.search(&super::SearchQuery {
project_id: repository.project.id.clone(),
@ -92,8 +92,8 @@ fn test_filter_by_timestamp() {
});
assert!(search_result_from_to.is_ok());
let search_result_from_to = search_result_from_to.unwrap();
assert_eq!(search_result_from_to.len(), 1);
assert_eq!(search_result_from_to[0].index, 1);
assert_eq!(search_result_from_to.total, 1);
assert_eq!(search_result_from_to.page[0].index, 1);
}
#[test]
@ -135,9 +135,9 @@ fn test_sorted_by_timestamp() {
assert!(search_result.is_ok());
let search_result = search_result.unwrap();
println!("{:?}", search_result);
assert_eq!(search_result.len(), 2);
assert_eq!(search_result[0].index, 1);
assert_eq!(search_result[1].index, 0);
assert_eq!(search_result.total, 2);
assert_eq!(search_result.page[0].index, 1);
assert_eq!(search_result.page[1].index, 0);
}
#[test]
@ -179,11 +179,11 @@ fn test_simple() {
println!("{:?}", search_result1);
assert!(search_result1.is_ok());
let search_result1 = search_result1.unwrap();
assert_eq!(search_result1.len(), 1);
assert_eq!(search_result1[0].session_id, session.id);
assert_eq!(search_result1[0].project_id, repository.project.id);
assert_eq!(search_result1[0].file_path, "test.txt");
assert_eq!(search_result1[0].index, 0);
assert_eq!(search_result1.total, 1);
assert_eq!(search_result1.page[0].session_id, session.id);
assert_eq!(search_result1.page[0].project_id, repository.project.id);
assert_eq!(search_result1.page[0].file_path, "test.txt");
assert_eq!(search_result1.page[0].index, 0);
let search_result2 = searcher.search(&super::SearchQuery {
project_id: repository.project.id.clone(),
@ -193,7 +193,7 @@ fn test_simple() {
range: Range { start: 0, end: 10 },
});
assert!(search_result2.is_ok());
let search_result2 = search_result2.unwrap();
let search_result2 = search_result2.unwrap().page;
assert_eq!(search_result2.len(), 1);
assert_eq!(search_result2[0].session_id, session.id);
assert_eq!(search_result2[0].project_id, repository.project.id);
@ -208,7 +208,7 @@ fn test_simple() {
range: Range { start: 0, end: 10 },
});
assert!(search_result3.is_ok());
let search_result3 = search_result3.unwrap();
let search_result3 = search_result3.unwrap().page;
assert_eq!(search_result3.len(), 2);
assert_eq!(search_result3[0].project_id, repository.project.id);
assert_eq!(search_result3[0].session_id, session.id);
@ -225,7 +225,7 @@ fn test_simple() {
range: Range { start: 0, end: 10 },
});
assert!(search_by_filename_result.is_ok());
let search_by_filename_result = search_by_filename_result.unwrap();
let search_by_filename_result = search_by_filename_result.unwrap().page;
assert_eq!(search_by_filename_result.len(), 2);
assert_eq!(search_by_filename_result[0].session_id, session.id);
assert_eq!(
@ -243,5 +243,5 @@ fn test_simple() {
});
assert!(not_found_result.is_ok());
let not_found_result = not_found_result.unwrap();
assert_eq!(not_found_result.len(), 0);
assert_eq!(not_found_result.total, 0);
}

View File

@ -1,6 +1,6 @@
mod deltas;
pub use deltas::{Deltas, SearchQuery, SearchResult};
pub use deltas::{Deltas, SearchQuery, SearchResults};
#[cfg(test)]
mod deltas_test;

View File

@ -1,17 +1,18 @@
import { invoke } from '@tauri-apps/api';
export type SearchResult = {
projectId: string;
sessionId: string;
filePath: string;
// index of the delta in the session.
index: number;
highlighted: string[]; // contains the highlighted text
projectId: string;
sessionId: string;
filePath: string;
// index of the delta in the session.
index: number;
// contains the highlighted text snippets.
highlighted: string[];
};
export const search = (params: {
projectId: string;
query: string;
limit?: number;
offset?: number;
}) => invoke<SearchResult[]>('search', params);
projectId: string;
query: string;
limit?: number;
offset?: number;
}) => invoke<{ total: number; page: SearchResult[] }>('search', params);

View File

@ -31,7 +31,7 @@
const fetchResults = async (projectId: string, query: string) => {
const results = await search({ projectId, query });
stopProcessing = false;
for (const result of results) {
for (const result of results.page) {
if (stopProcessing) {
processedResults = [];
stopProcessing = false;