|
2 | 2 | //! |
3 | 3 | //! Some of these are functions that return a query that is dynamically generated based on requirements. |
4 | 4 |
|
| 5 | +use crate::qp::Exam; |
| 6 | + |
5 | 7 | /// Query to get similar papers. Matches `course_code` ($1) always. Other parameters are optional and can be enabled or disabled using the arguments to this function. |
6 | 8 | pub fn get_similar_papers_query( |
7 | 9 | year: bool, |
@@ -80,62 +82,98 @@ pub fn get_all_unapproved_query() -> String { |
80 | 82 | format!("SELECT {} FROM iqps WHERE approve_status = false and is_deleted=false ORDER BY upload_timestamp ASC", ADMIN_DASHBOARD_QP_FIELDS) |
81 | 83 | } |
82 | 84 |
|
| 85 | +/// An enum representing the exam filter for the search query |
| 86 | +pub enum ExamFilter { |
| 87 | + Exam(Exam), // Match an exact exam or use `ct` substring match |
| 88 | + Any, // Match anything |
| 89 | + MidEnd, // Midsem or endsem |
| 90 | +} |
| 91 | + |
| 92 | +impl TryFrom<&String> for ExamFilter { |
| 93 | + type Error = color_eyre::eyre::Error; |
| 94 | + |
| 95 | + fn try_from(value: &String) -> Result<Self, Self::Error> { |
| 96 | + if value.is_empty() { |
| 97 | + Ok(ExamFilter::Any) |
| 98 | + } else if value == "midend" { |
| 99 | + Ok(ExamFilter::MidEnd) |
| 100 | + } else { |
| 101 | + Ok(ExamFilter::Exam(Exam::try_from(value)?)) |
| 102 | + } |
| 103 | + } |
| 104 | +} |
| 105 | + |
83 | 106 | /// Returns the query for searching question papers. It is mostly voodoo, @Rajiv please update the documentation. |
84 | 107 | /// |
85 | 108 | /// Optionally, the `exam` argument can be used to also add a clause to match the exam field. |
86 | | -pub fn get_qp_search_query(exam: bool) -> String { |
87 | | - let exam_filter = if exam { |
88 | | - "WHERE (exam = $2 OR exam = '')" |
89 | | - } else { |
90 | | - "" |
| 109 | +/// |
| 110 | +/// Query parameters: |
| 111 | +/// $1 - Search query |
| 112 | +/// $2 - Exam filter string (can be midsem, endsem, midend, or ct) |
| 113 | +/// |
| 114 | +/// Returns the query and a boolean representing whether the second argument is required |
| 115 | +pub fn get_qp_search_query(exam_filter: ExamFilter) -> (String, bool) { |
| 116 | + let (exam_filter, use_exam_arg) = match exam_filter { |
| 117 | + ExamFilter::Any => ("", false), |
| 118 | + ExamFilter::MidEnd => ( |
| 119 | + "WHERE (exam = 'midsem' OR exam = 'endsem' OR exam = '')", |
| 120 | + false, |
| 121 | + ), |
| 122 | + ExamFilter::Exam(exam) => match exam { |
| 123 | + Exam::CT(_) => ("WHERE (exam LIKE 'ct%' OR exam = '')", false), |
| 124 | + _ => ("WHERE (exam = $2 OR exam = '')", true), |
| 125 | + }, |
91 | 126 | }; |
92 | 127 |
|
93 | | - format!(" |
94 | | - WITH filtered AS ( |
95 | | - SELECT * from iqps {exam_filter} |
96 | | - ), |
97 | | - fuzzy AS ( |
98 | | - SELECT id, |
99 | | - similarity(course_code || ' ' || course_name, $1) AS sim_score, |
100 | | - row_number() OVER (ORDER BY similarity(course_code || ' ' || course_name, $1) DESC) AS rank_ix |
101 | | - FROM filtered |
102 | | - WHERE (course_code || ' ' || course_name) %>> $1 AND approve_status = true |
103 | | - ORDER BY rank_ix |
104 | | - LIMIT 30 |
105 | | - ), |
106 | | - full_text AS ( |
107 | | - SELECT id, |
108 | | - ts_rank_cd(fts_course_details, websearch_to_tsquery($1)) AS rank_score, |
109 | | - row_number() OVER (ORDER BY ts_rank_cd(fts_course_details, websearch_to_tsquery($1)) DESC) AS rank_ix |
110 | | - FROM filtered |
111 | | - WHERE fts_course_details @@ websearch_to_tsquery($1) AND approve_status = true |
112 | | - ORDER BY rank_ix |
113 | | - LIMIT 30 |
114 | | - ), |
115 | | - partial_search AS ( |
116 | | - SELECT id, |
117 | | - ts_rank_cd(fts_course_details, {to_tsquery}) AS rank_score, |
118 | | - row_number() OVER (ORDER BY ts_rank_cd(fts_course_details, {to_tsquery}) DESC) as rank_ix |
119 | | - FROM filtered |
120 | | - WHERE fts_course_details @@ {to_tsquery} AND approve_status = true |
121 | | - LIMIT 30 |
| 128 | + ( |
| 129 | + format!(" |
| 130 | + WITH filtered AS ( |
| 131 | + SELECT * from iqps {exam_filter} |
| 132 | + ), |
| 133 | + fuzzy AS ( |
| 134 | + SELECT id, |
| 135 | + similarity(course_code || ' ' || course_name, $1) AS sim_score, |
| 136 | + row_number() OVER (ORDER BY similarity(course_code || ' ' || course_name, $1) DESC) AS rank_ix |
| 137 | + FROM filtered |
| 138 | + WHERE (course_code || ' ' || course_name) %>> $1 AND approve_status = true |
| 139 | + ORDER BY rank_ix |
| 140 | + LIMIT 30 |
| 141 | + ), |
| 142 | + full_text AS ( |
| 143 | + SELECT id, |
| 144 | + ts_rank_cd(fts_course_details, websearch_to_tsquery($1)) AS rank_score, |
| 145 | + row_number() OVER (ORDER BY ts_rank_cd(fts_course_details, websearch_to_tsquery($1)) DESC) AS rank_ix |
| 146 | + FROM filtered |
| 147 | + WHERE fts_course_details @@ websearch_to_tsquery($1) AND approve_status = true |
| 148 | + ORDER BY rank_ix |
| 149 | + LIMIT 30 |
| 150 | + ), |
| 151 | + partial_search AS ( |
| 152 | + SELECT id, |
| 153 | + ts_rank_cd(fts_course_details, {to_tsquery}) AS rank_score, |
| 154 | + row_number() OVER (ORDER BY ts_rank_cd(fts_course_details, {to_tsquery}) DESC) as rank_ix |
| 155 | + FROM filtered |
| 156 | + WHERE fts_course_details @@ {to_tsquery} AND approve_status = true |
| 157 | + LIMIT 30 |
| 158 | + ), |
| 159 | + result AS ( |
| 160 | + SELECT {intermediate_fields} |
| 161 | + FROM fuzzy |
| 162 | + FULL OUTER JOIN full_text ON fuzzy.id = full_text.id |
| 163 | + FULL OUTER JOIN partial_search ON coalesce(fuzzy.id, full_text.id) = partial_search.id |
| 164 | + JOIN filtered ON coalesce(fuzzy.id, full_text.id, partial_search.id) = filtered.id |
| 165 | + ORDER BY |
| 166 | + coalesce(1.0 / (50 + fuzzy.rank_ix), 0.0) * 1 + |
| 167 | + coalesce(1.0 / (50 + full_text.rank_ix), 0.0) * 1 + |
| 168 | + coalesce(1.0 / (50 + partial_search.rank_ix), 0.0) * 1 |
| 169 | + DESC |
| 170 | + ) SELECT {search_qp_fields} FROM result", |
| 171 | + search_qp_fields = SEARCH_QP_FIELDS, |
| 172 | + to_tsquery = "to_tsquery('simple', websearch_to_tsquery('simple', $1)::text || ':*')", |
| 173 | + exam_filter = exam_filter, |
| 174 | + intermediate_fields = ADMIN_DASHBOARD_QP_FIELDS.split(", ").map(|field| format!("filtered.{}", field)).collect::<Vec<String>>().join(", ") |
122 | 175 | ), |
123 | | - result AS ( |
124 | | - SELECT {intermediate_fields} |
125 | | - FROM fuzzy |
126 | | - FULL OUTER JOIN full_text ON fuzzy.id = full_text.id |
127 | | - FULL OUTER JOIN partial_search ON coalesce(fuzzy.id, full_text.id) = partial_search.id |
128 | | - JOIN filtered ON coalesce(fuzzy.id, full_text.id, partial_search.id) = filtered.id |
129 | | - ORDER BY |
130 | | - coalesce(1.0 / (50 + fuzzy.rank_ix), 0.0) * 1 + |
131 | | - coalesce(1.0 / (50 + full_text.rank_ix), 0.0) * 1 + |
132 | | - coalesce(1.0 / (50 + partial_search.rank_ix), 0.0) * 1 |
133 | | - DESC |
134 | | - ) SELECT {search_qp_fields} FROM result", |
135 | | - search_qp_fields = SEARCH_QP_FIELDS, |
136 | | - to_tsquery = "to_tsquery('simple', websearch_to_tsquery('simple', $1)::text || ':*')", |
137 | | - exam_filter = exam_filter, |
138 | | - intermediate_fields = ADMIN_DASHBOARD_QP_FIELDS.split(", ").map(|field| format!("filtered.{}", field)).collect::<Vec<String>>().join(", ") |
| 176 | + use_exam_arg |
139 | 177 | ) |
140 | 178 | } |
141 | 179 |
|
|
0 commit comments