backend/model/entity/
plants_impl.rs

1//! Contains the implementation of [`Plants`].
2
3use diesel::{
4    debug_query, dsl::sql, pg::Pg, sql_types::Float, BoolExpressionMethods, ExpressionMethods,
5    QueryDsl, QueryResult,
6};
7use diesel_async::{AsyncPgConnection, RunQueryDsl};
8use log::debug;
9
10use crate::{
11    db::{
12        function::{array_to_string, greatest3, similarity},
13        pagination::Paginate,
14    },
15    model::dto::{Page, PageParameters, PlantsSummaryDto},
16    schema::plants::{self, all_columns, common_name_de, common_name_en, unique_name},
17};
18
19use super::Plants;
20
21impl Plants {
22    /// Get the top plants matching the search query.
23    ///
24    /// Uses `pg_trgm` to find matches in `unique_name`, `common_name_de`, `common_name_en` and `edible_uses_en`.
25    /// Ranks them using the `pg_trgm` function `similarity()`.
26    ///
27    /// # Errors
28    /// * Unknown, diesel doesn't say why it might error.
29    pub async fn search(
30        search_query: &str,
31        page_parameters: PageParameters,
32        conn: &mut AsyncPgConnection,
33    ) -> QueryResult<Page<PlantsSummaryDto>> {
34        let query = plants::table
35            .select((
36                greatest3(
37                    similarity(unique_name, search_query),
38                    similarity(array_to_string(common_name_de, " "), search_query),
39                    similarity(array_to_string(common_name_en, " "), search_query),
40                ),
41                plants::all_columns,
42            ))
43            .filter(
44                similarity(unique_name, search_query)
45                    .gt(0.1)
46                    .or(similarity(array_to_string(common_name_de, " "), search_query).gt(0.1))
47                    .or(similarity(array_to_string(common_name_en, " "), search_query).gt(0.1)),
48            )
49            .order(sql::<Float>("1").desc())
50            .paginate(page_parameters.page)
51            .per_page(page_parameters.per_page);
52        debug!("{}", debug_query::<Pg, _>(&query));
53        query
54            .load_page::<(f32, Self)>(conn)
55            .await
56            .map(Page::from_entity)
57    }
58
59    /// Get a page of some plants.
60    ///
61    /// # Errors
62    /// * Unknown, diesel doesn't say why it might error.
63    pub async fn find_any(
64        page_parameters: PageParameters,
65        conn: &mut AsyncPgConnection,
66    ) -> QueryResult<Page<PlantsSummaryDto>> {
67        let query = plants::table
68            .select(all_columns)
69            .into_boxed()
70            .paginate(page_parameters.page)
71            .per_page(page_parameters.per_page);
72        debug!("{}", debug_query::<Pg, _>(&query));
73        query.load_page::<Self>(conn).await.map(Page::from_entity)
74    }
75
76    /// Fetch plant by id from the database.
77    ///
78    /// # Errors
79    /// * Unknown, diesel doesn't say why it might error.
80    pub async fn find_by_id(
81        id: i32,
82        conn: &mut AsyncPgConnection,
83    ) -> QueryResult<PlantsSummaryDto> {
84        let query = plants::table.find(id);
85        debug!("{}", debug_query::<Pg, _>(&query));
86        query.first::<Self>(conn).await.map(Into::into)
87    }
88}