subgraph/data_sources/mongo/services/find_many/
mod.rs

1use async_graphql::{futures_util::StreamExt, Error, ErrorExtensions};
2use bson::{doc, Document};
3use log::{debug, error, trace};
4use mongodb::Database;
5
6use crate::{
7    data_sources::mongo::{EagerLoadOptions, MongoDataSource},
8    graphql::schema::create_options_input::OptionsInput,
9};
10
11use super::Services;
12
13impl Services {
14    pub async fn find_many(
15        db: Database,
16        filter: Document,
17        collection: String,
18        eager_load_options: Vec<EagerLoadOptions>,
19    ) -> Result<(Vec<Option<Document>>, i64), async_graphql::Error> {
20        let coll = db.collection::<Document>(&collection);
21
22        debug!("Find Many: {:?}", filter);
23
24        let query = match filter.get("query") {
25            Some(query) => query,
26            None => return Err(Error::new("Query filter not found")),
27        };
28
29        let query_doc = match query.as_document() {
30            Some(query_doc) => query_doc,
31            None => return Err(Error::new("Failed to convert query filter to document.")),
32        };
33
34        let nested_find_filter = Services::create_nested_find_filter(&query_doc);
35
36        let opts_bson = match filter.get("opts") {
37            Some(opts_doc) => opts_doc.clone(),
38            None => {
39                trace!("No opts found, using default opts.");
40                let default_opts = doc! {
41                    "per_page": 10,
42                    "page": 1,
43                    "sort": [
44                        {
45                            "field": "_id",
46                            "direction": "Asc"
47                        }
48                    ]
49                };
50                bson::to_bson(&default_opts).unwrap()
51            }
52        };
53
54        // Serialize the opts document to a OptionsInput
55        let opts: Option<OptionsInput> = match bson::from_bson(opts_bson) {
56            Ok(opts) => opts,
57            Err(error) => {
58                error!("Failed to convert opts to OptionsInput: {:?}", error);
59                return Err(Error::new("Failed to convert opts to OptionsInput."));
60            }
61        };
62
63        let aggregation =
64            MongoDataSource::create_aggregation(&nested_find_filter, eager_load_options, opts)?;
65
66        let mut cursor = coll.aggregate(aggregation, None).await?;
67
68        trace!("Find Many Cursor: {:?}", cursor);
69
70        let mut result_doc = None;
71
72        while let Some(result) = cursor.next().await {
73            match result {
74                Ok(document) => {
75                    result_doc = Some(document);
76                    break;
77                }
78                Err(_error) => Err(Error::new("Can't find results.")
79                    .extend_with(|err, e| e.set("details", err.message.as_str())))?,
80            }
81        }
82
83        trace!("Find Many Result: {:?}", result_doc);
84
85        if result_doc.is_none() {
86            return Err(Error::new("Can't find matched results."));
87        }
88        let results = match result_doc.as_ref().unwrap().get("documents") {
89            Some(documents) => {
90                let documents = match documents.as_array() {
91                    Some(documents) => documents.clone(),
92                    None => Vec::<bson::Bson>::new(),
93                };
94                let documents = documents
95                    .clone()
96                    .into_iter()
97                    .map(|doc| Some(doc.as_document().unwrap().clone()))
98                    .collect::<Vec<Option<Document>>>();
99                documents
100            }
101            None => Vec::<Option<Document>>::new(),
102        };
103
104        let total_count = if let Some(total_count_docs) = result_doc
105            .as_ref()
106            .unwrap()
107            .get("total_count")
108            .unwrap()
109            .as_array()
110        {
111            if total_count_docs.len() > 0 {
112                let total_count_doc = total_count_docs.get(0).unwrap().as_document().unwrap();
113                let total_count = total_count_doc
114                    .get("total_count")
115                    .unwrap_or(&bson::Bson::Int32(0))
116                    .as_i32()
117                    .unwrap();
118                total_count
119            } else {
120                0
121            }
122        } else {
123            0
124        };
125
126        Ok((results, total_count as i64))
127    }
128}