@@ -4,6 +4,7 @@ import {createSupabaseDirectClient, pgp} from 'shared/supabase/init'
44import { from , join , leftJoin , limit , orderBy , renderSql , select , where , } from 'shared/supabase/sql-builder'
55import { MIN_BIO_LENGTH } from "common/constants" ;
66import { compact } from "lodash" ;
7+ import { OptionTableKey } from "common/profiles/constants" ;
78
89export type profileQueryType = {
910 limit ?: number | undefined ,
@@ -37,14 +38,16 @@ export type profileQueryType = {
3738 skipId ?: string | undefined ,
3839 orderBy ?: string | undefined ,
3940 lastModificationWithin ?: string | undefined ,
41+ } & {
42+ [ K in OptionTableKey ] ?: string [ ] | undefined
4043}
4144
4245// const userActivityColumns = ['last_online_time']
4346
4447
4548export const loadProfiles = async ( props : profileQueryType ) => {
4649 const pg = createSupabaseDirectClient ( )
47- console . debug ( props )
50+ console . debug ( 'loadProfiles' , props )
4851 const {
4952 limit : limitParam ,
5053 after,
@@ -66,6 +69,9 @@ export const loadProfiles = async (props: profileQueryType) => {
6669 religion,
6770 wants_kids_strength,
6871 has_kids,
72+ interests,
73+ causes,
74+ work,
6975 is_smoker,
7076 shortBio,
7177 geodbCityIds,
@@ -95,24 +101,55 @@ export const loadProfiles = async (props: profileQueryType) => {
95101 : 'profiles'
96102
97103 const userActivityJoin = 'user_activity on user_activity.user_id = profiles.user_id'
104+
105+ // Pre-aggregated interests per profile
106+ function getManyToManyJoin ( label : OptionTableKey ) {
107+ return `(
108+ SELECT
109+ profile_${ label } .profile_id,
110+ ARRAY_AGG(${ label } .name ORDER BY ${ label } .name) AS ${ label }
111+ FROM profile_${ label }
112+ JOIN ${ label } ON ${ label } .id = profile_${ label } .option_id
113+ GROUP BY profile_${ label } .profile_id
114+ ) i ON i.profile_id = profiles.id`
115+ }
116+ const interestsJoin = getManyToManyJoin ( 'interests' )
117+ const causesJoin = getManyToManyJoin ( 'causes' )
118+ const workJoin = getManyToManyJoin ( 'work' )
119+
98120 const compatibilityScoreJoin = pgp . as . format ( `compatibility_scores cs on (cs.user_id_1 = LEAST(profiles.user_id, $(compatibleWithUserId)) and cs.user_id_2 = GREATEST(profiles.user_id, $(compatibleWithUserId)))` , { compatibleWithUserId} )
99121
122+ const joins = [
123+ orderByParam === 'last_online_time' && leftJoin ( userActivityJoin ) ,
124+ orderByParam === 'compatibility_score' && compatibleWithUserId && join ( compatibilityScoreJoin ) ,
125+ interests && leftJoin ( interestsJoin ) ,
126+ causes && leftJoin ( causesJoin ) ,
127+ work && leftJoin ( workJoin ) ,
128+ ]
129+
100130 const _orderBy = orderByParam === 'compatibility_score' ? 'cs.score' : `${ tablePrefix } .${ orderByParam } `
101131 const afterFilter = renderSql (
102132 select ( _orderBy ) ,
103133 from ( 'profiles' ) ,
104- orderByParam === 'last_online_time' && leftJoin ( userActivityJoin ) ,
105- orderByParam === 'compatibility_score' && compatibleWithUserId && join ( compatibilityScoreJoin ) ,
134+ ...joins ,
106135 where ( 'profiles.id = $(after)' , { after} ) ,
107136 )
108137
109138 const tableSelection = compact ( [
110139 from ( 'profiles' ) ,
111140 join ( 'users on users.id = profiles.user_id' ) ,
112- orderByParam === 'last_online_time' && leftJoin ( userActivityJoin ) ,
113- orderByParam === 'compatibility_score' && compatibleWithUserId && join ( compatibilityScoreJoin ) ,
141+ ...joins ,
114142 ] )
115143
144+ function getManyToManyClause ( label : OptionTableKey ) {
145+ return `EXISTS (
146+ SELECT 1 FROM profile_${ label } pi2
147+ JOIN ${ label } ii2 ON ii2.id = pi2.option_id
148+ WHERE pi2.profile_id = profiles.id
149+ AND ii2.name = ANY (ARRAY[$(values)])
150+ )`
151+ }
152+
116153 const filters = [
117154 where ( 'looking_for_matches = true' ) ,
118155 where ( `profiles.disabled != true` ) ,
@@ -190,6 +227,12 @@ export const loadProfiles = async (props: profileQueryType) => {
190227 { religion}
191228 ) ,
192229
230+ interests ?. length && where ( getManyToManyClause ( 'interests' ) , { values : interests } ) ,
231+
232+ causes ?. length && where ( getManyToManyClause ( 'causes' ) , { values : causes } ) ,
233+
234+ work ?. length && where ( getManyToManyClause ( 'work' ) , { values : work } ) ,
235+
193236 ! ! wants_kids_strength &&
194237 wants_kids_strength !== - 1 &&
195238 where (
@@ -229,12 +272,15 @@ export const loadProfiles = async (props: profileQueryType) => {
229272 lastModificationWithin && where ( `last_modification_time >= NOW() - INTERVAL $(lastModificationWithin)` , { lastModificationWithin} ) ,
230273 ]
231274
232- let selectCols = 'profiles.*, name, username, users.data as user'
275+ let selectCols = 'profiles.*, users. name, users. username, users.data as user'
233276 if ( orderByParam === 'compatibility_score' ) {
234277 selectCols += ', cs.score as compatibility_score'
235278 } else if ( orderByParam === 'last_online_time' ) {
236279 selectCols += ', user_activity.last_online_time'
237280 }
281+ if ( interests ) selectCols += `, COALESCE(i.interests, '{}') AS interests`
282+ if ( causes ) selectCols += `, COALESCE(i.causes, '{}') AS causes`
283+ if ( work ) selectCols += `, COALESCE(i.work, '{}') AS work`
238284
239285 const query = renderSql (
240286 select ( selectCols ) ,
@@ -267,7 +313,8 @@ export const getProfiles: APIHandler<'get-profiles'> = async (props, auth) => {
267313 if ( ! props . skipId ) props . skipId = auth . uid
268314 const { profiles, count} = await loadProfiles ( props )
269315 return { status : 'success' , profiles : profiles , count : count }
270- } catch {
316+ } catch ( error ) {
317+ console . log ( error )
271318 return { status : 'fail' , profiles : [ ] , count : 0 }
272319 }
273320}
0 commit comments