مهدی
مهدی
خواندن ۴ دقیقه·۱ سال پیش

آمار مشاهده با influxDB و Go

درود دوستان عزیزم. MVP اریموت(سامانه استخدام دورکاری: https://oremote.org) رو که بالا آوردم اول صرفا شماره بازدید هارو توی هر نمونه شغل پست شده آپدیت می‌کردم و مثلا نشون میدادم فلان آگهی فلان قدر بازدید داشته.

یادتون باشه دیگه ما اول یه چیزی میسازیم، بعد مدام بهتر و بهترش می‌کنیم و بهش فیچر اضافه می‌کنیم، کمال‌گرا نشید.

بعد که یکی دوبار آگهی توی سایت دیوار ثبت کردم، دیدم یه قسمتی پایینش آمار بازدید روزانه رو میزنه و گفتم ایول چه خفن منم اضافه کنمش.

اول گفتم خب همون با دیتابیسی که نوشتم بیام تایم استمپ یونیکسی رو بریزم توی دیتابیس ولی بعدش پشیمون شدم و گفتم بجاش از یه دیتابیس تایم سریز استفاده کنم از اونجایی که با زمان سروکار دارم بهتره برم سراغ چیزی که براش ساخته شده و اولین چیزی که تو ذهنم اومد اینفلاکس دیبی بودش، چون داشبورد خیلی خوشگلی داشت یادم مونده بود :)

و البته یه ایمج داکر نسبتا سبک داره که خیلی راحت میتونید ران کنید و توی سیستم لوکالتون تستش کنید.


اگر نمی‌دونید: تایم استمپ یونیکسی (Unix timestamp) یک عدد صحیح است که تعداد ثانیه‌های گذشته از ۱ ژانویه ۱۹۷۰، ۰۰:۰۰:۰۰ UTC را نشان می‌دهد.


حالا روند اینطوری بود، که من برای افزایش تعداد بازدیدها یه روت تعریف کرده بودم که میومد شغل رو میگرفت و میزان view رو توی سند دیتابیسش افزایش میداد (فریمورک Gin رو استفاده کردم)

GET /api/v1/job/view/:job_id

و این میومد تو دیتابیس مانگو ویو رو اپدیت میکرد.


حالا تو ورژن جدید اومدم چکار کردم؟

اومدم به ازای هر بازدید یه نقطه توی باکت(bucket) اینفلاکس دیبی ایجاد کردم.

دوستان نمیدونم چرا ویرگول دبل کوتیشن رو درست نشون نمیده، این &quot ها که نوشته شده، دبل کوتیشنه(")
org := &quottest-org&quot bucket := &quottest-bucket&quot writeAPI := InfluxClient.WriteAPIBlocking(org, bucket) point := influxdb2.NewPoint(&quotjob_view&quot, map[string]string{&quotjob_id&quot: instance.ID.Hex()}, map[string]interface{}{ &quotunix_ts&quot: time.Now().Unix(), }, time.Now(), ) err = writeAPI.WritePoint(context.TODO(), point) if err != nil { return err }

که البته اون یونیکس تایم استمپ هم نیاز نبود بیارمش ولی چون عددیه، احتمالا بعدا راحت تر بتونم باهاش کوئری فلاکس بنویسم.(نمیدونم شایددد)


و حالا که ثبت کردم باید یه روشی هم برا نمایشش ایجاد میکردم، از اونجایی که هر آگهی باید ۳۱ روز فعال باشه، من دیتای بازدید ۳۱ روز قبل تا حالا برای هر شغل رو نیاز دارم.

و اگر اون روزا پوینتی توی اینفلاکس نداشته باشم باید صفر رو برگردونم یعنی بازدیدی نبوده(یا آگهی ثبت نشده بوده هنوز، یا بازدید صفر بوده)

حالا ما اومدیم نقطه مون رو بر اساس ثانیه ثبت کردیم، چطوری بفهمیم اقا این نقطه(ها) تو مال کدوم روزن؟ اینجاست که دیتابیس time series به کمک میاد با توابع built-inش

org := &quottest-org&quot bucket := &quottest-bucket&quot queryAPI := InfluxClient.QueryAPI(org) query := fmt.Sprintf(` import &quottimezone&quot option location = timezone.location(name: &quotAsia/Tehran&quot) from(bucket: &quot%s&quot) |> range(start: -30d) |> filter(fn: (r) => r._measurement == &quotjob_view&quot and r.job_id == &quot%s&quot) |> map(fn: (r) => ({_time: r._time, _value: 1})) |> aggregateWindow(every: 1d, fn: sum, location: location) |> fill(value: 0) `, bucket, jobID) results, err := queryAPI.Query(context.Background(), query) if err != nil { return err }

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

اول از همه اومدم گفتم به باکت test-bucket میخوام کوئری بزنم، (توی تایم زون تهران)

بعد گفتم رنج زمانی از ۳۰ روز پیش میخوام باشه،

بعد اومدم یه تابع فیلتر نوشتم که بتونم بر اساس فیلد job_view و بر اساس کد شغل job_id که به طور string داشتمش، فیلتر کنه، اینطوری میاد تمام نقاط مربوط به اون شغل رو فیلتر میکنه،

بعد یه تابع مپ که قطعا توی برنامه نویسی فانکشنال باهاش اشنایی دارید رو روش اجرا کردم که به ازای تمام رکورد هایی که بهش رسیده، مقدار ستون _value رو یک قرار بده(چون هر بازدید یه دونس)،

بعدش گفتم aggregate (تجمیع، توده ساختن) انجام بده، توی بازه‌ی ۱ روزه، از اونجایی که من بازدید روزانه رو فعلا نیاز دارم، و هنگام اون توده سازی میخوام مقادیری که به ستون _value دادم رو جمع بزنم بنابرین تابع fn ما میشه sum،

و نهایتا با fill جاهایی که مقدار نداره رو صفر قراردادم(بعضی روزا ممکنه بازدیدی نباشه).

(من تازه با اینفلاکس اشنا شدم و احتمال خیلی زیاد روش بهینه تری برای کوئری زدن هست با ریسک خودتون از کد استفاده کنید.)

حالا که اینارو گرفتم باید با زمان ایران تنظیمش کنم و روزایی که دیتا ندارم رو صفر کنم.

قبلش یه نوع متغییر معرفی میکنم برای نگه داشتن شکل و شمایل امار:

بعدشم ازش استفاده می‌کنم.

این دیگه کد go هستش و توضیحی ندارم روش، اگر سوالی داشتید یا بهبود مد نظرتون بود(من سنیور نیستم) توی کامنتا اعلام کنید.

و اخرشم dailyCounts رو میدم به ctx.JSON که به کاربر برگردونم به صورت جیسون/جیسان؟؟؟ نمیدونم.

جیسون رو هم دو قسمتی میکنم به مقادیر ایکس و ایگرگ و میدمش به چارت جی اس(خود فرانت با ناکست نوشته شده) و نتیجه کارم فعلا شده این:

مقاله من اینجا تموم شد، خوشحال میشم برید https://oremote.org رو ببینید، ثبت آگهی حالا حالا ها توش رایگانه، اگر انتقاد/پیشنهاد چه از لحاظ دیزاین چه از لحاظ فنی بود بگید.

اریموت هنوز یوزر زیادی نداره، و یکی از قدمای بعدیم براش نوشتن ریکامندیشن سیستمه(سیستم پیشنهاد دهنده شغل) ولی برای اینکار دیتا نیاز دارم و برای دیتا یوزر نیاز دارم :)

موفق و پیروز باشید. بدرود.



برنامه نویسیgolang
{شاید} توسعه دهنده وب https://github.com/blackestwhite
شاید از این پست‌ها خوشتان بیاید