| | 150 | {{{ |
| | 151 | CREATE OR REPLACE FUNCTION recommend_properties_for_user( |
| | 152 | p_user_id BIGINT, |
| | 153 | p_limit INT DEFAULT 50 |
| | 154 | ) |
| | 155 | RETURNS TABLE ( |
| | 156 | property_id BIGINT, |
| | 157 | title varchar, |
| | 158 | description text, |
| | 159 | base_price NUMERIC, |
| | 160 | max_guests INTEGER, |
| | 161 | city varchar, |
| | 162 | country_name varchar, |
| | 163 | distance DOUBLE PRECISION, |
| | 164 | recommendation_type text |
| | 165 | ) |
| | 166 | LANGUAGE plpgsql |
| | 167 | AS $$ |
| | 168 | BEGIN |
| | 169 | IF EXISTS ( |
| | 170 | SELECT 1 |
| | 171 | FROM user_recommendation_profiles urp |
| | 172 | WHERE urp.user_id = p_user_id |
| | 173 | AND urp.preference_embedding IS NOT NULL |
| | 174 | ) THEN |
| | 175 | |
| | 176 | RETURN QUERY |
| | 177 | SELECT |
| | 178 | p.property_id, |
| | 179 | p.title, |
| | 180 | p.description, |
| | 181 | p.base_price, |
| | 182 | p.max_guests, |
| | 183 | a.city, |
| | 184 | c.country_name, |
| | 185 | p.embedding <=> urp.preference_embedding AS distance, |
| | 186 | 'PERSONALIZED'::text AS recommendation_type |
| | 187 | FROM properties p |
| | 188 | JOIN addresses a |
| | 189 | ON a.address_id = p.address_id |
| | 190 | JOIN countries c |
| | 191 | ON c.country_id = a.country_id |
| | 192 | JOIN user_recommendation_profiles urp |
| | 193 | ON urp.user_id = p_user_id |
| | 194 | WHERE p.embedding IS NOT NULL |
| | 195 | AND urp.preference_embedding IS NOT NULL |
| | 196 | AND p.status = 'ACTIVE' |
| | 197 | AND p.property_id NOT IN ( |
| | 198 | SELECT DISTINCT r2.property_id |
| | 199 | FROM bookings b2 |
| | 200 | JOIN guests g2 |
| | 201 | ON g2.guest_id = b2.guest_id |
| | 202 | JOIN rooms r2 |
| | 203 | ON r2.room_id = b2.room_id |
| | 204 | WHERE g2.user_id = p_user_id |
| | 205 | ) |
| | 206 | ORDER BY distance ASC |
| | 207 | LIMIT p_limit; |
| | 208 | |
| | 209 | ELSE |
| | 210 | |
| | 211 | -- korisnikot nema prev bookings |
| | 212 | RETURN QUERY |
| | 213 | SELECT |
| | 214 | p.property_id, |
| | 215 | p.title, |
| | 216 | p.description, |
| | 217 | p.base_price, |
| | 218 | p.max_guests, |
| | 219 | a.city, |
| | 220 | c.country_name, |
| | 221 | NULL::DOUBLE PRECISION AS distance, |
| | 222 | 'FALLBACK_POPULAR'::text AS recommendation_type |
| | 223 | FROM properties p |
| | 224 | JOIN addresses a |
| | 225 | ON a.address_id = p.address_id |
| | 226 | JOIN countries c |
| | 227 | ON c.country_id = a.country_id |
| | 228 | LEFT JOIN reviews rv |
| | 229 | ON rv.property_id = p.property_id |
| | 230 | LEFT JOIN rooms r |
| | 231 | ON r.property_id = p.property_id |
| | 232 | LEFT JOIN bookings b |
| | 233 | ON b.room_id = r.room_id |
| | 234 | AND b.booking_status IN ('CONFIRMED', 'COMPLETED') |
| | 235 | WHERE p.status = 'ACTIVE' |
| | 236 | GROUP BY |
| | 237 | p.property_id, |
| | 238 | p.title, |
| | 239 | p.description, |
| | 240 | p.base_price, |
| | 241 | p.max_guests, |
| | 242 | a.city, |
| | 243 | c.country_name, |
| | 244 | p.created_at |
| | 245 | ORDER BY |
| | 246 | COUNT(b.booking_id) DESC, |
| | 247 | AVG(rv.rating) DESC NULLS LAST, |
| | 248 | p.created_at DESC |
| | 249 | LIMIT p_limit; |
| | 250 | |
| | 251 | END IF; |
| | 252 | END; |
| | 253 | $$; |
| | 254 | }}} |