green_barrel/
migration.rs

1//! Migrations are Green Barrel’s way of
2//! propagating changes you make to
3//! your models (adding a field, deleting a collection, etc.) into
4//! your database schema.
5
6use chrono::Utc;
7use futures::stream::TryStreamExt;
8use mongodb::{
9    bson::{
10        de::from_document,
11        doc,
12        document::Document,
13        ser::{to_bson, to_document},
14        Bson,
15    },
16    Client, Database,
17};
18use regex::Regex;
19use serde::{Deserialize, Serialize};
20use std::{collections::HashMap, error::Error, path::Path};
21
22use crate::{
23    models::helpers::{FileData, ImageData},
24    store::METADATA,
25};
26
27// MIGRATION
28// #################################################################################################
29/// For creation and updating of a technical database.
30#[derive(Serialize, Deserialize)]
31pub struct ModelState {
32    pub database: String,
33    pub collection: String,
34    pub fields: Vec<String>,
35    pub field_type_map: HashMap<String, String>,
36    pub status: bool,
37}
38
39/// For monitoring the state of models.
40pub struct Monitor<'a> {
41    pub app_name: &'a str,
42    pub unique_app_key: &'a str,
43    pub model_key_list: Vec<String>,
44}
45
46impl<'a> Monitor<'a> {
47    /// Get the name of the technical database for a project.
48    // *********************************************************************************************
49    pub fn green_tech_name(&self) -> Result<String, Box<dyn Error>> {
50        // app_name Validation.
51        // Valid characters: _ a-z A-Z 0-9
52        // Max size: 20
53        let re = Regex::new(r"^[a-zA-Z][_a-zA-Z\d]{1,20}$")?;
54        if !re.is_match(self.app_name) {
55            Err("app_name => \
56                    Valid characters: _ a-z A-Z 0-9 and \
57                    Max size: 20 ; \
58                    First character: a-z A-Z")?
59        }
60        // UNIQUE_PROJECT_KEY Validation.
61        // UNIQUE_PROJECT_KEY - It is recommended not to change.
62        // Valid characters: a-z A-Z 0-9
63        // Size: 16
64        // Example: "7rzgacfqQB3B7q7T"
65        let re = Regex::new(r"^[a-zA-Z\d]{16}$")?;
66        if !re.is_match(self.unique_app_key) {
67            Err("UNIQUE_PROJECT_KEY => \
68                    Valid characters: a-z A-Z 0-9 and \
69                    Size: 16.")?
70        }
71        //
72        Ok(format!(
73            "green_tech__{}__{}",
74            self.app_name, self.unique_app_key
75        ))
76    }
77
78    /// Refresh models state.
79    // *********************************************************************************************
80    ///
81    /// ```
82    /// if {
83    ///     If there is no technical database, it will be created.
84    /// } else {
85    ///     Resets the Model's status to `false`.
86    /// }
87    /// ```
88    ///
89    async fn refresh(&self, client: &Client) -> Result<(), Box<dyn Error>> {
90        // Get the name of the technical database for a project.
91        let db_green_tech: String = self.green_tech_name()?;
92        // Collection for monitoring the state of Models.
93        let collection_models_name: &str = "monitor_models";
94        // Used to store selection items, for
95        // Fields type like selectTextDyn, selectTextMultDyn, etc.
96        let collection_dyn_fields_type: &str = "dynamic_fields";
97        //Get a list of databases.
98        let database_names = client.list_database_names(None, None).await?;
99        // Create a technical database for the project if it doesn't exist.
100        if !database_names.contains(&db_green_tech) {
101            // Create a collection for models.
102            client
103                .database(&db_green_tech)
104                .create_collection(collection_models_name, None)
105                .await?;
106            // Create a collection for fields types of `select`.
107            // (selectTextDyn, selectTextMultDyn, etc.)
108            client
109                .database(&db_green_tech)
110                .create_collection(collection_dyn_fields_type, None)
111                .await?;
112        } else {
113            // Reset models state information.
114            let green_tech_db = client.database(&db_green_tech);
115            let collection_models = green_tech_db.collection::<Document>(collection_models_name);
116            let mut cursor = collection_models.find(None, None).await?;
117            while let Some(doc) = cursor.try_next().await? {
118                let mut model_state = from_document::<ModelState>(doc)?;
119                model_state.status = false;
120                let query = doc! {
121                    "database": &model_state.database,
122                    "collection": &model_state.collection
123                };
124                let update = doc! {"$set": to_document(&model_state)?};
125                collection_models.update_one(query, update, None).await?;
126            }
127        }
128        //
129        Ok(())
130    }
131
132    /// Reorganize databases state
133    /// (full delete of orphaned collections and databases)
134    // *********************************************************************************************
135    async fn napalm(&self, client: &Client) -> Result<(), Box<dyn Error>> {
136        // Get the name of the technical database for a project.
137        let db_green_tech: String = self.green_tech_name()?;
138        let collection_models_name: &str = "monitor_models";
139        let collection_dyn_fields_type: &str = "dynamic_fields";
140        let green_tech_db: Database = client.database(&db_green_tech);
141        let collection_models = green_tech_db.collection::<Document>(collection_models_name);
142        let collection_dyn_fields =
143            green_tech_db.collection::<Document>(collection_dyn_fields_type);
144        // Delete orphaned Collections.
145        let mut cursor = collection_models.find(None, None).await?;
146        while let Some(doc) = cursor.try_next().await? {
147            let model_state: ModelState = from_document(doc)?;
148            if !model_state.status {
149                // Delete Collection (left without a model).
150                client
151                    .database(&model_state.database)
152                    .collection::<Document>(&model_state.collection)
153                    .drop(None)
154                    .await?;
155                // Delete a document with a record about the state of
156                // the model from the technical base.
157                let query: Document = doc! {
158                    "database": &model_state.database,
159                    "collection": &model_state.collection
160                };
161                collection_models.delete_one(query.clone(), None).await?;
162                collection_dyn_fields.delete_one(query, None).await?;
163            }
164        }
165        //
166        Ok(())
167    }
168
169    /// Migrating Models
170    // *********************************************************************************************
171    /// Check the changes in the models and (if necessary) apply to the database.
172    pub async fn migrat(&self, client: &Client) -> Result<(), Box<dyn Error>> {
173        // Run refresh models state.
174        self.refresh(client).await?;
175        for model_key in self.model_key_list.iter() {
176            // Get metadata of Model
177            let meta = {
178                // Get metadata store.
179                let metadata = METADATA.lock().await;
180                // Get metadata of Model.
181                if let Some(meta) = metadata.get(model_key) {
182                    meta.clone()
183                } else {
184                    Err(format!(
185                        "Model key: `{model_key}` ; Method: `migrat()` => \
186                    Failed to get data from cache.",
187                    ))?
188                }
189            };
190            if !meta.is_add_doc {
191                continue;
192            }
193            // Service_name validation.
194            if !Regex::new(r"^[_a-zA-Z][_a-zA-Z\d]{1,30}$")
195                .unwrap()
196                .is_match(meta.service_name.as_str())
197            {
198                Err(format!(
199                    "Model: `{}` > SERVICE_NAME => \
200                        Valid characters: _ a-z A-Z 0-9 \
201                        ; Max size: 30 \
202                        ; First character: _ a-z A-Z",
203                    meta.model_name
204                ))?;
205            }
206            // Database name validation.
207            if !Regex::new(r"^[_a-zA-Z][_a-zA-Z\d]{14,61}$")
208                .unwrap()
209                .is_match(meta.database_name.as_str())
210            {
211                Err(format!(
212                    "Model: `{}` > DATABASE_NAME => \
213                        Valid characters: _ a-z A-Z 0-9 \
214                        ; Max size: 20 \
215                        ; First character: _ a-z A-Z",
216                    meta.model_name
217                ))?;
218            }
219            //
220            let fields_name = &meta.fields_name;
221            let ignore_fields = &meta.ignore_fields;
222            // List field names without `hash` and ignored fields.
223            let trunc_fields_name_list = fields_name
224                .iter()
225                .filter(|item| *item != "hash" && !ignore_fields.contains(*item))
226                .collect::<Vec<&String>>();
227            // Get the name of the technical database for a project.
228            let db_green_tech: String = self.green_tech_name()?;
229            let database_names: Vec<String> = client.list_database_names(None, None).await?;
230            // Map of default values and value types from `value (default)` attribute -
231            // <field_name, (field_type, value)>
232            let default_value_map: HashMap<String, serde_json::Value> =
233                meta.default_value_map.clone();
234            // Get map of fields types.
235            let field_type_map = &meta.field_type_map;
236            // Get truncated map of fields types.
237            let trunc_field_type_map = field_type_map.clone();
238            trunc_field_type_map
239                .clone()
240                .retain(|item, _| item != "hash" && !ignore_fields.contains(item));
241            // Get a map of fields type from the technical database,
242            // from the `monitor_models` collection for current Model.
243            let monitor_field_type_map: HashMap<String, String>;
244
245            // Check the field changes in the Model and (if required)
246            // update documents in the current Collection.
247            // -------------------------------------------------------------------------------------
248            // Get a list of current model field names from the technical database
249            // `green_barrel_keyword`.
250            let filter: Document = doc! {
251                "database": &meta.database_name,
252                "collection": &meta.collection_name
253            };
254            let model: Option<Document> = client
255                .database(&db_green_tech)
256                .collection("monitor_models")
257                .find_one(filter, None)
258                .await?;
259            if let Some(model) = model {
260                // Get a list of fields from the technical database,
261                // from the `monitor_models` collection for current Model.
262                let monitor_models_fields_name: Vec<String> = {
263                    let fields: Vec<Bson> = model.get_array("fields")?.to_vec();
264                    fields
265                        .into_iter()
266                        .map(|item| item.as_str().unwrap().to_string())
267                        .collect()
268                };
269                // Get a map of fields type from the technical database,
270                // from the `monitor_models` collection for current Model.
271                monitor_field_type_map = {
272                    model
273                        .get_document("field_type_map")
274                        .unwrap()
275                        .iter()
276                        .map(|item| (item.0.clone(), item.1.as_str().unwrap().to_string()))
277                        .collect()
278                };
279                // Check if the set of fields in the collection of
280                // the current Model needs to be updated.
281                let mut changed_fields: Vec<&str> = Vec::new();
282                for field in trunc_fields_name_list.iter() {
283                    if !monitor_models_fields_name.contains(&field.to_string())
284                        || (trunc_field_type_map.get(*field).unwrap()
285                            != monitor_field_type_map.get(*field).unwrap_or(&String::new()))
286                    {
287                        changed_fields.push(field);
288                    }
289                }
290                // Start (if necessary) updating the set of fields in the current collection.
291                if !changed_fields.is_empty() {
292                    // Get the database and collection of the current Model.
293                    let db: Database = client.database(&meta.database_name);
294                    let collection = db.collection::<Document>(&meta.collection_name);
295                    // Get cursor to all documents of the current Model.
296                    let mut cursor = collection.find(None, None).await?;
297                    // Iterate through all documents in a current (model) collection.
298                    while let Some(doc_from_db) = cursor.try_next().await? {
299                        // Create temporary blank document.
300                        let mut tmp_doc = Document::new();
301                        // Loop over all fields of the model.
302                        for (field_name, field_type) in field_type_map.iter() {
303                            if field_name == "hash" || ignore_fields.contains(field_name) {
304                                continue;
305                            }
306                            // Insert the reserved fields.
307                            if field_name == "created_at" || field_name == "updated_at" {
308                                if doc_from_db.contains_key(field_name) {
309                                    let value_from_db: Option<&Bson> = doc_from_db.get(field_name);
310                                    if let Some(value_from_db) = value_from_db {
311                                        tmp_doc.insert(field_name.to_string(), value_from_db);
312                                    } else {
313                                        Err(format!(
314                                            "Service: `{}` > Model: `{}` ; \
315                                                Method: `migrat()` => \
316                                                Cannot get field value from database for \
317                                                field `{}`.",
318                                            meta.service_name, meta.model_name, field_name
319                                        ))?
320                                    }
321                                } else {
322                                    Err(format!(
323                                        "Service: `{}` > Model: `{}` ; Method: `migrat()` => \
324                                            Key `{}` was not found in the document from \
325                                            the database.",
326                                        meta.service_name, meta.model_name, field_name
327                                    ))?
328                                }
329                                //
330                                continue;
331                            }
332                            // If the field exists, get its value.
333                            if !changed_fields.contains(&field_name.as_str()) {
334                                let value_from_db: Option<&Bson> = doc_from_db.get(field_name);
335                                if let Some(value_from_db) = value_from_db {
336                                    tmp_doc.insert(field_name.to_string(), value_from_db);
337                                } else {
338                                    Err(format!(
339                                        "Service: `{}` > Model: `{}` > Field: `{}` ; \
340                                            Method: `migrat()` => \
341                                            Can't get field value from database.",
342                                        meta.service_name, meta.model_name, field_name
343                                    ))?;
344                                }
345                            } else {
346                                // If no field exists, get default value.
347                                let default_value = default_value_map.get(field_name).unwrap();
348                                tmp_doc.insert(
349                                    field_name.clone(),
350                                    match field_type.as_str() {
351                                         "ColorField" | "EmailField" | "PasswordField" | "PhoneField"| "TextField" 
352                                        | "URLField" | "IPField"  |"ChoiceTextField" | "SlugField" => {
353                                            if !default_value.is_null() {
354                                                Bson::String(default_value.as_str().unwrap().to_string())
355                                            } else {
356                                                Bson::Null
357                                            }
358                                        }
359                                        "DateField" => {
360                                           if !default_value.is_null() {
361                                                let val = format!("{}T00:00",default_value.as_str().unwrap());
362                                                if let Ok(ndt) = chrono::NaiveDateTime::parse_from_str( &val, "%Y-%m-%dT%H:%M")
363                                                {
364                                                    let dt = chrono::DateTime::<Utc>::from_naive_utc_and_offset(ndt,Utc);
365                                                    Bson::DateTime(dt.into())
366                                                } else {
367                                                    Err(format!("Service: `{}` > Model: `{}` ; \
368                                                        Method: `migrat()` => \
369                                                        Incorrect date format. \
370                                                        Example: 1970-02-28",
371                                                        meta.service_name, meta.model_name
372                                                    ))?
373                                                }
374                                            } else {
375                                                Bson::Null
376                                            }
377                                        }
378                                        "DateTimeField" | "HiddenDateTimeField" => {
379                                            if !default_value.is_null() {
380                                                let val = default_value.as_str().unwrap();
381                                                if let Ok(ndt) = chrono::NaiveDateTime::parse_from_str( val, "%Y-%m-%dT%H:%M")
382                                                {
383                                                    let dt = chrono::DateTime::<Utc>::from_naive_utc_and_offset(ndt,Utc);
384                                                    Bson::DateTime(dt.into())
385                                                } else {
386                                                    Err(format!("Service: `{}` > Model: `{}` ; \
387                                                        Method: `migrat()` => \
388                                                        Incorrect date and time format. \
389                                                        Example: 1970-02-28T00:00",
390                                                        meta.service_name, meta.model_name
391                                                    ))?
392                                                }
393                                            } else {
394                                                Bson::Null
395                                            }
396                                        }
397                                        "I32Field" | "ChoiceI32Field" => {
398                                            if !default_value.is_null() {
399                                                Bson::Int32(
400                                                    i32::try_from(default_value.as_i64().unwrap())?
401                                                )
402                                            } else {
403                                                Bson::Null
404                                            }
405                                        }
406                                        "U32Field" | "ChoiceU32Field" | "I64Field" | "ChoiceI64Field" => {
407                                            if !default_value.is_null() {
408                                                Bson::Int64(
409                                                    default_value.as_i64().unwrap()
410                                                )
411                                            } else {
412                                                Bson::Null
413                                            }
414                                        }
415                                        "F64Field" | "ChoiceF64Field" => {
416                                            if !default_value.is_null() {
417                                                Bson::Double(
418                                                   default_value.as_f64().unwrap()
419                                                )
420                                            } else {
421                                                Bson::Null
422                                            }
423                                        }
424                                        "BoolField" => {
425                                            if !default_value.is_null() {
426                                                Bson::Boolean(
427                                                    default_value.as_bool().unwrap()
428                                                )
429                                            } else {
430                                                Bson::Boolean(false)
431                                            }
432                                        }
433                                        "FileField" => {
434                                            if !default_value.is_null() {
435                                                let mut file_data = serde_json::from_value::<FileData>(default_value.clone())?;
436                                                // Define flags to check.
437                                                if file_data.path.is_empty() || file_data.url.is_empty() {
438                                                    Err(format!("Model: `{}` > Field: `{}` ; Method: \
439                                                        `migrat()` => Check the `path` and `url` \
440                                                        attributes in the `default` field parameter.",
441                                                        meta.model_name, field_name)
442                                                    )?
443                                                }
444                                                // Create path for validation of file.
445                                                let path: String = file_data.path.clone();
446                                                let f_path = Path::new(path.as_str());
447                                                if !f_path.is_file() {
448                                                    Err(format!("Model: `{}` > Field: `{}` ; \
449                                                    Method: `migrat()` => File is missing - {}",
450                                                        meta.model_name, field_name, path)
451                                                    )?
452                                                }
453                                                // Get file metadata.
454                                                let metadata = f_path.metadata()?;
455                                                // Get file size in bytes.
456                                                file_data.size = metadata.len() as f64;
457                                                // Get file name.
458                                                file_data.name = f_path.file_name().unwrap().to_str().unwrap().to_string();
459                                                // Create doc.
460                                                let result = to_document(&file_data)?;
461                                                Bson::Document(result)
462                                            } else {
463                                                Bson::Null
464                                            }
465                                        }
466                                        "ImageField" => {
467                                            if !default_value.is_null() {
468                                                let mut file_data = serde_json::from_value::<ImageData>(default_value.clone())?;
469                                                // Define flags to check.
470                                                if file_data.path.is_empty() || file_data.url.is_empty() {
471                                                    Err(format!("Model: `{}` > Field: `{}` ; Method: \
472                                                        `migrat()` => Check the `path` and `url` \
473                                                        attributes in the `default` field parameter.",
474                                                        meta.model_name, field_name
475                                                    ))?
476                                                }
477                                                // Create path for validation of file.
478                                                let path: String = file_data.path.clone();
479                                                let f_path = Path::new(path.as_str());
480                                                if !f_path.is_file() {
481                                                    Err(format!("Model: `{}` > Field: `{}` ; Method: \
482                                                            `migrat()` => Image is missing - {}",
483                                                        meta.model_name, field_name, path
484                                                    ))?
485                                                }
486                                                // Get file metadata.
487                                                let metadata = f_path.metadata()?;
488                                                // Get file size in bytes.
489                                                file_data.size = metadata.len() as f64;
490                                                // Get file name.
491                                                file_data.name = f_path.file_name().unwrap().to_str().unwrap().to_string();
492                                                // Get image width and height.
493                                                let dimensions = image::image_dimensions(path)?;
494                                                file_data.width = dimensions.0 as f64;
495                                                file_data.height = dimensions.1 as f64;
496                                                // Create doc.
497                                                let result = to_document(&file_data)?;
498                                                Bson::Document(result)
499                                            } else {
500                                                Bson::Null
501                                            }
502                                        }
503                                        "ChoiceTextMultField" => {
504                                            if !default_value.is_null() {
505                                                let val = serde_json::from_value::<Vec<String>>(default_value.clone())?
506                                                    .iter().map(|item| Bson::String(item.clone()))
507                                                    .collect::<Vec<Bson>>();
508                                                Bson::Array(val)
509                                            } else {
510                                                Bson::Null
511                                            }
512                                        }
513                                        "ChoiceI32MultField" => {
514                                            if !default_value.is_null() {
515                                                let val = serde_json::from_value::<Vec<i32>>(default_value.clone())?
516                                                    .iter().map(|item| Bson::Int32(*item))
517                                                    .collect::<Vec<Bson>>();
518                                                Bson::Array(val)
519                                            } else {
520                                                Bson::Null
521                                            }
522                                        }
523                                         "ChoiceU32MultField" | "ChoiceI64MultField"  => {
524                                            if !default_value.is_null() {
525                                                let val = serde_json::from_value::<Vec<i64>>(default_value.clone())?
526                                                    .iter().map(|item| mongodb::bson::Bson::Int64(*item))
527                                                    .collect::<Vec<Bson>>();
528                                                Bson::Array(val)
529                                            } else {
530                                                Bson::Null
531                                            }
532                                        }
533                                        "ChoiceF64MultField" => {
534                                            if !default_value.is_null() {
535                                                let val = serde_json::from_value::<Vec<f64>>(default_value.clone())?
536                                                    .iter().map(|item| Bson::Double(*item))
537                                                    .collect::<Vec<Bson>>();
538                                                Bson::Array(val)
539                                            } else {
540                                                Bson::Null
541                                            }
542                                        }
543                                        "ChoiceTextDynField" | "ChoiceTextMultDynField" | "ChoiceI32DynField"
544                                        | "ChoiceI32MultDynField" | "ChoiceU32DynField" | "ChoiceU32MultDynField"
545                                        | "ChoiceI64DynField" | "ChoiceI64MultDynField" | "ChoiceF64DynField"
546                                        | "ChoiceF64MultDynField" => {
547                                            Bson::Null
548                                        }
549                                        _ => {
550                                            Err(format!("Service: `{}` > Model: `{}` ; \
551                                                    Method: `migrat()` => Invalid Field type.",
552                                                meta.service_name, meta.model_name
553                                            ))?
554                                        }
555                                    },
556                                );
557                            }
558                        }
559                        // Save updated document.
560                        let query = doc! {"_id": doc_from_db.get_object_id("_id")?};
561                        collection
562                            .update_one(query, doc! {"$set": tmp_doc}, None)
563                            .await?;
564                    }
565                }
566            } else {
567                monitor_field_type_map = HashMap::new();
568            }
569
570            // Create a new database (if doesn't exist) and add new collection.
571            // -------------------------------------------------------------------------------------
572            // Get the database for the current collection of Model.
573            let db: Database = client.database(&meta.database_name);
574            // If there is no collection for the current Model, create it.
575            if !database_names.contains(&meta.database_name)
576                || !db
577                    .list_collection_names(None)
578                    .await?
579                    .contains(&meta.collection_name)
580            {
581                db.create_collection(&meta.collection_name, None).await?;
582            }
583
584            // Get the technical database `db_green_tech` for the current model.
585            // -------------------------------------------------------------------------------------
586            let db: Database = client.database(&db_green_tech);
587
588            // Update the state of models for `models::Monitor`.
589            // -------------------------------------------------------------------------------------
590            // Check if there is a technical database of the project, if not, causes panic.
591            if !database_names.contains(&db_green_tech)
592                || !db
593                    .list_collection_names(None)
594                    .await?
595                    .contains(&"monitor_models".to_owned())
596            {
597                Err("In the `refresh()` method, \
598                        no technical database has been created for the project.")?
599            } else {
600                let collection = db.collection::<Document>("monitor_models");
601                let filter = doc! {
602                    "database": &meta.database_name,
603                    "collection": &meta.collection_name
604                };
605                let doc: Document = mongodb::bson::doc! {
606                    "database": &meta.database_name,
607                    "collection": &meta.collection_name,
608                    "fields": trunc_fields_name_list.iter().map(|item| item.to_string())
609                        .collect::<Vec<String>>(),
610                    "field_type_map": to_bson(&trunc_field_type_map.clone())?,
611                    "status": true
612                };
613                // Check if there is model state in the database.
614                if collection.count_documents(filter.clone(), None).await? == 0 {
615                    // Add model state information.
616                    collection.insert_one(doc, None).await?;
617                } else {
618                    // Full update model state information.
619                    collection
620                        .update_one(filter, doc! {"$set": doc}, None)
621                        .await?;
622                }
623            }
624
625            // Document management to support model fields with dynamic fields type.
626            // -------------------------------------------------------------------------------------
627            // Check if there is a technical database of the project, if not, causes panic.
628            if !database_names.contains(&db_green_tech)
629                || !db
630                    .list_collection_names(None)
631                    .await?
632                    .contains(&"dynamic_fields".to_owned())
633            {
634                Err("In the `refresh()` method, \
635                        no technical database has been created for the project.")?
636            }
637            //
638            let collection = db.collection::<Document>("dynamic_fields");
639            let filter = doc! {
640                "database": &meta.database_name,
641                "collection": &meta.collection_name
642            };
643            // Check if there is a document in the database for
644            // storing the values of dynamic fields type of model.
645            if collection.count_documents(filter.clone(), None).await? == 0 {
646                // Init new document.
647                let mut new_doc = doc! {
648                    "database": &meta.database_name,
649                    "collection": &meta.collection_name,
650                    "fields": {}
651                };
652                // Add empty arrays to the new document.
653                let mut fields_doc = Document::new();
654                for (field_name, field_type) in field_type_map.clone() {
655                    if field_type.contains("Dyn") {
656                        fields_doc.insert(field_name, Bson::Array(Vec::new()));
657                    }
658                }
659                // Insert new document.
660                new_doc.insert("fields".to_string(), fields_doc);
661                collection.insert_one(new_doc, None).await?;
662            } else {
663                // Get an existing document.
664                let mut exist_doc = collection.find_one(filter.clone(), None).await?.unwrap();
665                // Get a document with `dynamic_fields` fields.
666                let fields_doc = exist_doc.get_document_mut("fields")?;
667                // Get a list of fields from the technical database,
668                // from the `dynamic_fields` collection for current Model.
669                let dyn_fields_from_db: Vec<String> =
670                    fields_doc.keys().map(|item| item.into()).collect();
671                // Create an empty list for fields with dynamic field types.
672                let mut dyn_fields_from_model: Vec<String> = Vec::new();
673                // Add new (if any) fields in `fields_doc`.
674                for (field_name, field_type) in trunc_field_type_map.clone() {
675                    if field_type.contains("Dyn") {
676                        dyn_fields_from_model.push(field_name.clone());
677                        // If the new field or fields type do not match,
678                        // initialize with an empty array.
679                        if !dyn_fields_from_db.contains(&field_name)
680                            || (field_type
681                                != *monitor_field_type_map
682                                    .get(field_name.as_str())
683                                    .unwrap_or(&String::new()))
684                        {
685                            fields_doc.insert(field_name, Bson::Array(Vec::new()));
686                        }
687                    }
688                }
689                // Remove orphaned fields.
690                for field_name in dyn_fields_from_db {
691                    if !dyn_fields_from_model.contains(&field_name) {
692                        fields_doc.remove(&field_name).unwrap();
693                    }
694                }
695                // Full update existing document.
696                collection
697                    .update_one(filter, doc! {"$set":exist_doc}, None)
698                    .await?;
699            }
700        }
701        // Run reorganize databases state.
702        self.napalm(client).await?;
703        //
704        Ok(())
705    }
706}