Mohammad Jawad Barati
Mohammad Jawad Barati
خواندن ۶ دقیقه·۴ سال پیش

ساختن یه سیستم matchmaking توی نود.جی‌اس

مفهوم match making چیه؟ بیاید فرض کنیم شما وب سایت پونیشا رو راه انداختید. حالا میخواید به کسایی که ثبت سفارش پروژه میکنن پیشنهاد بدی که از بین این برنامه نویسایی که برای اجرا کردن پروژه ی تو درخواست فرستادن کدوم خوب تر هست. یعنی بیای بر اساس یه سری پارامتر به کسی که پروژه رو ثبت کرده بگی بر اساس مثلا خوش قولی و کیفیت کار فلان برنامه نویس امتیاز ۹۰ گرفته و فلانی امتیاز ۶۷ و ...

البته اصل این مفهوم رو من تا الان بیشتر توی game و business دیدم. مثالش میتونه یه چیزی مثل پکیج matchmaker و tiny-matchmaking. یا این ریپوزیتوری گیت هاب باشه.

پس در کل میشه گفت matchmaking میاد بر اساس پارامتر هایی مگه دو تا چیز چقدر با هم برابر هستن. چیزی که مشخصه اینه که امتیاز هر فرد توی اون پارامتر ها از قبل مشخص شده است. البته ممکنه که امتیاز number نباشه و boolean باشه.

برای این تعیین امتیاز هم الگوریتم داریم یا اینکه فرد مستقیم میاد اونو وارد میکنه. بعد از هر بار وارد شدن مقدار جدید باید مجموع/میانگین امتیاز فرد توی اون پارامتر دوباره حساب بشه. اگه قرار باشه میانگین حساب بشه باید مقادیر قبلی اون پارامتر رو داشته باشید تا بتونید میانگین بگیرید.

چیزی هم که ممکنه اتفاق بیفته اینه که شما بگی من یه حداقلی برای سیستمم تعیین میکنم، یعنی اگه کاربری امتیازش کمتر از اون شد اصلا توی نتیجه نهایی نشونش نمیدم. این ممکنه برات این مشکل رو بوجود بیاره که اصلا هیچ کاربری قبول نشه. پس بای به فکر بود که توی این شرایط چکار کرد.



تعریف چند تا واژه

  • لغت skill system: یه معیار که بهت میگه کاربرت چقدر خوبه
  • لغت ranking system: سیستمی که به کاربرات میگه چقدر خوب یا بد هستن. این سیستم ممکنه از skill system استفاده بکنه یا نکنه.
  • لغت matchmaking system: سیستمی که کاربرا رو با هم match میکنه. ممکنه از skill system استفاده بکنه، ممکنه از ranking system استفاده بکنه ولی چیزی که مشخصه اینه که matchmaking system روی دو تا سیستم دیگه تاثیر میزاره.
https://www.dideo.ir/v/yt/-pglxege-gU/skill%2C-matchmaking%2C-and-ranking-systems-design

چند دقیقه اول این فیلم رو ببینید.



خب قبل از هر چیزی بیاید مرحله به مرحله کار رو پیش ببریم. اول یه کالکشن ایجاد بکنیم تا روش بتونیم تست هامون رو اجرا کنیم. یه فایل به اسم point.schema.js ایجاد کنید :

// @ts-check const { Schema } = require('mongoose'); const pointSchema = new Schema({ type: { type: String, enum: ['Point'], default: 'Point', }, coordinates: { type: [Number], required: true, }, }); module.exports = { pointSchema };

ما type های مختلفی توی GeoJSON میتونیم داشته باشیم؛ Point که اشاره به یه نقطه روی نقشه داره، Polygon که یه محدوده رو در بر میگیره، LineString که توضیح دادنش با مثال ساده تره: فرض کن میخوای track جاهایی رو که رفتی نگهداری، در این صورت میتونی از این نوع استفاده بکنی. نوع های MultiPoint و MultiPolygon و MultiLineString هم داریم که چند تا نقطه یا polygon یا LineString رو نگه میدارن.

و یه فایل test.schema.js

// @ts-check const { Schema, model } = require('mongoose'); const { pointSchema } = require('./point.schema'); const COLLECTION_NAME = 'test'; const schemaName = new Schema( { location: { type: pointSchema, index: '2dsphere', }, }, { timestamps: true, collection: COLLECTION_NAME, }, ); const SchemaModel = model(COLLECTION_NAME, schemaName);

کاری که میخوایم بکنیم اینه که روی location یه matchmaking درست کنیم. خب مثل همیشه بیاید اول با کلیت آشنا بشیم. برای اینکه بتونید کوئری زیر رو اجرا بکنید باید اول روی فیلد GeoJSON خودتون index گذاری بکنید. به همین خاطر ما فیلد location رو index گذاری کردیم.

چند نوع ایندکس گذاری داریم:

  • نوع 2d: ایندکس گذاری قدیمی برای زمانی که داری فیلدت رو توی آبجکت های GeoJSON ذخیره نمیکنی هست.
  • نوع 2dsphere که نوع جدید ایندکس گذاری هست و توی اکثر موارد استفاده میشه چونکه حتی از روش قدیمی ذخیره دیتا پشتیبانی میکنه.
روش قدیمی ذخیره لوکیشن.
روش قدیمی ذخیره لوکیشن.
  • نوع geoHaystack که use case خاص خودش رو داره و من اصلا نمیدونم چیه. پس کلا بهتره شما هم فعلا بیخیالش بشید.

برای جزئیات بیشتر این ویدئو رو ببینید:

https://aparat.com/v/TSBMy

حالا بیاید سراغ کوئری بریم:

const result = await SchemaModel.find({ location: { $near: { $maxDistance: 100000, $geometry: { type: 'Point', coordinates: [36.3, 59.41], }, }, }, });

اگرم چیزی به عنوان خروجی برگشت داده نشد اون عددای coordinates و maxDistance رو عوض کنید. این کوئری رکورد هایی رو بر میگردونه که near (نزدیک) geometry داده شده هستن.

حالا این maxDistance چی هست؟

  • تعیین کننده حداکثر فاصله ای هست که به عنوان near حساب میکنه
  • واحدش متر هست
میخوای بیشتر در مورد operator های MongoDB بدونی؟
میخوای بیشتر در مورد operator های MongoDB بدونی؟

نکته مهم:

You cannot combine the $near operator, which requires a special geospatial index, with a query operator or command that requires another special index. For example you cannot combine $near with the $text query.

مثلا اگه میخوای or بزنی و بگی چند تا عملگر near داشته باشی باید چند تا کوئری مجزا بزنی و نتیجه رو با هم ترکیب بکنی. نکته بعدی اینه که near باید توی root کوئری باشه و نمیتونی اونو بزاری توی level های پایین تر. همون طوری که توی این لینک توضیح داده شده.

رفرنس هر دو تا نکته ی بالایی.



خب ولی کاری که من اینجا میخوام بکنم اینه که یه کاربر داریم که ما ۴ تا لوکیشن مختلف ازش داریم:

  • محل کارگاهش
  • محل زندگیش
  • محل نمایشگاهش
  • و محل دفترش

حالا میخوایم بگیم که کدوم کاربرا، یکی از این لوکیشن هاشون به یه نقطه خاص، نزدیک هست. اون کاربرا باید اطلاعاتشون برگشت داده بشه. پس با operator (عملگر) near مشکل حل نمیشه، یا اینکه باید ۴ تا کوئری بزنیم. پس میریم سراغ عملگر geoWithin.

عملگر geoWithin

از این عملگر توی aggregate ها استفاده میکنیم. این عملگر داکیومنت هایی رو بر میگردونه که location اونها توی یه محدوده خاص هست. حالا این محدوده رو میتونی با polygon یا ... تعیین بکنی. ما عملگر های shape مختلفی توی MongoDB داریم:

  • عملگر box
  • عملگر polygon
  • عملگر center
  • عملگر centerSphere

این دو تا عملگر آخری برامون یه دایره میکشن. عملگری که در ادامه مخیوام توضیح بدم عملگر centerSphere هست. این عملگر یه آرایه میگیره. این آرایه دوتا عضو بیشتر قبول نمیکنه. ایندکس صفر باید coordinates مرکز دایره باشه و ایندکس دوم باید مشخص کننده شعاع دایره باشه.

این شعاع به مایل هست مثلا ۱۰ مایل. البته نکته ای که هست اینه که باید این ۱۰ مایل رو تقسیم بر شعاع استوایی زمین بکنید. شعاع استوایی زمین به مایل 3963.2 هست. پس در نهایت یه همچین چیزی داریم:

همون طوری که میبینید توی کوئری بالا گفتم تا شعاع یک کیلومتر بگرده و هر کی خونش یا دفتر کارش توی دایره ای که مرکز اون coordinates ای که دادم هست رو برام برگردونه.

nodejsdeveloperskhmatchmakingnodejsmongodbmongodb operators
برنانه نویس، مدرس، محقق. عاشق انیمه هستم و دنبال چالش ها جدید.
شاید از این پست‌ها خوشتان بیاید