milli_core/
fields_ids_map.rs

1use std::collections::BTreeMap;
2
3use serde::{Deserialize, Serialize};
4
5use crate::FieldId;
6
7mod global;
8pub mod metadata;
9pub use global::GlobalFieldsIdsMap;
10
11#[derive(Debug, Clone, Serialize, Deserialize)]
12pub struct FieldsIdsMap {
13    names_ids: BTreeMap<String, FieldId>,
14    ids_names: BTreeMap<FieldId, String>,
15    next_id: Option<FieldId>,
16}
17
18impl FieldsIdsMap {
19    pub fn new() -> FieldsIdsMap {
20        FieldsIdsMap { names_ids: BTreeMap::new(), ids_names: BTreeMap::new(), next_id: Some(0) }
21    }
22
23    /// Returns the number of fields ids in the map.
24    pub fn len(&self) -> usize {
25        self.names_ids.len()
26    }
27
28    /// Returns `true` if the map is empty.
29    pub fn is_empty(&self) -> bool {
30        self.names_ids.is_empty()
31    }
32
33    /// Returns the field id related to a field name, it will create a new field id if the
34    /// name is not already known. Returns `None` if the maximum field id as been reached.
35    pub fn insert(&mut self, name: &str) -> Option<FieldId> {
36        match self.names_ids.get(name) {
37            Some(id) => Some(*id),
38            None => {
39                let id = self.next_id?;
40                self.next_id = id.checked_add(1);
41                self.names_ids.insert(name.to_owned(), id);
42                self.ids_names.insert(id, name.to_owned());
43                Some(id)
44            }
45        }
46    }
47
48    /// Get the ids of a field and all its nested fields based on its name.
49    pub fn nested_ids(&self, name: &str) -> Vec<FieldId> {
50        self.names_ids
51            .range(name.to_string()..)
52            .take_while(|(key, _)| key.starts_with(name))
53            .filter(|(key, _)| crate::is_faceted_by(key, name))
54            .map(|(_name, id)| *id)
55            .collect()
56    }
57
58    /// Get the id of a field based on its name.
59    pub fn id(&self, name: &str) -> Option<FieldId> {
60        self.names_ids.get(name).copied()
61    }
62
63    /// Get the name of a field based on its id.
64    pub fn name(&self, id: FieldId) -> Option<&str> {
65        self.ids_names.get(&id).map(String::as_str)
66    }
67
68    /// Remove a field name and id based on its name.
69    pub fn remove(&mut self, name: &str) -> Option<FieldId> {
70        match self.names_ids.remove(name) {
71            Some(id) => self.ids_names.remove_entry(&id).map(|(id, _)| id),
72            None => None,
73        }
74    }
75
76    /// Iterate over the ids and names in the ids order.
77    pub fn iter(&self) -> impl Iterator<Item = (FieldId, &str)> {
78        self.ids_names.iter().map(|(id, name)| (*id, name.as_str()))
79    }
80
81    /// Iterate over the ids in the order of the ids.
82    pub fn ids(&'_ self) -> impl Iterator<Item = FieldId> + '_ {
83        self.ids_names.keys().copied()
84    }
85
86    /// Iterate over the names in the order of the ids.
87    pub fn names(&self) -> impl Iterator<Item = &str> {
88        self.ids_names.values().map(AsRef::as_ref)
89    }
90}
91
92impl Default for FieldsIdsMap {
93    fn default() -> FieldsIdsMap {
94        FieldsIdsMap::new()
95    }
96}
97
98impl crate::documents::FieldIdMapper for FieldsIdsMap {
99    fn id(&self, name: &str) -> Option<FieldId> {
100        self.id(name)
101    }
102
103    fn name(&self, id: FieldId) -> Option<&str> {
104        self.name(id)
105    }
106}
107
108pub trait MutFieldIdMapper {
109    fn insert(&mut self, name: &str) -> Option<FieldId>;
110}
111
112impl MutFieldIdMapper for FieldsIdsMap {
113    fn insert(&mut self, name: &str) -> Option<FieldId> {
114        self.insert(name)
115    }
116}
117
118#[cfg(test)]
119mod tests {
120    use super::*;
121
122    #[test]
123    fn fields_ids_map() {
124        let mut map = FieldsIdsMap::new();
125
126        assert_eq!(map.insert("id"), Some(0));
127        assert_eq!(map.insert("title"), Some(1));
128        assert_eq!(map.insert("description"), Some(2));
129        assert_eq!(map.insert("id"), Some(0));
130        assert_eq!(map.insert("title"), Some(1));
131        assert_eq!(map.insert("description"), Some(2));
132
133        assert_eq!(map.id("id"), Some(0));
134        assert_eq!(map.id("title"), Some(1));
135        assert_eq!(map.id("description"), Some(2));
136        assert_eq!(map.id("date"), None);
137
138        assert_eq!(map.len(), 3);
139
140        assert_eq!(map.name(0), Some("id"));
141        assert_eq!(map.name(1), Some("title"));
142        assert_eq!(map.name(2), Some("description"));
143        assert_eq!(map.name(4), None);
144
145        assert_eq!(map.remove("title"), Some(1));
146
147        assert_eq!(map.id("title"), None);
148        assert_eq!(map.insert("title"), Some(3));
149        assert_eq!(map.len(), 3);
150
151        let mut iter = map.iter();
152        assert_eq!(iter.next(), Some((0, "id")));
153        assert_eq!(iter.next(), Some((2, "description")));
154        assert_eq!(iter.next(), Some((3, "title")));
155        assert_eq!(iter.next(), None);
156    }
157
158    #[test]
159    fn nested_fields() {
160        let mut map = FieldsIdsMap::new();
161
162        assert_eq!(map.insert("id"), Some(0));
163        assert_eq!(map.insert("doggo"), Some(1));
164        assert_eq!(map.insert("doggo.name"), Some(2));
165        assert_eq!(map.insert("doggolution"), Some(3));
166        assert_eq!(map.insert("doggo.breed.name"), Some(4));
167        assert_eq!(map.insert("description"), Some(5));
168
169        insta::assert_debug_snapshot!(map.nested_ids("doggo"), @r###"
170        [
171            1,
172            4,
173            2,
174        ]
175        "###);
176
177        insta::assert_debug_snapshot!(map.nested_ids("doggo.breed"), @r###"
178        [
179            4,
180        ]
181        "###);
182
183        insta::assert_debug_snapshot!(map.nested_ids("_vector"), @"[]");
184    }
185}