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