|
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