qdrant_edge/segment/index/
payload_config.rs1use std::collections::HashMap;
2use std::ops::{Deref, DerefMut};
3use std::path::{Path, PathBuf};
4
5use crate::common::fs::{atomic_save_json, read_json};
6use crate::common::universal_io::{OkNotFound, UniversalReadFs, read_json_via};
7use serde::{Deserialize, Serialize};
8
9use crate::segment::common::operation_error::OperationResult;
10use crate::segment::types::{PayloadFieldSchema, PayloadKeyType};
11
12pub const PAYLOAD_INDEX_CONFIG_FILE: &str = "config.json";
13
14#[derive(Debug, Default, Deserialize, Serialize, Clone)]
16pub struct PayloadConfig {
17 #[serde(flatten)]
19 pub indices: PayloadIndices,
20}
21
22impl PayloadConfig {
23 pub fn get_config_path(path: &Path) -> PathBuf {
24 path.join(PAYLOAD_INDEX_CONFIG_FILE)
25 }
26
27 pub fn load(path: &Path) -> OperationResult<Self> {
28 Ok(read_json(path)?)
29 }
30
31 pub fn load_universal<Fs: UniversalReadFs>(
34 fs: &Fs,
35 path: &Path,
36 ) -> OperationResult<Option<Self>> {
37 Ok(read_json_via(fs, path).ok_not_found()?)
38 }
39
40 pub fn save(&self, path: &Path) -> OperationResult<()> {
41 Ok(atomic_save_json(path, self)?)
42 }
43}
44
45#[derive(Debug, Default, Deserialize, Serialize, Clone)]
49#[serde(from = "PayloadIndicesStorage", into = "PayloadIndicesStorage")]
50pub struct PayloadIndices {
51 fields: HashMap<PayloadKeyType, PayloadFieldSchemaWithIndexType>,
52}
53
54impl PayloadIndices {
55 pub fn to_schemas(&self) -> HashMap<PayloadKeyType, PayloadFieldSchema> {
56 self.fields
57 .iter()
58 .map(|(field, index)| (field.clone(), index.schema.clone()))
59 .collect()
60 }
61}
62
63impl Deref for PayloadIndices {
64 type Target = HashMap<PayloadKeyType, PayloadFieldSchemaWithIndexType>;
65 fn deref(&self) -> &Self::Target {
66 &self.fields
67 }
68}
69
70impl DerefMut for PayloadIndices {
71 fn deref_mut(&mut self) -> &mut Self::Target {
72 &mut self.fields
73 }
74}
75
76#[derive(Deserialize, Serialize)]
81pub struct PayloadIndicesStorage {
82 pub indexed_fields: HashMap<PayloadKeyType, PayloadFieldSchema>,
84
85 #[serde(default, skip_serializing_if = "HashMap::is_empty")]
92 pub indexed_types: HashMap<PayloadKeyType, Vec<FullPayloadIndexType>>,
93}
94
95impl From<PayloadIndicesStorage> for PayloadIndices {
96 fn from(mut storage: PayloadIndicesStorage) -> Self {
97 let fields = storage
98 .indexed_fields
99 .into_iter()
100 .map(|(field, schema)| {
101 let index_types = storage.indexed_types.remove(&field).unwrap_or_default();
102 (
103 field,
104 PayloadFieldSchemaWithIndexType::new(schema, index_types),
105 )
106 })
107 .collect::<HashMap<_, _>>();
108 Self { fields }
109 }
110}
111
112impl From<PayloadIndices> for PayloadIndicesStorage {
113 fn from(storage: PayloadIndices) -> Self {
114 let (indexed_fields, indexed_types) = storage.fields.into_iter().fold(
115 (HashMap::new(), HashMap::new()),
116 |(mut fields, mut types), (field, schema)| {
117 fields.insert(field.clone(), schema.schema);
118 if !schema.types.is_empty() {
119 types.insert(field, schema.types);
120 }
121 (fields, types)
122 },
123 );
124 Self {
125 indexed_fields,
126 indexed_types,
127 }
128 }
129}
130
131#[derive(Debug, Clone, PartialEq)]
132pub struct PayloadFieldSchemaWithIndexType {
133 pub schema: PayloadFieldSchema,
134 pub types: Vec<FullPayloadIndexType>,
135}
136
137impl PayloadFieldSchemaWithIndexType {
138 pub fn new(schema: PayloadFieldSchema, types: Vec<FullPayloadIndexType>) -> Self {
139 Self { schema, types }
140 }
141}
142
143#[derive(Debug, Deserialize, Serialize, Clone, PartialEq, strum::EnumIter)]
144#[serde(rename_all = "snake_case")]
145pub enum PayloadIndexType {
146 IntIndex,
147 DatetimeIndex,
148 IntMapIndex,
149 KeywordIndex,
150 FloatIndex,
151 GeoIndex,
152 FullTextIndex,
153 BoolIndex,
154 UuidIndex,
155 UuidMapIndex,
156 NullIndex,
157}
158
159#[derive(Debug, Deserialize, Serialize, Clone, PartialEq)]
160pub struct FullPayloadIndexType {
161 pub index_type: PayloadIndexType,
162 pub mutability: IndexMutability,
163 pub storage_type: StorageType,
164}
165
166impl FullPayloadIndexType {
167 pub fn mutability(&self) -> &IndexMutability {
168 &self.mutability
169 }
170}
171
172#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)]
173#[serde(rename_all = "snake_case")]
174pub enum IndexMutability {
175 Mutable,
177 Immutable,
179}
180
181#[derive(Debug, Deserialize, Serialize, Clone, Copy, PartialEq)]
182#[serde(rename_all = "snake_case")]
183pub enum StorageType {
184 Gridstore,
185 Mmap { is_on_disk: bool },
186}
187
188#[cfg(test)]
189mod test {
190 use std::str::FromStr;
191
192 use serde_json::Value;
193
194 use super::*;
195 use crate::segment::json_path::JsonPath;
196
197 #[test]
198 fn test_storage_compatibility() {
199 let old = r#"{"indexed_fields":{"c":{"type":"integer","lookup":true,"range":false,"is_principal":false,"on_disk":false}}}"#;
202 let payload_config: PayloadConfig = serde_json::from_str(old).unwrap();
203
204 let old_value: Value = serde_json::from_str(old).unwrap();
205 let old_schema = old_value
206 .as_object()
207 .unwrap()
208 .get("indexed_fields")
209 .unwrap()
210 .get("c")
211 .unwrap()
212 .clone();
213
214 let old_config: PayloadFieldSchema = serde_json::from_value(old_schema).unwrap();
215 assert_eq!(
216 payload_config
217 .indices
218 .get(&JsonPath::from_str("c").unwrap())
219 .unwrap()
220 .schema,
221 old_config
222 );
223 }
224}