1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
use std::hash::Hash;
// -----------------------------------------------------------------------------
impl<K: Hash + Ord> crate::simple::search_index::SearchIndex<K> {
/// This search function will return keys as the search results. Each
/// resulting key can then be used to retrieve the full record from its
/// collection. _This search method only accepts a single keyword as the
/// search string._ Search keywords must be an exact match.
///
/// The search string is expected to only contain a single keyword. This is
/// the lightest and fastest type. It is good for compact interfaces, where
/// records are very simple, or data-sets are quite small. Results are
/// returned in lexographic order.
///
/// Search only supports exact keyword matches and does not use fuzzy
/// matching. Consider providing the `autocomplete` feature to your users as
/// an ergonomic alternative to fuzzy matching.
///
/// Basic usage:
///
/// ```ignore
/// # use indicium::simple::{AutocompleteType, Indexable, SearchIndex, SearchType};
/// # use pretty_assertions::assert_eq;
/// #
/// # struct MyStruct {
/// # title: String,
/// # year: u16,
/// # body: String,
/// # }
/// #
/// # impl Indexable for MyStruct {
/// # fn strings(&self) -> Vec<String> {
/// # vec![
/// # self.title.clone(),
/// # self.year.to_string(),
/// # self.body.clone(),
/// # ]
/// # }
/// # }
/// #
/// # let my_vec = vec![
/// # MyStruct {
/// # title: "Harold Godwinson".to_string(),
/// # year: 1066,
/// # body: "Last crowned Anglo-Saxon king of England.".to_string(),
/// # },
/// # MyStruct {
/// # title: "Edgar Ætheling".to_string(),
/// # year: 1066,
/// # body: "Last male member of the royal house of Cerdic of Wessex.".to_string(),
/// # },
/// # MyStruct {
/// # title: "William the Conqueror".to_string(),
/// # year: 1066,
/// # body: "First Norman monarch of England.".to_string(),
/// # },
/// # MyStruct {
/// # title: "William Rufus".to_string(),
/// # year: 1087,
/// # body: "Third son of William the Conqueror.".to_string(),
/// # },
/// # MyStruct {
/// # title: "Henry Beauclerc".to_string(),
/// # year: 1100,
/// # body: "Fourth son of William the Conqueror.".to_string(),
/// # },
/// # ];
/// #
/// # let mut search_index: SearchIndex<usize> = SearchIndex::default();
/// #
/// # my_vec
/// # .iter()
/// # .enumerate()
/// # .for_each(|(index, element)|
/// # search_index.insert(&index, element)
/// # );
/// #
/// let search_results = search_index.search_keyword(&20, "Wessex");
///
/// assert_eq!(
/// // Convert `BTreeMap<&K>` to `Vec<&K>` for comparison:
/// search_results.into_iter().collect::<Vec<&usize>>(),
/// vec![&1]
/// );
/// ```
//
// Note: This function is a variation of the `internal_keyword_search`
// function. If this function is modified, it is likely the
// `internal_keyword_search` function should be updated also.
//
// The difference between these two functions is that `keyword_search`
// observes `maximum_search_results`, while `internal_keyword_search` does
// not.
#[tracing::instrument(level = "trace", name = "keyword search", skip(self))]
#[allow(clippy::option_if_let_else)] // `map_or_else` is illegible
pub(crate) fn search_keyword(
&self,
maximum_search_results: usize,
keyword: &str
) -> Vec<&K> {
// If the search index is set to be case insensitive, normalize the
// keyword to lower-case:
let keyword = self.normalize(keyword);
// For debug builds:
#[cfg(debug_assertions)]
tracing::debug!("searching: {}", keyword);
// Attempt to get matching keys for the search keyword from BTreeMap:
match self.b_tree_map.get(&kstring::KString::from_ref(&keyword)) {
Some(keys) => keys
// Iterate over all matching keys and only return
// `maximum_search_results` number of keys:
.iter()
// Only return `maximum_search_results` number of keys:
.take(maximum_search_results)
// Insert a reference to each resulting key into the hash set:
.collect(),
None => Vec::new(),
} // match
} // fn
} // impl