1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
use super::*;
use crate::{
    error::DecorateErrorWithFieldInformationExtension, output_meta, query_builder::MongoReadQueryBuilder,
    query_strings::Find, vacuum_cursor, IntoBson,
};
use mongodb::{bson::doc, options::FindOptions, ClientSession, Database};
use query_structure::*;
use tracing::{info_span, Instrument};

/// Finds a single record. Joins are not required at the moment because the selector is always a unique one.
pub async fn get_single_record<'conn>(
    database: &Database,
    session: &mut ClientSession,
    model: &Model,
    filter: &Filter,
    selected_fields: &FieldSelection,
) -> crate::Result<Option<SingleRecord>> {
    let coll = database.collection(model.db_name());

    let span = info_span!(
        "prisma:engine:db_query",
        user_facing = true,
        "db.statement" = &format_args!("db.{}.findOne(*)", coll.name())
    );

    let meta_mapping = output_meta::from_selected_fields(selected_fields);
    let query_arguments: QueryArguments = (model.clone(), filter.clone()).into();
    let query = MongoReadQueryBuilder::from_args(query_arguments)?
        .with_model_projection(selected_fields.clone())?
        .with_virtual_fields(selected_fields.virtuals())?
        .build()?;

    let docs = query.execute(coll, session).instrument(span).await?;

    if docs.is_empty() {
        Ok(None)
    } else {
        let field_names: Vec<_> = selected_fields.db_names().collect();
        let doc = docs.into_iter().next().unwrap();
        let record = document_to_record(doc, &field_names, &meta_mapping)?;

        Ok(Some(SingleRecord { record, field_names }))
    }
}

// Checklist:
// - [x] OrderBy scalar.
// - [ ] OrderBy relation.
// - [x] Skip, take
// - [x] Cursor
// - [x] Distinct select (inherently given from core).
// - [x] Relation aggregation count
pub async fn get_many_records<'conn>(
    database: &Database,
    session: &mut ClientSession,
    model: &Model,
    query_arguments: QueryArguments,
    selected_fields: &FieldSelection,
) -> crate::Result<ManyRecords> {
    let coll = database.collection(model.db_name());

    let span = info_span!(
        "prisma:engine:db_query",
        user_facing = true,
        "db.statement" = &format_args!("db.{}.findMany(*)", coll.name())
    );

    let reverse_order = query_arguments.take.map(|t| t < 0).unwrap_or(false);
    let field_names: Vec<_> = selected_fields.db_names().collect();

    let meta_mapping = output_meta::from_selected_fields(selected_fields);
    let mut records = ManyRecords::new(field_names.clone());

    if let Some(0) = query_arguments.take {
        return Ok(records);
    };

    let query = MongoReadQueryBuilder::from_args(query_arguments)?
        .with_model_projection(selected_fields.clone())?
        .with_virtual_fields(selected_fields.virtuals())?
        .build()?;

    let docs = query.execute(coll, session).instrument(span).await?;
    for doc in docs {
        let record = document_to_record(doc, &field_names, &meta_mapping)?;
        records.push(record)
    }

    if reverse_order {
        records.reverse();
    }

    Ok(records)
}

pub async fn get_related_m2m_record_ids<'conn>(
    database: &Database,
    session: &mut ClientSession,
    from_field: &RelationFieldRef,
    from_record_ids: &[SelectionResult],
) -> crate::Result<Vec<(SelectionResult, SelectionResult)>> {
    if from_record_ids.is_empty() {
        return Ok(vec![]);
    }

    let model = from_field.model();
    let coll = database.collection(model.db_name());
    let id_field = pick_singular_id(&model);
    let ids = from_record_ids
        .iter()
        .map(|p| {
            (&id_field, p.values().next().unwrap())
                .into_bson()
                .decorate_with_scalar_field_info(&id_field)
        })
        .collect::<crate::Result<Vec<_>>>()?;

    // Scalar field name where the relation ids list is on `model`.
    let id_holder_field = from_field.scalar_fields().into_iter().next().unwrap();
    let relation_ids_field_name = id_holder_field.name().to_owned();

    let filter = doc! { id_field.db_name(): { "$in": ids } };
    let projection = doc! { id_field.db_name(): 1, relation_ids_field_name: 1 };

    let query_string_builder = Find::new(&filter, &projection, coll.name());
    let find_options = FindOptions::builder().projection(projection.clone()).build();

    let cursor = observing(&query_string_builder, || {
        coll.find_with_session(filter.clone(), Some(find_options), session)
    })
    .await?;
    let docs = vacuum_cursor(cursor, session).await?;
    let parent_id_meta = output_meta::from_scalar_field(&id_field);
    let related_ids_holder_meta = output_meta::from_scalar_field(&id_holder_field);
    let child_id_field = pick_singular_id(&from_field.related_model());

    let mut id_pairs = vec![];
    for mut doc in docs {
        let id_value = doc.remove(id_field.db_name()).unwrap();
        let parent_id = value_from_bson(id_value, &parent_id_meta)?;

        let related_id_array = doc
            .remove(id_holder_field.name())
            .unwrap_or_else(|| Bson::Array(vec![]));

        let child_ids: Vec<PrismaValue> = match value_from_bson(related_id_array, &related_ids_holder_meta)? {
            PrismaValue::List(vals) => vals,
            val => vec![val],
        };

        let parent_projection = SelectionResult::from((id_field.clone(), parent_id));

        for child_id in child_ids {
            let child_projection = SelectionResult::from((child_id_field.clone(), child_id));
            id_pairs.push((parent_projection.clone(), child_projection));
        }
    }

    Ok(id_pairs)
}