Skip to content

Commit 7fee2fb

Browse files
authored
Refactor recipe retrieval procedure to support flexible filtering and pagination (#29)
* Refactor recipe retrieval procedure to support flexible filtering and pagination * chore: fix linting issues
1 parent fc1e583 commit 7fee2fb

3 files changed

Lines changed: 67 additions & 166 deletions

File tree

github_conf/branch_protection_rules.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
"message": "Not Found",
33
"documentation_url": "https://docs.github.com/rest",
44
"status": "404"
5-
}
5+
}

procedures/get/recipe.sql

Lines changed: 62 additions & 165 deletions
Original file line numberDiff line numberDiff line change
@@ -3,193 +3,90 @@ USE smartcooking;
33

44
DELIMITER //
55

6-
CREATE OR REPLACE PROCEDURE get_recipe_by_id(
7-
IN p_recipe_id INT,
8-
IN p_language_iso_code CHAR(2)
9-
)
10-
BEGIN
11-
SELECT
12-
r.recipe_id,
13-
r.author_id,
14-
p.person_name AS author_name,
15-
r.publication_date,
16-
r.modification_date,
17-
r.picture_id,
18-
r.preparation_time,
19-
r.cook_time,
20-
r.servings,
21-
r.difficulty_level,
22-
r.estimated_cost,
23-
r.number_of_reviews,
24-
r.recipe_source,
25-
rs.status_name AS recipe_status,
26-
rt.title,
27-
rt.details,
28-
rt.preparation,
29-
rt.nutritional_information,
30-
rt.video_url
31-
FROM recipe r
32-
INNER JOIN recipe_translation rt ON r.recipe_id = rt.recipe_id
33-
INNER JOIN lang l ON rt.language_id = l.language_id
34-
INNER JOIN person p ON r.author_id = p.person_id
35-
INNER JOIN recipe_status rs ON r.recipe_status = rs.status_id
36-
WHERE r.recipe_id = p_recipe_id
37-
AND l.iso_code = p_language_iso_code;
38-
END //
39-
40-
-- Centralized procedure for fetching paginated recipes with filtering
41-
CREATE OR REPLACE PROCEDURE get_recipes_paginated(
42-
IN p_filter_condition TEXT,
6+
CREATE OR REPLACE PROCEDURE search_recipes_flexible(
7+
IN p_filters JSON,
438
IN p_limit INT,
449
IN p_offset INT,
45-
IN p_language_iso_code CHAR(2),
46-
IN p_group_by TEXT,
47-
IN p_having_condition TEXT,
48-
IN p_order_by TEXT,
49-
IN p_filter_param TEXT
10+
IN p_language_iso_code CHAR(2)
5011
)
5112
BEGIN
5213
DECLARE v_limit INT;
5314
DECLARE v_offset INT;
15+
5416
SET v_limit = enforce_row_limit(p_limit);
5517
SET v_offset = calculate_offset(p_offset, v_limit);
5618

57-
SET @query = 'SELECT r.recipe_id, r.author_id, p.person_name AS author_name, '
58-
'r.picture_id, r.cook_time, r.difficulty_level, r.number_of_reviews, '
59-
'r.recipe_status, rt.title '
60-
'FROM recipe r '
61-
'INNER JOIN recipe_translation rt ON r.recipe_id = rt.recipe_id '
62-
'INNER JOIN lang l ON rt.language_id = l.language_id '
63-
'INNER JOIN person p ON r.author_id = p.person_id '
64-
'WHERE l.iso_code = ? ';
19+
SET @sql = '
20+
SELECT DISTINCT r.recipe_id,
21+
r.author_id,
22+
p.person_name AS author_name,
23+
r.picture_id,
24+
r.cook_time,
25+
r.difficulty_level,
26+
r.number_of_reviews,
27+
rs.status_name AS recipe_status,
28+
rt.title
29+
FROM recipe r
30+
INNER JOIN recipe_translation rt ON r.recipe_id = rt.recipe_id
31+
INNER JOIN lang l ON rt.language_id = l.language_id
32+
INNER JOIN person p ON r.author_id = p.person_id
33+
INNER JOIN recipe_status rs ON r.recipe_status = rs.status_id
34+
WHERE l.iso_code = ?
35+
';
6536

66-
IF p_filter_condition IS NOT NULL THEN
67-
SET @query = CONCAT(@query, ' ', p_filter_condition);
37+
-- Handle dynamic filters
38+
IF JSON_CONTAINS_PATH(p_filters, 'one', '$.title') THEN
39+
SET @sql = CONCAT(@sql, ' AND rt.title LIKE CONCAT("%", JSON_UNQUOTE(JSON_EXTRACT(p_filters, "$.title")), "%")');
6840
END IF;
6941

70-
IF p_group_by IS NOT NULL THEN
71-
SET @query = CONCAT(@query, ' ', p_group_by);
42+
IF JSON_CONTAINS_PATH(p_filters, 'one', '$.author_id') THEN
43+
SET @sql = CONCAT(@sql, ' AND r.author_id = JSON_EXTRACT(p_filters, "$.author_id")');
7244
END IF;
7345

74-
IF p_having_condition IS NOT NULL THEN
75-
SET @query = CONCAT(@query, ' ', p_having_condition);
46+
IF JSON_CONTAINS_PATH(p_filters, 'one', '$.difficulty_max') THEN
47+
SET @sql = CONCAT(@sql, ' AND r.difficulty_level <= JSON_EXTRACT(p_filters, "$.difficulty_max")');
7648
END IF;
7749

78-
IF p_order_by IS NOT NULL THEN
79-
SET @query = CONCAT(@query, ' ', p_order_by);
50+
IF JSON_CONTAINS_PATH(p_filters, 'one', '$.category') THEN
51+
SET @sql = CONCAT(@sql, '
52+
AND EXISTS (
53+
SELECT 1 FROM recipe_category rc
54+
JOIN category c ON rc.category_id = c.category_id
55+
WHERE rc.recipe_id = r.recipe_id
56+
AND c.category_name = JSON_UNQUOTE(JSON_EXTRACT(p_filters, "$.category"))
57+
)
58+
');
8059
END IF;
8160

82-
SET @query = CONCAT(@query, ' LIMIT ? OFFSET ?');
83-
84-
PREPARE stmt FROM @query;
85-
86-
IF p_filter_param IS NOT NULL THEN
87-
EXECUTE stmt USING p_language_iso_code, p_filter_param, v_limit, v_offset;
88-
ELSE
89-
EXECUTE stmt USING p_language_iso_code, v_limit, v_offset;
61+
IF JSON_CONTAINS_PATH(p_filters, 'one', '$.tags') THEN
62+
SET @sql = CONCAT(@sql, '
63+
AND EXISTS (
64+
SELECT 1 FROM recipe_tag rtg
65+
JOIN tag t ON rtg.tag_id = t.tag_id
66+
WHERE rtg.recipe_id = r.recipe_id
67+
AND JSON_CONTAINS(p_filters, JSON_QUOTE(t.tag_name), "$.tags")
68+
)
69+
');
9070
END IF;
9171

92-
DEALLOCATE PREPARE stmt;
93-
END //
94-
95-
-- Procedure for retrieving all recipes with optional pagination
96-
CREATE OR REPLACE PROCEDURE get_all_recipes_paginated(
97-
IN p_limit INT,
98-
IN p_offset INT,
99-
IN p_language_iso_code CHAR(2)
100-
)
101-
BEGIN
102-
CALL get_recipes_paginated(
103-
NULL, p_limit, p_offset, p_language_iso_code, NULL, NULL, NULL, NULL
104-
);
105-
END //
106-
107-
-- Procedure for retrieving recipes by author with pagination
108-
CREATE OR REPLACE PROCEDURE get_recipes_by_author_paginated(
109-
IN p_author_id INT,
110-
IN p_limit INT,
111-
IN p_offset INT,
112-
IN p_language_iso_code CHAR(2)
113-
)
114-
BEGIN
115-
CALL get_recipes_paginated(
116-
'AND r.author_id = ?', p_limit, p_offset, p_language_iso_code, NULL, NULL, NULL, p_author_id
117-
);
118-
END //
119-
120-
-- Procedure for retrieving recipes liked by a person with pagination
121-
CREATE OR REPLACE PROCEDURE get_recipes_liked_by_person_paginated(
122-
IN p_person_id INT,
123-
IN p_limit INT,
124-
IN p_offset INT
125-
)
126-
BEGIN
127-
DECLARE v_limit INT;
128-
DECLARE v_offset INT;
129-
SET v_limit = enforce_row_limit(p_limit);
130-
SET v_offset = calculate_offset(p_offset, v_limit);
131-
132-
SELECT
133-
r.recipe_id,
134-
r.author_id,
135-
p.person_name AS author_name,
136-
r.picture_id,
137-
r.cook_time,
138-
r.difficulty_level,
139-
r.number_of_reviews,
140-
r.recipe_status
141-
FROM
142-
recipe r
143-
INNER JOIN recipe_engagement re ON r.recipe_id = re.recipe_id
144-
INNER JOIN person p ON r.author_id = p.person_id
145-
WHERE re.person_id = p_person_id AND re.engagement_type = 'like'
146-
LIMIT v_limit OFFSET v_offset;
147-
END //
148-
149-
-- Procedure for retrieving recipes by category with pagination
150-
CREATE OR REPLACE PROCEDURE get_recipes_by_category_paginated(
151-
IN p_category_id INT,
152-
IN p_limit INT,
153-
IN p_offset INT,
154-
IN p_language_iso_code CHAR(2)
155-
)
156-
BEGIN
157-
CALL get_recipes_paginated(
158-
'INNER JOIN recipe_category rc ON r.recipe_id = rc.recipe_id AND rc.category_id = ?',
159-
p_limit, p_offset, p_language_iso_code, NULL, NULL, NULL, p_category_id
160-
);
161-
END //
162-
163-
-- Procedure for retrieving recipes by tags with pagination
164-
CREATE OR REPLACE PROCEDURE get_recipes_by_tags_paginated(
165-
IN p_tags JSON,
166-
IN p_limit INT,
167-
IN p_offset INT,
168-
IN p_language_iso_code CHAR(2)
169-
)
170-
BEGIN
171-
CALL get_recipes_paginated(
172-
'INNER JOIN recipe_tag rtg ON r.recipe_id = rtg.recipe_id AND JSON_CONTAINS(?, JSON_QUOTE(rtg.tag))',
173-
p_limit, p_offset, p_language_iso_code, NULL, NULL, NULL, p_tags
174-
);
175-
END //
72+
IF JSON_CONTAINS_PATH(p_filters, 'one', '$.ingredient') THEN
73+
SET @sql = CONCAT(@sql, '
74+
AND EXISTS (
75+
SELECT 1 FROM recipe_ingredient ri
76+
JOIN ingredient i ON ri.ingredient_id = i.ingredient_id
77+
JOIN ingredient_translation it ON i.ingredient_id = it.ingredient_id
78+
WHERE ri.recipe_id = r.recipe_id
79+
AND it.translated_name LIKE CONCAT("%", JSON_UNQUOTE(JSON_EXTRACT(p_filters, "$.ingredient")), "%")
80+
AND it.language_id = l.language_id
81+
)
82+
');
83+
END IF;
17684

177-
-- Procedure for retrieving recipes by name with pagination
178-
CREATE OR REPLACE PROCEDURE get_recipes_by_name_paginated(
179-
IN p_name VARCHAR(255),
180-
IN p_limit INT,
181-
IN p_offset INT,
182-
IN p_language_iso_code CHAR(2)
183-
)
184-
BEGIN
185-
DECLARE v_safe_recipe_name VARCHAR(255);
186-
SET v_safe_recipe_name = sanitize_string(p_name);
85+
SET @sql = CONCAT(@sql, ' LIMIT ? OFFSET ?');
18786

188-
CALL get_recipes_paginated(
189-
'AND rt.title LIKE ?',
190-
p_limit, p_offset, p_language_iso_code, NULL, NULL, NULL,
191-
v_safe_recipe_name
192-
);
87+
PREPARE stmt FROM @sql;
88+
EXECUTE stmt USING p_language_iso_code, v_limit, v_offset;
89+
DEALLOCATE PREPARE stmt;
19390
END //
19491

19592
-- Procedure for retrieving recipes by status with pagination

super-linter-output/super-linter-summary.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
| JSCPD | Pass ✅ |
1010
| JSON | Pass ✅ |
1111
| JSON_PRETTIER | Pass ✅ |
12+
| MARKDOWN | Pass ✅ |
13+
| MARKDOWN_PRETTIER | Pass ✅ |
14+
| NATURAL_LANGUAGE | Pass ✅ |
15+
| SQLFLUFF | Pass ✅ |
1216
| YAML | Pass ✅ |
1317
| YAML_PRETTIER | Pass ✅ |
1418

0 commit comments

Comments
 (0)