Просмотр исходного кода

:recycle: breakig out matching to separate service

tags/0.0.1
j 4 лет назад
Родитель
Сommit
2e6f1d6a1a

+ 17
- 1
backend/lib/models/matchqueue.js Просмотреть файл

@@ -1,11 +1,27 @@
1 1
 const Schwifty = require('@hapipal/schwifty')
2 2
 const Joi = require('joi')
3
+const User = require('./user')
3 4
 
4 5
 module.exports = class MatchQueue extends Schwifty.Model {
5 6
     static get tableName() {
6 7
         return 'match_queues'
7 8
     }
8
-
9
+    static get relationMappings() {
10
+        return {
11
+            user: {
12
+                relation: Schwifty.Model.HasOneThroughRelation,
13
+                modelClass: User,
14
+                join: {
15
+                    from: 'match_queues.target_id',
16
+                    through: {
17
+                        from: 'profiles.profile_id',
18
+                        to: 'profiles.user_id',
19
+                    },
20
+                    to: 'users.user_id',
21
+                },
22
+            },
23
+        }
24
+    }
9 25
     static get joiSchema() {
10 26
         return Joi.object({
11 27
             match_queue_id: Joi.number(),

+ 2
- 0
backend/lib/plugins/profile.js Просмотреть файл

@@ -8,6 +8,7 @@ const MatchQueueModel = require('../models/matchqueue')
8 8
 
9 9
 const ProfileService = require('../services/profile')
10 10
 const MatchQueueService = require('../services/matchqueue')
11
+const MatchService = require('../services/match')
11 12
 
12 13
 const ProfileScoreRoute = require('../routes/profile/score')
13 14
 const ProfileUpdateRoute = require('../routes/profile/update')
@@ -34,6 +35,7 @@ module.exports = {
34 35
         await server.register(Schmervice)
35 36
         await server.registerService(ProfileService)
36 37
         await server.registerService(MatchQueueService)
38
+        await server.registerService(MatchService)
37 39
 
38 40
         await server.route(ProfileScoreRoute)
39 41
         await server.route(ProfileRespondRoute)

+ 7
- 5
backend/lib/routes/profile/match.js Просмотреть файл

@@ -13,7 +13,7 @@ const pluginConfig = {
13 13
 const validators = {}
14 14
 
15 15
 const responseSchemas = {
16
-    response: Joi.array().items(),
16
+    response: Joi.object(),
17 17
     error: Joi.object({
18 18
         error: Joi.string(),
19 19
     }),
@@ -29,10 +29,12 @@ module.exports = {
29 29
         auth: false,
30 30
 
31 31
         handler: async function (request, h) {
32
-            const { profileService } = request.services()
33
-            const postMatchYins = await profileService.calcMatches()
32
+            const { matchService, matchQueueService } =
33
+                request.server.services()
34
+            const allQueues = await matchQueueService.getAllQueues()
35
+            matchService.calcMatches(allQueues)
34 36
             try {
35
-                if (!postMatchYins) {
37
+                if (!allQueues) {
36 38
                     throw new RangeError('Unable to match profiles')
37 39
                 }
38 40
 
@@ -40,7 +42,7 @@ module.exports = {
40 42
                     .response({
41 43
                         ok: true,
42 44
                         handler: pluginConfig.handlerType,
43
-                        data: postMatchYins,
45
+                        data: allQueues,
44 46
                     })
45 47
                     .code(200)
46 48
             } catch (err) {

+ 0
- 61
backend/lib/services/match-maker.js Просмотреть файл

@@ -1,61 +0,0 @@
1
-const similarity = require('compute-cosine-similarity')
2
-const magic = 1000
3
-
4
-const ScoreKeeper = {
5
-    score: (seeker, potentialMatch) => {
6
-        const seekerResponseValues = seeker.profileResponses.map(res =>
7
-            parseInt(Object.values(res)),
8
-        )
9
-        console.log(seeker)
10
-        const potentialMatchResponseValues =
11
-            potentialMatch.profileResponses.map(res =>
12
-                parseInt(Object.values(res)),
13
-            )
14
-        return Math.floor(
15
-            similarity(seekerResponseValues, potentialMatchResponseValues) *
16
-                magic,
17
-        )
18
-    },
19
-}
20
-
21
-module.exports = class MatchMaker {
22
-    constructor(settings) {
23
-        this.proposer = settings.proposer
24
-
25
-        // Score main profile
26
-        this.keeper = ScoreKeeper
27
-    }
28
-    runPrematch(settings) {
29
-        // grab all profiles form the db
30
-
31
-        // grab all responses
32
-        // grab all response keys
33
-        // create a full response object
34
-        // create a full profile of responses
35
-
36
-        const unscreenedProfiles = []
37
-        const screenedProfiles = []
38
-
39
-        for (const profile in unscreenedProfiles) {
40
-            // Do something here
41
-            if (!settings) {
42
-                return
43
-            }
44
-            screenedProfiles.push(profile)
45
-        }
46
-
47
-        return screenedProfiles
48
-    }
49
-    matchFor(proposer, profiles, settings) {
50
-        // Do something here
51
-        return this.runPrematch(profiles, settings)
52
-    }
53
-    scoreProfiles(profiles) {
54
-        const matchScores = []
55
-        for (const profile in profiles) {
56
-            const scored = this.keeper.score(profile)
57
-            matchScores.push(scored)
58
-        }
59
-        return matchScores
60
-    }
61
-}

+ 108
- 0
backend/lib/services/match.js Просмотреть файл

@@ -0,0 +1,108 @@
1
+const Schmervice = require('@hapipal/schmervice')
2
+
3
+const engageEveryone = allYins => {
4
+    let done
5
+    do {
6
+        console.log('rerunning...')
7
+        done = true
8
+        allYins.forEach(yin => {
9
+            // Keep matching if no true pairing is found
10
+            console.log(yin.realId, yin.otp?.realId)
11
+            if (!yin.otp) {
12
+                done = false
13
+                const yang = yin.getNextCandidate()
14
+                if (!yang.otp || yang.prefers(yin)) {
15
+                    yin.engageTo(yang)
16
+                }
17
+            } else {
18
+                console.log(yin.otp.realId)
19
+            }
20
+        })
21
+    } while (!done)
22
+}
23
+class ProfileFacade {
24
+    constructor(id, matchQueue) {
25
+        this.realId = id ? id : undefined
26
+
27
+        this.matchCandidateIndex = 0
28
+        this.otp = null
29
+        this.matchQueue = matchQueue?.length ? matchQueue : []
30
+        this.fOrder = []
31
+    }
32
+    clearPlaceholderMatches() {
33
+        this.matchQueue = this.matchQueue.filter(match => match.realId == false)
34
+        this.fOrder = this.fOrder.filter(
35
+            potentialFiance => potentialFiance.realId == false,
36
+        )
37
+        if (this.otp && this.otp.realId) {
38
+            this.otp = null
39
+        }
40
+    }
41
+    rank(id) {
42
+        const idQueue = this.matchQueue.map(
43
+            profileFacade => profileFacade.realId,
44
+        )
45
+        return idQueue.includes(id)
46
+            ? idQueue.indexOf(id)
47
+            : this.matchQueue.length + 1
48
+    }
49
+    prefers(p) {
50
+        return this.rank(p.realId) < this.rank(this.otp.realId)
51
+    }
52
+    getNextCandidate() {
53
+        if (this.matchCandidateIndex >= this.matchQueue.length) return null
54
+        this.matchCandidateIndex = this.matchCandidateIndex + 1
55
+        return this.matchQueue[this.matchCandidateIndex - 1]
56
+    }
57
+    engageTo(p) {
58
+        if (p.otp) {
59
+            p.otp.otp = null
60
+        }
61
+        if (this.otp) {
62
+            this.otp.otp = null
63
+        }
64
+
65
+        p.otp = this
66
+        p.fOrder.unshift(this)
67
+
68
+        this.otp = p
69
+        this.fOrder.unshift(p)
70
+        console.log(
71
+            'partners pref: ',
72
+            this.otp.matchQueue.map(f => f.realId).indexOf(this.realId) + 1,
73
+            'choice',
74
+        )
75
+    }
76
+}
77
+
78
+module.exports = class MatchService extends Schmervice.Service {
79
+    constructor(...args) {
80
+        super(...args)
81
+    }
82
+    async calcMatches(allQueuesByType) {
83
+        const seekerIds = Object.keys(allQueuesByType['seeker']).map(id => ({
84
+            profile_id: parseInt(id),
85
+            queue: allQueuesByType['seeker'][id],
86
+        }))
87
+        const posterIds = Object.keys(allQueuesByType['poster']).map(id => ({
88
+            profile_id: parseInt(id),
89
+            queue: allQueuesByType['poster'][id],
90
+        }))
91
+
92
+        const diff = Math.abs(posterIds.length - seekerIds.length)
93
+        const smallerList =
94
+            posterIds.length < seekerIds.length ? posterIds : seekerIds
95
+        // ADD DUMMY IDS TO THE SMALLER LIST
96
+        for (let d = 0; d < diff; d++) {
97
+            smallerList.push({ profile_id: 'dummy', queue: [] })
98
+        }
99
+        console.log(seekerIds)
100
+        console.log(posterIds)
101
+        // const yins = seekerIds.map(id => allProfileFacadesWithQueue[id])
102
+        // const yangs = posterIds.map(id => allProfileFacadesWithQueue[id])
103
+
104
+        // You only need to engage from one side
105
+        // engageEveryone(yins)
106
+        return []
107
+    }
108
+}

+ 28
- 1
backend/lib/services/matchqueue.js Просмотреть файл

@@ -4,13 +4,40 @@ module.exports = class MatchQueueService extends Schmervice.Service {
4 4
     constructor(...args) {
5 5
         super(...args)
6 6
     }
7
-
7
+    /**
8
+     * Gets a list of queue entries for profileId
9
+     * @param {number} profileId
10
+     * @returns {array} MatchQueue
11
+     */
8 12
     async getQueue(profileId) {
9 13
         const { MatchQueue } = this.server.models()
10 14
         return await MatchQueue.query()
11 15
             .where('profile_id', profileId)
12 16
             .andWhere('is_deleted', false)
13 17
     }
18
+    /**
19
+     * Returns queues by profile id by user type
20
+     * @returns {object}
21
+     */
22
+    async getAllQueues() {
23
+        const { MatchQueue } = this.server.models()
24
+        const queueEntries = await MatchQueue.query()
25
+            .andWhere('is_deleted', false)
26
+            .withGraphFetched('user')
27
+
28
+        const queueByProfileId = {
29
+            poster: {},
30
+            seeker: {},
31
+        }
32
+        queueEntries.forEach(entry => {
33
+            const type = entry.user.is_poster == 0 ? 'poster' : 'seeker'
34
+            if (!queueByProfileId[type][entry.profile_id]) {
35
+                queueByProfileId[type][entry.profile_id] = []
36
+            }
37
+            queueByProfileId[type][entry.profile_id].push(entry.target_id)
38
+        })
39
+        return queueByProfileId
40
+    }
14 41
     /**
15 42
      * Saves Scored Profile Ids to MatchQue IN ORDER
16 43
      * @param {number} profileId

+ 0
- 129
backend/lib/services/profile.js Просмотреть файл

@@ -2,88 +2,6 @@ const Schmervice = require('@hapipal/schmervice')
2 2
 const cosineSimilarity = require('compute-cosine-similarity')
3 3
 const haversine = require('haversine')
4 4
 
5
-const runMatch = (allYins, allYangs) => {
6
-    console.log(allYins.length, ':', allYangs.length)
7
-    // You only need to engage from one side
8
-    engageEveryone(allYins)
9
-
10
-    console.log('---')
11
-}
12
-const engageEveryone = allYins => {
13
-    let done
14
-    do {
15
-        console.log('rerunning...')
16
-        done = true
17
-        allYins.forEach(yin => {
18
-            // Keep matching if no true pairing is found
19
-            console.log(yin.realId, yin.otp?.realId)
20
-            if (!yin.otp) {
21
-                done = false
22
-                const yang = yin.getNextCandidate()
23
-                if (!yang.otp || yang.prefers(yin)) {
24
-                    yin.engageTo(yang)
25
-                }
26
-            } else {
27
-                console.log(yin.otp.realId)
28
-            }
29
-        })
30
-    } while (!done)
31
-}
32
-class ProfileFacade {
33
-    constructor(id, matchQueue) {
34
-        this.realId = id ? id : undefined
35
-
36
-        this.matchCandidateIndex = 0
37
-        this.otp = null
38
-        this.matchQueue = matchQueue?.length ? matchQueue : []
39
-        this.fOrder = []
40
-    }
41
-    clearPlaceholderMatches() {
42
-        this.matchQueue = this.matchQueue.filter(match => match.realId == false)
43
-        this.fOrder = this.fOrder.filter(
44
-            potentialFiance => potentialFiance.realId == false,
45
-        )
46
-        if (this.otp && this.otp.realId) {
47
-            this.otp = null
48
-        }
49
-    }
50
-    rank(id) {
51
-        const idQueue = this.matchQueue.map(
52
-            profileFacade => profileFacade.realId,
53
-        )
54
-        return idQueue.includes(id)
55
-            ? idQueue.indexOf(id)
56
-            : this.matchQueue.length + 1
57
-    }
58
-    prefers(p) {
59
-        return this.rank(p.realId) < this.rank(this.otp.realId)
60
-    }
61
-    getNextCandidate() {
62
-        if (this.matchCandidateIndex >= this.matchQueue.length) return null
63
-        this.matchCandidateIndex = this.matchCandidateIndex + 1
64
-        return this.matchQueue[this.matchCandidateIndex - 1]
65
-    }
66
-    engageTo(p) {
67
-        if (p.otp) {
68
-            p.otp.otp = null
69
-        }
70
-        if (this.otp) {
71
-            this.otp.otp = null
72
-        }
73
-
74
-        p.otp = this
75
-        p.fOrder.unshift(this)
76
-
77
-        this.otp = p
78
-        this.fOrder.unshift(p)
79
-        console.log(
80
-            'partners pref: ',
81
-            this.otp.matchQueue.map(f => f.realId).indexOf(this.realId) + 1,
82
-            'choice',
83
-        )
84
-    }
85
-}
86
-
87 5
 const magic = 1000
88 6
 const scoreResponses = (seeker, potentialMatch) => {
89 7
     if (seeker.responses.length != potentialMatch.responses.length)
@@ -349,54 +267,7 @@ module.exports = class ProfileService extends Schmervice.Service {
349 267
         // Order by score
350 268
         return scoredProfilesWithDistance.sort((a, b) => a.score - b.score)
351 269
     }
352
-    async calcMatches() {
353
-        const { Profile } = this.server.models()
354 270
 
355
-        // Grab all profiles with matchQueues
356
-        let allProfiles = await Profile.query().withGraphFetched('user')
357
-        const seekerIds = allProfiles
358
-            .filter(profile => profile.user.is_poster == 0)
359
-            .map(profile => profile.profile_id)
360
-        const posterIds = allProfiles
361
-            .filter(profile => profile.user.is_poster == 1)
362
-            .map(profile => profile.profile_id)
363
-        let diff = Math.abs(posterIds.length - seekerIds.length)
364
-        let smallerList =
365
-            posterIds.length < seekerIds.length ? posterIds : seekerIds
366
-        // ADD DUMMY IDS TO THE SMALLER LIST
367
-        for (let d = 0; d < diff; d++) {
368
-            smallerList.push(allProfiles.length + d)
369
-        }
370
-        // !:FAKE Score everyone
371
-        const scoredProfileQueuesById = {}
372
-        for (let profile of allProfiles) {
373
-            const profileQueue = await this.scoreProfilesFor(
374
-                profile.profile_id,
375
-                10000,
376
-                'mile',
377
-            )
378
-            scoredProfileQueuesById[profile.profile_id] = profileQueue.map(
379
-                profile => profile.profile_id,
380
-            )
381
-        }
382
-        const allProfileFacadesWithQueue = allProfiles.map(profile => {
383
-            const profileFacadeQueue = scoredProfileQueuesById[
384
-                profile.profile_id
385
-            ].map(id => {
386
-                const subQueue = scoredProfileQueuesById[id].map(
387
-                    id => new ProfileFacade(id),
388
-                )
389
-                return new ProfileFacade(id, subQueue)
390
-            })
391
-            return new ProfileFacade(profile.profile_id, profileFacadeQueue)
392
-        })
393
-        // // ! FAKE -- END
394
-        const yins = seekerIds.map(id => allProfileFacadesWithQueue[id])
395
-        const yangs = posterIds.map(id => allProfileFacadesWithQueue[id])
396
-
397
-        runMatch(yins, yangs)
398
-        return yins
399
-    }
400 271
     /**
401 272
      * Use the db for zipcode info
402 273
      * @param {string} zipCode

Загрузка…
Отмена
Сохранить