indicium/simple/remove.rs
1// Conditionally select hash map type based on feature flags:
2#[cfg(feature = "gxhash")]
3type HashSet<T> = std::collections::HashSet<T, gxhash::GxBuildHasher>;
4
5#[cfg(feature = "ahash")]
6use ahash::HashSet;
7
8#[cfg(feature = "rustc-hash")]
9use rustc_hash::FxHashSet as HashSet;
10
11#[cfg(all(not(feature = "ahash"), not(feature = "gxhash"), not(feature = "rustc-hash")))]
12use std::collections::HashSet;
13
14// Static dependencies:
15use crate::simple::{indexable::Indexable, search_index::SearchIndex};
16use kstring::KString;
17
18// -----------------------------------------------------------------------------
19
20impl<K: Clone + Ord> SearchIndex<K> {
21 // -------------------------------------------------------------------------
22 //
23 /// Removes a key-value pair from the search index.
24 ///
25 /// Note that for the search results to be accurate, it is important to
26 /// update the search index as the collection is updated. If an element is
27 /// removed from your collection, it should also be removed from the search
28 /// index.
29 ///
30 /// Basic usage:
31 ///
32 /// ```rust
33 /// # use indicium::simple::{AutocompleteType, Indexable, SearchIndex, SearchType};
34 /// # use pretty_assertions::assert_eq;
35 /// #
36 /// # struct MyStruct {
37 /// # title: String,
38 /// # year: u16,
39 /// # body: String,
40 /// # }
41 /// #
42 /// # impl Indexable for MyStruct {
43 /// # fn strings(&self) -> Vec<String> {
44 /// # vec![
45 /// # self.title.clone(),
46 /// # self.year.to_string(),
47 /// # self.body.clone(),
48 /// # ]
49 /// # }
50 /// # }
51 /// #
52 /// # let my_vec = vec![
53 /// # MyStruct {
54 /// # title: "Harold Godwinson".to_string(),
55 /// # year: 1066,
56 /// # body: "Last crowned Anglo-Saxon king of England.".to_string(),
57 /// # },
58 /// # MyStruct {
59 /// # title: "Edgar Ætheling".to_string(),
60 /// # year: 1066,
61 /// # body: "Last male member of the royal house of Cerdic of Wessex.".to_string(),
62 /// # },
63 /// # MyStruct {
64 /// # title: "William the Conqueror".to_string(),
65 /// # year: 1066,
66 /// # body: "First Norman monarch of England.".to_string(),
67 /// # },
68 /// # MyStruct {
69 /// # title: "William Rufus".to_string(),
70 /// # year: 1087,
71 /// # body: "Third son of William the Conqueror.".to_string(),
72 /// # },
73 /// # MyStruct {
74 /// # title: "Henry Beauclerc".to_string(),
75 /// # year: 1100,
76 /// # body: "Fourth son of William the Conqueror.".to_string(),
77 /// # },
78 /// # ];
79 /// #
80 /// # let mut search_index: SearchIndex<usize> = SearchIndex::default();
81 /// #
82 /// # my_vec
83 /// # .iter()
84 /// # .enumerate()
85 /// # .for_each(|(index, element)|
86 /// # search_index.insert(&index, element)
87 /// # );
88 /// #
89 /// let search_results = search_index.search("last");
90 /// assert_eq!(search_results, vec![&0, &1]);
91 ///
92 /// search_index.remove(
93 /// &0,
94 /// &MyStruct {
95 /// title: "Harold Godwinson".to_string(),
96 /// year: 1066,
97 /// body: "Last crowned Anglo-Saxon king of England.".to_string(),
98 /// },
99 /// );
100 ///
101 /// let search_results = search_index.search("last");
102 /// assert_eq!(search_results, vec![&1]);
103 /// ```
104
105 #[tracing::instrument(level = "trace", name = "search index remove", skip(self, key, value))]
106 pub fn remove(&mut self, key: &K, value: &dyn Indexable) {
107 // Get all keywords for the `Indexable` record:
108 let mut keywords: HashSet<KString> = self.indexable_keywords(value);
109
110 // If `dump_keyword` feature is turned on, ensure that all records are
111 // detached from this special keyword:
112 if let Some(dump_keyword) = &self.dump_keyword {
113 keywords.insert(dump_keyword.as_ref().into());
114 } // if
115
116 // Iterate over the keywords:
117 for keyword in keywords {
118 // Attempt to get mutuable reference to the _keyword entry_ in
119 // the search index:
120 let is_empty = self.b_tree_map.get_mut(&keyword).is_some_and(|keys| {
121 // If keyword found in search index, remove the _key
122 // reference_ for this record from _keyword entry_:
123 keys.remove(key);
124 // Return whether the _keyword entry_ is now empty or not:
125 keys.is_empty()
126 }); // is_some_and
127
128 if is_empty {
129 self.b_tree_map.remove(&keyword);
130 } // if
131 } // for_each
132 } // fn
133} // impl