<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0">
    <channel>
        <title>نوشته های Hadi Varposhti</title>
        <link>https://virgool.io/feed/@hadivarposhti</link>
        <description>سعی میکنم چیزی رو بنویسم که نیاز آدما باشه</description>
        <language>fa</language>
        <pubDate>2026-06-16 17:12:00</pubDate>
        <image>
            <url>https://files.virgool.io/upload/users/111073/avatar/B8VJaO.png?height=120&amp;width=120</url>
            <title>Hadi Varposhti</title>
            <link>https://virgool.io/@hadivarposhti</link>
        </image>

                    <item>
                <title>همه اون چیزی که در رابطه با کلاسترینگ الستیک سرچ باید بدونین</title>
                <link>https://virgool.io/@hadivarposhti/%D9%87%D9%85%D9%87-%D8%A7%D9%88%D9%86-%DA%86%DB%8C%D8%B2%DB%8C-%DA%A9%D9%87-%D8%AF%D8%B1-%D8%B1%D8%A7%D8%A8%D8%B7%D9%87-%D8%A8%D8%A7-%DA%A9%D9%84%D8%A7%D8%B3%D8%AA%D8%B1%DB%8C%D9%86%DA%AF-%D8%A7%D9%84%D8%B3%D8%AA%DB%8C%DA%A9-%D8%B3%D8%B1%DA%86-%D8%A8%D8%A7%DB%8C%D8%AF-%D8%A8%D8%AF%D9%88%D9%86%DB%8C%D9%86-lirzsizzdxum</link>
                <description>من به تناسب نیازی که داشتم مدتها درگیر این بودم که چطور میشه یک از ابزاری مثل الستیک سرچ بدرستی استفاده کرد.از اونجایی که تقریبا هیچ مستندفنی فارسی درستی که بتونه کمک بکنه اصلا وجود نداره و قالب مستند ها مقالات فنی موجود هم تقریبا کلی گویی کردن  یا بعضا خیلی نقص دارن و گاهی حتی تکرارین سعی کردم، توی این مقاله بصورت خلاصه همه اون چیزی که شما لازم دارین تا یک الستیک سرچ پایدار از نظر منابع، قابل توسعه با کمتر هزینه داشته باشین رو براتون اماده کنم امیدوارم که بدردتون بخوره.از روزی که شروع کردم به خوندن راجع به این ابزار یکی از عمده مواردی که بهش برخوردم، مسئله کلاستر کردن بود که به جد میشه گفت تقریبا هیچ کجا شما نمیتونین منبعی پیدا کنین توضیح درستی بده که کلاسترینگ اصلا یعنی چی ؟ خیلی سر راست بگه ایا شما نیاز دارین که الستیک سرچ خودتون رو کلاسترکنین یا نه و خیلی سوالای دیگه.توی این نوشته میخوام بهتون توضیح بدم که چطور میتونین چنتا نود الستیک سرچ رو باهم کلاستر کنین. قبلش باید اضافه کنم که این روش رو روی Fedora ۳۰/Fedora ۲۹/CentOS ۷ هم تست کردم، پس خیالتون راحت باشه از بابتش.خب در قدم اول باید بگم که کلاستر کردن الستیک سرچ اصلا یعنی چی؟ کلاستر الستیک سرچ رو به گروهی از نودها میگن که cluster.name یکسانی داشته باشن.هرنودی که به کلاستر اضافه و کم میشه، کلاستر بصورت اتوماتیک خودش رو باز سازماندهی میکنه تا توزیع داده ها روی نودها رو مدیریت کنه. اینکار باعث افزایش ظرفیت و تضمین پایداری کلاستر میشه.هر نودی توی کلاستر الستیک سرچ وظیفه خاصی رو برعهده داره که به ترتیب توضیح میدم:مستر نود (Master-eligible node) - به نودی  گفته میشه که صفت node.master بصورت پیش فرض بر روی اون true باشه. این نود در کلاستر مسئول کارهایی مثل حذف یا ایجاد ایندکس ها، رهگیری (tracking) نودهایی که عوضی از کلاستر هستن و تصمیم اینکه کدوم شارد باید به کدوم نود الحاق بشه بر عهده مستر نود.نود داده (Date node) - به نودی گفته میشه که صفت node.data بصورت پیش فرض بر روی اون true باشه. این نود داده ها رو بروی خودش نگه میداره و وظایفی مثل جستجو، تجمیع داده (aggregations)‌ و اعمالی مثل CRUD رو روی داده ها اعمال میکنه.اینگست نود (Ingest node) - به نودی گفته میشه که صفت node.ingest بصورت پیش فرض بر روی اون true باشه. اینگست نود وظیفه داره تا اینگست پایپ لاین ها رو بر روی ایندکس، قبل از اینکه ساخته بشه اعمال بکنه.نود هماهنگ کننده (Coordinating node) - وظیفه اصلی این نود مسیریابی جستجو و فهرست بندی درخواست ها از مشتریان به دادهاست، که درواقع داره مثل یک لود بالانسر برای ما عمل میکنه.راه اندازی کلاستر الستیک سرچتوی این نوشته ، من میخوام یک کلاستر Elasticsearch با سه تا نود راه‌اندازی کنم که هر نود واجد شرایط مستر نود باشه.نصب الستیک سرچ بر روی فدورااز اونجایی که این نوشته قرار فقط در رابطه با توضیح روش کلاستر کردن باشه وارد جزئیات نصب نمیشم اما شما میتونین با کمک لینک هایی که قرار میدم روش نصب روی فدورای ۲۹ و فدورای ۳۰ رو یاد بگیرین.راه اندازی کلاستر الستیک سرچزمانی که فرایند نصب الستیک سرچ بر روی  نودهاتون انجام شد، زمان شروع کلاستر کردن نودها میشه.نام گذاری در کلاستر الستیک سرچتو این مرحله لازمه که توی تمامی نودهاتون فایل تنظیمات الستیک رو باز کنین و اسمی رو که برای کلاسترتون در نظر دارین روی اون ست کنین.مسیری که باید فاید تنظیمات رو توی اون پیدا کنینvim /etc/elasticsearch/elasticsearch.yml&gt; نود اول... 
# ---------------------------------- Cluster ----------------------------------- 
#
# Use a descriptive name for your cluster:
# cluster.name: es-nodes-01
...&gt; نود دوم...
# ---------------------------------- Cluster -----------------------------------  
# 
# Use a descriptive name for your cluster: 
# cluster.name: es-nodes-02
...&gt; نود سوم... 
# ---------------------------------- Cluster -----------------------------------   
#  
# Use a descriptive name for your cluster:  
# cluster.name: es-nodes-03
...غیرفعال کردن Swapping حافظهقبل از هرچیزی باید بگم که Swapping بر روی پایداری کلاستر الستیک سرچ تاثیر داره، و حتی ممکن باعث کند شدن نودها و یا جدا شدن اونها از کلاستر بشه. یکی از راههای غیرفعال کردن Swapping حافظه، فعال کردن مموری لاک روی کلاسترتونه. فقط کافیه که خط bootstrap.memory_lock: true از کامنت خارج کنین.... 
# ----------------------------------- Memory ----------------------------------- 
# 
# Lock the memory on startup: 
#
 bootstrap.memory_lock: true 
...البته پشنهادم به شما اینه که اگه الستیک رو بصورت سرویس نصب کردین، swapping رو از طریق ویرایش فایل سرویس، الستیک سرچ با کمک systemd و اضافه کردن خط زیر بهش غیر فعال کنینsystemctl edit elasticsearch[Service] 
LimitMEMLOCK=infinityاحتمالا میدونین هرباری که یه سرویسی رو ویرایش میکنین، بعد از ویرایش باید اونو یکبار reload کنین تا تغییراتتون اعمال بشه، اینکارم با کمک دستور زیر انجام میشه.sudo systemctl daemon-reloadیکی دیگه از روشهای پیشنهادی هم غیرفعال کردن کامل swapping روی سیستم عامله البته بشرطی که الستیک سرچ تنها سرویسی باشه که قرار روی اون سیستم عامل کار کنه. اینکارم میتونین از طریق دستور زیر انجام بدین.swapoff -aمشخص کردن  وظیفه نودهاهمانطور که در بالاترم گفتم، می توانین به هر نود یه وظیفه مشخص اختصاص بدین. در این تنظیمات، من هر سه نود رو به گونه ای پیکربندی می کنم که هم به عنوان مستر نود و هم به عنوان نود داده عمل کنند.... 
# ---------------------------------- Cluster ----------------------------------- 
# 
# Use a descriptive name for your cluster: 
# 
cluster.name: es-nodes  
node.master: true 
node.data: true
...وصل کردن نودها بهملازمه که اشاره کنم، الستیک سرچ بصورت پیش فرض از پروتکل TCP و پورت ۹۲۰۰ برای ارتباط  با بیرون  استفاده میکنه و پروتکل TCP هم از پورت ۹۳۰۰ - ۹۴۰۰ برای ارتباط بین نود ها استفاده میکنه.تنظیمات کلاستردر تنظیمات الستیک سرچ دو نکته مهم وجود داره که باید قبل از شروع بکار کلاسترمون پیکربندی بشن تا نود ها بتونند همدیگرو ببینن و نود اصلی رو انتخاب کنن.discovery.seed_hosts و cluster.initial_master_nodesگزینه discovery.seed_hosts لیستی از نودهای اصلی داخل کلاسترو نگه میداره و به صورت host:port و یا host نمایششون میده. البته مقدار پورت رو توی نسخه های جدید برابر با مقدار transport.profiles.default.port قرار میدن و بصورت جداگونه ای نمایش داده میشه اما توی نسخه های قدیمی تر این مقدار برابر بوده با discovery.zen.ping.unicast.hosts.... 
# --------------------------------- Discovery ---------------------------------- 
# 
# Pass an initial list of hosts to perform discovery when this node is started: 
# The default list of hosts is [&amp;quot127.0.0.1&amp;quot, &amp;quot[::1]&amp;quot] 
# 
discovery.seed_hosts: [&amp;quotnode1:ip&amp;quot, &amp;quotnode2:ip&amp;quot, &amp;quotnode3:ip&amp;quot] 
...اما در رابطه با گزینه cluster.initial_master_nodes باید بگم که وظیفش مشخص کردن مستر نود اصلی برای اولین بار. این اتفاق زمانی مهم میشه که برای اولین بار دارین کلاسترتون رو بالا میارین. و اینکه این گزینه وقتی که کلاسترتون پایدار شد توسط خود کلاستر کنار گذاشته میشه.... 
# Bootstrap the cluster using an initial set of master-eligible nodes: 
#
# #cluster.initial_master_nodes: [&amp;quotnode-1&amp;quot, &amp;quotnode-2&amp;quot] 
cluster.initial_master_nodes: [&amp;quotes-node-01&amp;quot, &amp;quotes-node-02&amp;quot, &amp;quotes-node-03&amp;quot] 
...مشخص کردن اندازه Heap sizeالستیک سرچ مقدار heap size رو بصورت پیش فرض ۱GB قرار میده، اما بر اساس قانون thump مقدار Xmx نباید از ۵۰ درصد از حافظه فیزیکی بیشتر بشه اما نه بیشتر از ۳۲GB این به این معنی که شما در بیشتر حالت مقدار heap size رو میتونین ۱۶GB قرار بدین.vim /etc/elasticsearch/jvm.options... 
################################################################  
# Xms represents the initial size of total heap space 
# Xmx represents the maximum size of total heap space  
-Xms1g 
-Xmx1g 
...سایر تنظیمات مهمتنظیمات حافظه مجازیالستیک سرچ بطور پیش فرض از  تنظیمات mmapfs برای ذخیره ایندکس ها استفاده میکنه. برای اینکه مطمئن بشین بیشتر از مقدار حافظه مجازیتون استفاده نمیشه لازمه که فایل etc/sysctl.conf/  ویرایش کنین و مقدار vm.max_map_cpunt همونطوری که پایینتر توضیح میدم قرار بدین.لازمه که اضافه کنم این تنظیمات قبل از راه اندازی کلاستر توسط کلاستر چک میشه و درصورتی که تنظیم نباشه کلاسترتون شروع به کار نمیکنه.vm.max_map_count=262144راه دیگه ای که میتونین این مقدار روتنظیم کنین با استفاده از دستوری که اینجا مینویسمecho &amp;quotvm.max_map_count=262144&amp;quot &gt;&gt; /etc/sysctl.confسیستم را مجددا راه اندازی کنین تا تغییراتی که دادین اعمال بشه و بعد دستور sysctl vm.max_map_count را برای اینکه مطمئن بشیم تنظیماتمون درست انجام شده اجرا میکنیم.sysctl vm.max_map_count 
vm.max_map_count = 262144باز کردن پورت های الستیک سرچ در فایروالهمونطور که در بالاتر گفتم،   الستیک سرچ  از طریق پورت TCP ۹۲۰۰  و به نود ها اجازه می ده ارتباط با بیرون و از طریق  پورت TCP ۹۳۰۰-۹۴۰۰ بین نودهای داخل کلاستر باهم ارتباط برقرار کنن. باید این پورت ها را روی فایروال باز باشن.firewall-cmd --add-port={9200,9300-9400}/tcp --permanent
firewall-cmd --reloadاجرای الستیک سرچتو قدم اول یکبار systemd رو reload کنینsystemctl daemon-reloadالستیک سرچ رو تنظیم کنین که با بالا اومدن سیستم عامل فعال بشه.systemctl enable elasticsearchدرنهایت الستیک سرچ رو استارت کنینsystemctl start elasticsearchمیتونین وضعیت الستیک رو با کمک دستور زیر چک کنین که ایا شروع به کار کرده یا نهsystemctl status elasticsearch.serviceبرای بررسی خطا ها هم میتونین از دستور زیر کمک بگیرینtail /var/log/elasticsearch/&lt;cluster-name&gt;.logچک کردم وضعیت سلامت کلاستر هاcurl -X GET &amp;quotnode:ip:9200/_cluster/health?pretty&amp;quot{   
&amp;quotcluster_name&amp;quot : &amp;quotes-nodes&amp;quot,   
&amp;quotstatus&amp;quot : &amp;quotgreen&amp;quot,   
&amp;quottimed_out&amp;quot : false,  
 &amp;quotnumber_of_nodes&amp;quot : 3,   
&amp;quotnumber_of_data_nodes&amp;quot : 3,  
 &amp;quotactive_primary_shards&amp;quot : 0,   
&amp;quotactive_shards&amp;quot : 0,   
&amp;quotrelocating_shards&amp;quot : 0,   
&amp;quotinitializing_shards&amp;quot : 0,  
 &amp;quotunassigned_shards&amp;quot : 0,   
&amp;quotdelayed_unassigned_shards&amp;quot : 0,   
&amp;quotnumber_of_pending_tasks&amp;quot : 0,  
 &amp;quotnumber_of_in_flight_fetch&amp;quot : 0,  
 &amp;quottask_max_waiting_in_queue_millis&amp;quot : 0,   
&amp;quotactive_shards_percent_as_number&amp;quot : 100.0 
}وضعیت سلامت نودها عبارتند از: سبز، زرد یا قرمز. وضعیت قرمز نشان می‌ده که شاردها خاص در نود تخصیص داده نمی‌شن، زرد به این معنی  که شاردهای اصلی تخصیص داده شدن اما کپی‌ها اختصاص داده نمیشن، و سبز به این معنی است که همه شاردها تخصیص داده شده‌اند.در اخر:توی نوشتن این مطلب از جاهای مختلفی کمک گرفتم که بتونم کامل ترین نسخه ممکن براتون اماده کنماگه جایی از این نوشته براتون سخت و یا نامفهوم خوشحال میشم که بتونم براتون توضیح بدم و کمک به درک بهترش کنم.قطعا از من ادمهای متخصص تری وجود داره اگه ایرادی توی این نوشته دیدن خوشحال میشم بهم بگین تا بتونم اصلاح کنم  تا ادمای بیشتری بتونن از این مطلب استفاده کنن.سعی کردم مثل همیشه به ساده ترین حالت ممکن بنویسم اما خب مطلب تا حدی تخصصی و یجاهایی واقعا سخت میشه.و اینکه احتمالا این اخرین نوشتم در رابطه به الستیک سرچ باشه مگه اینکه عنوانی ببینم که حس کنم لازم باشه در رابطه باهاش بنویسم.و در نهایت هم امیدوارم براتون مفید باشه، بتونین ازش کمک بگیرین توی پروژهاتون اگه هم به مشکل خوردین خوشحال میشم بتونم کمکتون کنم.</description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Sat, 16 Dec 2023 22:59:14 +0330</pubDate>
            </item>
                    <item>
                <title>مدیریت خطاها در لاگ استش</title>
                <link>https://virgool.io/@hadivarposhti/%D9%85%D8%AF%DB%8C%D8%B1%DB%8C%D8%AA-%D8%AE%D8%B7%D8%A7%D9%87%D8%A7-%D8%AF%D8%B1-%D9%84%D8%A7%DA%AF-%D8%A7%D8%B3%D8%AA%D8%B4-sv2eqqq1izuf</link>
                <description>احتمالا با این مشکل برخوردین که وقتی الستیک رو راه انداختین و دارین داده های موردنیازتون رو با کمک لاگ استش داخلش میفرستین اگر سیستمتون خطایی داشته باشه، لاگی که بشه ازش کمک گرفت تا مشکل رو فهمید تقریبا وجود نداره،مگر اینکه برای مثال از داکر استفاده کرده باشین که اونم به خاطر حجم بالای داده های ورودی اضافی که همراه هر ایونت میاد در بهترین حالت واقعا گیج کنندس. حالا چطور میشه این مشکل رو حل کرد؟ یکی از راه حل هایی که وجود داره  استفاده از ((دد لتر کیو)) Dead letter queue که با توجه به امکاناتی که داره حداقل میشه بهش فکر کرد.[از این به بعد به اختصار میگم dlq]براساس تعریفی که خود وبسایت الستیک درباره dlq میگه :The dead letter queue (DLQ) is designed as a place to temporarily write events that cannot be processed.dlq به عنوان مکانی موقتی برای ذخیره ایونت هایی که قابل پردازش نیستن طراحی شده است. براساس همین تعریف که نیاز ما به dlq مشخص میشه، بیاید تصور کنیم که شما با توجه به نیاز کسب و کارتون لازم دارین تا لاگ های ورودی رو جایی نگه دارین و این لاگ ها بسیار زیادن بطوری که ایونت های ورودی در ساعت بیشتر از چند هزارتاست.با تصور همین سناریو هم پیدا کردن مشکل تقریبا کار غیر ممکنی به حساب میاد پس لازم که بجای بررسی همه ایونت ها به لاگ استش بگیم که اونهایی رو که قابل پردازش نیستن رو جدا کنه تا حل مسئله راحتتر بشه.قبل از ادامه دادن باید توضیحی رو راجع به مدل نگه داری داده ها توی الستیک بگم، همونطوری که قبلا گفتم الستیک یک موتور جستجو با معماری no sql به این معنی که ساختار مشخصی مثل پایگاه های داده sql نداره، این یعنی که به ازای داده های ورودی نوع داده رو باید بتونه مدیریت کنه اما اتفاقی که میبفته اینه که الستیک داده های ورودیش رو به دو صورت میتونه  نگه میداره.داینامیک میپینگ ((dynamic mapping)) و یا اکسپلیسیت مپینگ ((explicit mapping))  داینامیک میپنیگ به این معنی که این امکان رو به الستیک میدیم تا خودش براساس هوش مصنوعی که داره شروع کنه به حدس زدن نوع داده فیلد ها و اونها رو ذخیره کنه اما در اکسپلیسیت مپنیگ این ماهستم که مشخص میکنم فیلد های ورودی رو با چه نوع داده ای میخوایم ذخیره کنیم.تا اینجاشاید به نظر نکته مهمی نباشه اما قضیه از جایی مهم میشه که زمانی که اولین ایونت در داخل یک ایندکس قرار میگیره الستیک نوع داده رو ثابت در نظر میگیره این به این معنی که درصورتی که ایونت بعدی که وارد این ایندکس میشه اگه نوع دادش فرق کنه الستیک به شما کد خطای ۴۰۰ بر میگردونه، به این مفهوم که نوع داده ای برای فیلد مشابهی از قبل وجود داره و این فیلد با نوع داده متفاوت نمیتونه توی همین ایندکس ذخیره بشه.حالا راه حل چیه: دوتا راه حل وجود داره اول اینکه اسم اون فیلد رو تغییر بدیم که عملا امکان پذیر نیست و یا اینکه اون ایونت رو در داخل همون ایندکس ذخیره نکنیم براش ایندکس جدید بسازیم و یا کلا اون رو در جای جداگونه این ذخیره کنیم تا بعدا براش راه حلی پیدا کنیم.DLQ چطوری کار میکنه؟ بصورت پیش فرض ایونت ها میان سمت لاگ استش و از طریق اون داخل الستیک ذخیره میشن، در صورتی که این ایونت دارای مشکلی باشه که خطای ۴۰۰ برگردونه مثل موردی که بالاتر اشاره کردم لاگ استش ترجیح میده که کلا از اون ایونت رد بشه و اون رو ذخیره نکنه، که این به معنی از دست دادن دیتاست.حالا برای جلوگیری از این اتفاق میشه لاگ استش رو طوری تنظیم کرد تا اون ها رو داخل DLQ بنویسه به جای این که کلا اونها رو حذف کنه.قبل از هرچیزی لازم که تکرار کنم تمام سناریوهای که توضیح میدم در ساختار استک هستن ( الستیک - لاگ استش و کیبانا) که معنی پیدا میکن. همونطوری که توضیح دادم ایونت های ورودی به سمت لاگ استش میان و لاگ استش اونها رو بصورت bulk یا دسته ای به سمت الستیک میفرسته.باید یاداوری کنم که &quot;لاگ استش تنها کارش انتقال دادست و هیچ تاثیری روی اصل داده نداره &quot; پس وقتی داره داده ها رو ارسال میکنه این الستیک که مشخص میکنه ایا این ایونت درسته یا نه؟در صورتی که ایونت ورودی خطا داشته باشه این الستیک که ایونت رو رد میکنه نه لاگ استش اما لاگ استش این  امکان رو داره تا از این اتفاق جلوگیری که به این صورت که؛ برای لاگ استش باید پایپ لاینی «pipeline» بنویسیم تا این مشکل رو برامون حل کنه.تنظیمات لاگ استش برای استفاده از DLQ DLQ بصورت پیش فرض بر روی لاگ استش غیر فعال، برای اینکه اون رو فعالش کنیم باید با کمک فایل لاگ استش «logstash.yml» در مسیر logstash &gt; config &gt; logstash.ymlو دستور زیر رو داخل اون قرار میدیمdead_letter_queue.enable: trueDLQ بصورت پیش فرض ایونت هایی که دچار خطا شده باشن رو در داخل مسیر خودش نگه داری میکنه اما اگه شرایط خاصی دارین که لازم این ایونت ها در جای دیگه ای ذخیره بشن میتونین با اضافه کردن دستور زیر مثل قبل مسیر ذخیره این ایونت ها رو هم مشحص کنین.path.dead_letter_queue: &quot;path/to/data/dead_letter_queue&quot;نکته دیگه ای که لازم بدونین این که ایونت هایی که دچار خطا شد در ابتدا بصورت موقت داخل یک فایل نگه داری میشن و بعدش با تغییر نام به اسم ایونت ذخیره میشن.اتفاقی که میفته اینطوری که به ازای هر فایل موقتی که ساخته میشه بازه زمانی طول میکشه تا این فایل پر بشه و فایل بعدی ایجاد بشه که شما میتونین برای این بازه زمانی رو با اضافه کردن دستور زیر مشخص کنین.dead_letter_queue.flush_interval: 5000تا این جای کار تنظیمات اصلی رو انجام دادیم، از اینجا به بعد شرایط بسته به نوع پیاده سازی شماست که مشخص میشه مثلا اگر شما استکتون رو با استفاده از داکر بالا اورده باشین پس لازم دارین تا مسیر ذخیره شدن DLQ رو بیرون از داکر هم تعیین کنین تا بتونین فایل های خطا رو از بیرون داکر هم تعین کنین تا بتونین فایل های خطا رو از بیرون داکر بینین و مشکلشون رو حل کنین.نکته ای که احتمالا براتون کاربردی تر، این که ما تا اینجا این ایونت های خطا رو نگه داشتیم این جا به بعد باهاشون چیکار کنیم؟برای این شرایط سه راه حل مشخص وجود داره،کلا بیخیال ایونت ها بشیم اون ها رو ویرایش کنیم و دوباره بفرستیم برای ایندکس شدندر نهایت اینکه اون ها رو از طریقی به فرستنده ایونت برگردونیم تااون ایونت هارو درست بکنه و دوباره بفرستهتکلیف دو راه حل اول که مشخص اما برای راه حل سوم چیکار باید انجام بدیم؟برای راه حل سوم، اگر با ساختار لاگ استش آشنا باشین میدونین که لاگ استش این امکان رو داره تا از وردی های مختلف اطلاعات رو دریافت و به خروجی های مختلف بفرسته، این توانایی این ایده رو بوجود میاره که برای هرورودی اگر لازم باشه تنظیمات خاصی انجام بشه، چیکار باید کرد و این همون چیزی که اینجا به کمکتون میاد.در داخل مسیر لاگ استش حتما متوجه شدین که دوتا پوشه وجود داره که یکی برای قرار گیری فایل تنظمیات لاگ استش، برای مثال تعریف مدل ورودی و دومی برای قرار گیری تنظیمات خود لاگ استش هستش، شما کافیه که داخل پوشه دوم یک فایل pipeline برای خودتون بسازین و تنظیمات زیر رو که ساده ترین مدل هستش داخلش قرار بدین.input {  dead_letter_queue {    path =&gt; &quot;/path/to/data/dead_letter_queue&quot;     commit_offsets =&gt; true     pipeline_id =&gt; &quot;main&quot;   }}output {  stdout {    codec =&gt; rubydebug { metadata =&gt; true }  }}اگر از نوشته های قبلی یادتون باشه این فایل تنظیمات تقریبا مشابه بقیه فایل های لاگ استش  که توی پوشه pipeline  هستن، هستش با این تفاوت که این بار این فایل رو داخل پوشه config میسازیم.از طریق این فایل تنظیمات شما میتونین تصمیم بگیرین که برای ایونت های از دست رفته چه کاری درست ترین به حساب میاد.درنهایت: اینکه من سعی کردم، تمام اون چیزی که شما برای حفظ داده هاتون در صورت بروز خطای ۴۰۰ در داخل الستیک لازم دارین رو توضیح بدم، اما جزیاتی وجود داره که سعی میکنم اونهارو بعدا تو نوشته ای جدا توضیح بدم، بصورت کلی شما میتونین با جستجو در داخل منابع اصلی اطلاعات تکمیلی رو بدست بیارین.پی نوشت: - مثل همیشه لازم میدونم که بگم، اگر جایی پیچیده بود و یا نیاز به اصلاح داشت کافیه برام بنویسین تا حتما اون رو اصلاح کنم.</description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Wed, 29 Mar 2023 02:40:10 +0330</pubDate>
            </item>
                    <item>
                <title>ردیس، کافکا و ربیت‌ام کیو</title>
                <link>https://virgool.io/@hadivarposhti/%D8%B1%D8%AF%DB%8C%D8%B3-%DA%A9%D8%A7%D9%81%DA%A9%D8%A7-%D9%88-%D8%B1%D8%A8%DB%8C%D8%AA-%D8%A7%D9%85-%DA%A9%DB%8C%D9%88-xxuyux7hadn5</link>
                <description>در ارتباطات ناهمزمان یا Async بین میکروسرویس ها از ابزاری که اصطلاحا به اون واسط پیام یا message broker میگن، استفاده میشه. این مسیج بروکر تضمین میکنه که ارتباط بین میکروسرویس های مختلف پایدار و قابل اعتماد باشه، اینطوری که پیام ها داخل این سیستم مدیریت و نظارت میشن اینطوری پیامی از بین نمیره.مسیج بروکرهای مختلفی وجود داره که در مقیاس و قابلیت های داده ها متفاوتند، توی این نوشته سعی کردم مقایسه ای داشته باشم بین مسیج بروکر معروف RabbitMQ، Kafka و Redis، امیدوارم که براتون مفید باشه.ارتباط بین میکروسرویس ها: همزمان و ناهمزماندو راه متداول برای ارتباط میکروسرویس ها با هم وجود داره: همزمان و ناهمزمان ((Synchronous and Asynchronous)). در یه ارتباط همزمان، تماس گیرنده قبل از ارسال پیام بعدی منتظر جواب می مونه و به عنوان یک پروتکل REST در بالای HTTP عمل می کنه. برعکس، در ارتباطات ناهمزمان، پیام ها بدون انتظار برای جواب ارسال می شن. این مدل برای سیستم های توزیع شده مناسبه و معمولاً به یه مسیج بروکر برای مدیریت پیام ها نیاز داره.در این شرایط نوع ارتباطی که برای ارتباط بین میکروسرویس هاتون انتخاب می‌کنین باید براش پارامترهای مختلفی را در نظر داشته باشین، برای مثال میشه به نحوه ساختار میکروسرویس‌هایی که دارین، زیرساخت‌های موجود، تأخیر، مقیاس، وابستگی‌ها و هدف ارتباط اشاره کرد. برقراری ارتباط ناهمزمان ممکن که پیچیده‌تر باشه و نیاز به اضافه کردن اجزای بیشتری به سیستم داشته باشیم، اما مزایای استفاده از ارتباطات ناهمزمان برای میکروسرویس‌ها بیشتر از معایب اونهاست.مزایای ارتباط ناهمزماناول و مهمتر از همه، اینکه ارتباط ناهمزمان بنا به تعریف غیرقابل مسدود شدن هستن. همچنین از مقیاس پذیری بهتری نسبت به ارتباط همزمان بهره میبرن. سوم اینکه، در صورت بروز مشکل برای میکروسرویس، مکانیسم های ارتباطی ناهمزمان تکنیک های بازیابی مختلفی را ارائه می دن و به طور کلی در مدیریت خطاهای مربوط به خرابی بهتر عمل میکنن. علاوه بر این، در زمان استفاده چون از مسیج بروکرها به جای پروتکل REST استفاده میکنن، در نتیجه خدمات دریافت کننده ارتباط واقعاً نیازی به شناخت فرستنده نداره، اینطوری که یه سرویس جدید حتی می‌توانه پس از گذشت زمان و خدمات دهی سرویس قدیمی، داخل سیستم جایگزین بشه.در نهایت، استفاده از  ارتباط ناهمزمان &quot;Asynchronous&quot;، توانایی ، نظارت، پایداری سیستم یا حتی اجرای سیاستهایی در آینده را افزایش می ده. این به شما توانایی هایی برای انعطاف پذیری، مقیاس پذیری و قابلیت های بیشتر در کدنویسی و  ساخت سیستم ارائه می کنه.انتخاب مسیج بروکر مناسبارتباطات ناهمزمان معمولاً از طریق یه مسیج بروکر مدیریت می شن. اما راه های دیگه ای هم وجود داره، مثل aysncio، که محدودیت های خودش رو داره.در انتخاب مسیج بروکر مناسب نکاتی وجود داره که اینجا توضیح میدم:۱- مقیاس پذیری مسیج بروکر -- تعداد مسیج هایی که در ثانیه به سیستم ارسال میشه.۲-پایداری داده ها -- قابلیت بازیابی مسیج ها.۳- ظرفیت مصرف کنندها --  آیا مسیج بروکر قادر به مدیریت یک به یک و/یا یک به چند بین خودش و مصرف کننده ها هست یا نه.یک به یک یک به چند خب تا اینجا مواردی که ممکن توی کار بهش بر بخورین و مفاهیم اولیه رو توضیح دادم حالا بین مسیج بروکر های فعلی بازار مقایسه ای میکنم تاببینیم کدوم یکی بهتر میتونه نیاز شما رو برطرف کنه.مقایسه انواع مسیج بروکر هاربیت ام کیو (AMQP):مقیاس پذیری: بر اساس پیکربندی و منابع، بصورت تخمینی در حدود 50 هزار پیام در ثانیه است.پایداری : پایداری تضمین میشه.اتصال یک به یک و/یا یک به چند: هردو پشتیبانی میشن.ربیت ام کیو در سال ۲۰۰۷ برای اولین بار منتشر میشه و جز اولین مسیج بروکرهاست.ربیت ام کیو یه ابزار منبع باز است که پیام ها را از دو روش نقطه به نقطه و pub-sub با پیاده سازی پروتکل های پیشرفته صف پیام یا (AMQP) ارسال می کنه.باید اشاره کنم که ربیت ام کیو طراحی شده تا از منطق مسیریابی پیچیده هم پشتیبانی کنه.RabbitMQ از تمامی زبان ها از جمله پایتون، جاوا، دات نت، پی اچ پی، روبی، جاوا اسکریپت، گو، سوئیفت و غیره پشتیبانی می کنه.و در اخر باید اضافه کنم که زمانی که در حالت پایدار هستین، باید منتظر برخی مشکلات عملکردی هم باشین.کافکا:مقیاس پذیری: امکان ارسال تا یک میلیون پیام را درثانیه داره.پایداری:  تضمین میکنه.اتصال یک به یک و/یا یک به چند: فقط از اتصال یک به چند پشتیبانی میکنه.کافکا توسط Linkedin در سال ۲۰۱۱ ایجاد شد تا پردازش با توان بالا و تاخیر کم را انجام بده. به عنوان یک پلتفرم پخش توزیع شده، کافکا یه سرویس انتشار-اشتراک (pub-sub) را تکرار می کنه. پایداری داده‌ها را فراهم می‌کنه و استریم هایی از رکوردها را ذخیره می‌کنه که اون رو قادر به حفظ کیفیت تبادل پیام‌ها می‌کنه.کافکا به عنوان SaaS روی Azure، AWS و Confluent پیاده سازی شده چرا که همه اونها به نوعی خالقان و مشارکت کنندگان اصلی پروژه کافکا هستن. کافکا از همه زبان‌های اصلی، از جمله Python، Java، C/C++، Clojure، .NET، PHP، Ruby، JavaScript، Go، Swift و غیره پشتیبانی می‌کنه.ردیس :مقیاس پذیری: امکان ارسال تا یک میلیون پیام را درثانیه داره.ماندگاری : اساساً، نه - چرا که ذخیره‌سازی اطلاعات در حافظه (مموری) اتفاق داره میفته.اتصال یک به یک و/یا یک به چند : هردو پشتیبانی میشن.ردیس متفاوت تر از بقیه مسیج بروکرهاست. ردیس در هسته خودش، بصورت یه دیتا استور که داد ها رو in-memory ذخیره میکنه و میشه ازش بعنوان یه دیتابیس key-value و یا مسیج بروکر ازش استفاده کرد، تعریف میشه. نکته مهمی که باید بهش اشاره کنم این که چون ردیس داده ها رو بصورت in-memory نگهداری میکنه قابلیت ماندگاری نداره اما برای پردازش های در لحظه عالیه.با اینکه ردیس از اتصال یک به چند اساسا پشتیبانی نمیکرده اما این قابلیت از نسخه ۵ به ردیس اضافه شده.مسیج بروکرها و موارد استفادهما برخی از ویژگی های ربیت ام کیو، کافکا و ردیس رو توضیح دادم. هر کدوم در قابلیت ها ویژگی های مهمی دارن، اما همانطور که توضیح دادم، کاملاً متفاوت عمل می کنن. توصیه من برای انتخاب مسیج بروکر مناسب، انتخاب اون با توجه به موارد استفاده اون ابزار و نیاز خودتون.پیام های کوتاه مدت: ردیسردیس یه دیتابیس که داده ها رو in-memory نگهداری میکنه، اصولا برای مواردی مناسب تر که پایداری (persistency) پیام خیلی مهم نیست.چراکه برای ردیس سرعت انتقال مهمه و قابلیت in-memory ردیس کاندیدای مناسبی برای مواردی که  در اون‌ها ماندگاری چندان مهم نیست و می‌شه مقداری ضرر رو هم تحمل کرد.مسیریابی پیچیده: ربیت ام کیوربیت ام کیو، یکی از قدیمی ترین مسیج بروکر ها و در عین حال ویژگی ها و قابلیت های زیادی داره که از اونها میشه به مسیریابی پیچیده اشاره کرد. حتی زمانی که نرخ تبادل مورد نیاز بالا نباشه یعنی (بیش از چند ده هزار msg/sec نباشه) بازم از مسیریابی پیچیده پشتیبانی می کنه.نیاز پروژه و استک نرم افزاری در نهایت اینکه، اصل مهم ، استک نرم افزار فعلی شما و نیاز پروژتون که مشخص کننده ابزار مورد نیازشماست. اگه به دنبال یک فرآیند یکپارچه سازی نسبتا آسون هستین و نمی خواین مسیج بروکرهای مختلف رو وارد بازی کنین، ممکن که تمایل بیشتری به کار با بروکری داشته باشین که قبلاً توسط استک نرم افزاری شما پشتیبانی میشه.به عنوان مثال، اگر از Celery برای Task Queue در سیستم خودتون استفاده می کنین، احتمالا کار با ربیت ام کیو یا ردیس گزینه بهتری باشه برخلاف کافکا که از Task Queue پشتیبانی نمیکنه و نیاز به بازنویسی قسمت های از کدتون رو دارین.جمع بندی:مهم است که به یاد داشته باشین که هر ابزار مزایا و معایب خاص خودشو رو داره و در مورد درک اونها و انتخاب گزینه مناسب برای کار ، باید موقعیت و الزامات خاص پروژتون رو همیشه در نظر بگیرین.پی نوشت: طبق معمول همه نوشته ها، سعی کردم که نوشتم با زبان محاوره و ساده گفته بشه که برای همه قابل درک باشه. اگه مورد و مشکلی وجود داره خوشحال میشم که برام بنویسین تا اون رو اصلاح کنم حتما.شاد باشین (:</description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Sat, 19 Nov 2022 00:32:45 +0330</pubDate>
            </item>
                    <item>
                <title>مدل سازی داده ها در Neo4j</title>
                <link>https://virgool.io/@hadivarposhti/%D9%85%D8%AF%D9%84-%D8%B3%D8%A7%D8%B2%DB%8C-%D8%AF%D8%A7%D8%AF%D9%87-%D9%87%D8%A7-%D8%AF%D8%B1-neo4j-rbg0bhaotrzt</link>
                <description>Neo4J چیست ؟پایگاه داده Neo4j میشه اینطوری تعریف کرد که،&quot;نوعی پایگاه داده است که از طریق عملیات کراد، Create,Read,Update و Delete داده ها را بصورت گراف ذخیره میکند.&quot;پایگاه داده گراف بیس چیه؟پایگاه داده گراف که بهش پایگاه داده گراف گرا هم گفته میشه، نوعی از پایگاه داده از جنس Nosql که از تئوری گراف برای ذخیره، مپ کردن و کوئری نوشتن ازش استفاده میشه.پایگاه داده گراف اساسا مجوعه از node ها و edge هاست.هر گراف ترکیبی از دو المان: node (یا vertex) و relationship (یا edge) ساخته میشه. هر نود بیانگر یک موجودیت (شخص، مکان، اشیا ...) و هر ایج بیانگر نحوه ارتباط بین دو نود.هدف اصلی توی این ساختار اجازه دادن به کاربر که هرمدل سناریوی رو بتونه مدل کنه.از یک سیستم جاده ای، شبکه ای از ابزارها، تاریخچه پزشکی بیماران و یا هرچیزی که بوسیله روابط (ایج) قابل ارائه باشه.چرا اصلا به یک پایگاه داده گراف بیس نیاز داریم؟برخلاف پایگاه داده های رابطه ای (RDBMS) پایگاه داده گراف بیس، با روابط بین جداول هم مثل یه شهروند درجه یک رفتار میکنه، به همین دلیل نیازی به استفاده از رویکردهای پیچیده تری مثل Join کوئری ها یا استفاده از کلید های خارجی برای پیدا کردن داده های مرتبط نیست. از اونجایی که به محض اینکه متوجه بشیم دوتا موجودیت باهم مرتبط هستن بهم وصلشون میکنیم پس نیاز به Join کوئری ضروری نیست اصلا، و از اونجایی که پایگاه داده های گراف گرا توی هسته خودشون از تفکر شی گرا استفاده می کنن، مدل داده ای که برای اونها ترسیم میکنین همون مدل داده ای که توی پایگاه داده های معمولی طراحی میشه.مدل کردن دادها بخش اساسی از هر پروژه ای  چرا که روش رسیدن به دادها و فراخوانی اونها رو معین میکنه و چون اغلب ایجاد تغییرات بزرگ ممکن امکان پذیر نباشد، مطمئناً آسون نیست، و این روند را مهم تر می کند.پروسه مدل کردن داده ها در دیتابیس های گراف بیس (و خصوصا Neo4j ) اغلب شامل یک تصمیم مهم و اون هم اینکه کی باید یک داده رو (data point) رو یک نود در نظر گرفت و کی باید یک ایج، و اغلب یک طراحی هوشمندانست که توی کوئری نوشتن های بعدی بسیار کمک میکنه.چطور یک دیتابیس رابطه ای (RDBMS) رو به یک دیتابیس گراف بیس (Neo4j) تبدیل کنیم؟جدول به نود ------ هر جدول موجودیت در مدل رابطه ای به یک برچسب روی نود ها در مدل گراف بیس تبدیل می شود. ردیف به نود ------  هر سطر در یک جدول موجودیت رابطه ای به یک نود درمدل گراف بیس تبدیل می شود.ستون به ویژگی نود ------  ستون ها (فیلدها) در جداول رابطه ای به ویژگی های نود در مدل گراف بیس تبدیل می شوند.کلیدهای اصلی بیزینسی ------ کلیدهای اصلی فنی را حذف میشن، کلیدهای اصلی بیزینسی کاربردین.شاخص ها/ محدودیت ها ------ محدودیت‌های منحصربه‌فردی را برای کلیدهای اصلی بیزینسی اضافه میشن، برای فیلدهایی که مکرر جستجو میشن ایندکس گذاری اضافه میکنیم .کلیدهای خارجی و رابطه ها ------ کلیدهای خارجی جداول را با ایج جایگزین ، سپس آونها رو حذف میکنیم.بدون پیش فرض ------ داده ها را با مقادیر پیش فرض حذ میشن، چرا که نیازی به ذخیره اونها نیست. پاک کردن داده ها ------ داده‌های تکراری در داده های دنرمالایز شده باید به نودهای جداگونه کشیده بشن تا یک مدل تمیزتر به دست بیاد.ایندکس گذاری ستون ها به آرایه ها ------ نام ستون های ایندکس شده (مثل email1، email2، email3) ممکن  که نشون دهنده ویژگی آرایه را باشه.جداول واسط (Join) و ایج ------ جدول واسط (join) به ایج تبدیل شده، ستون های روی آن جداول به ویژگی های اون ایج تبدیل می شه.طراحی مدل داده ها بصورت گرافپایگاه داده Neo4j پروسه مدل سازی دادها رو بصورت ساختن یک وایتبرد مطرح میکنه، این به این معنی که  نود ها و ایج هایی که می تونیم روی تخته سفید یا به عنوان جملات انگلیسی ایجاد کنیم، اغلب نحوه مدل سازی داده ها را مشخص می کنن.اغلب داده‌هایی که داریم را می‌شه به عنوان یک ویژگی یا یه نود به خودی خود نشون داد. به عنوان مثال در مورد داده های تراکنش های ایمیل، می توانیم داده ها رو به این صورت نشون بدیم.اگرچه که این مدل واضحی اما اگر بخوایم اون رو گسترش بدیم تا اطلاعات دیگه ای مثل CC، BCC ... این امکان برامون وجود نداره پس مدلمون رو به صورت زیر تغییرش میدیم.مثال دیگه ای که میشه زد میشه به ارتباط بین شهر و شرکت اشاره کرد.ما میتونیم مقدار شهر رو به نود شرکت بصورت مستقیم وصل کنیم. به تصویر زیر توجه کنین.برای کوئری زدن بر روی چنین داده هایی که نیاز به فیلتر کردن بر اساس یک مکان داره، باید تمام نودهای یک شرکت در نمودار رو پیمایش کنین و بعدش بر اساس مقدار شهر اونهارو فیلتر کنین.همونطور که توی تصویر بالا مشخص، از آنجایی که تعداد نود های شرکت به طور بالقوه می تونند بسیار زیاد باشن، کوئری برای همه شرکت ها در یک شهر خاص، از نظر زمان و مصرف CPU، منابع زیادی را می طلبه.برای اینکار میتونیم نمودار بالا رو بصورت زیر تغییرش بدیم.حالا میشه کوئری مون رو تغییر داد تا ابتدا شهر ها راو فیلتر کنه و فقط به دنبال شرکت هایی بگرده که به نود اون شهر متصل هستن. این زمان محاسبه کوئری ما را به طور قابل توجهی بهبود می ده.کلمه کلیدی EXPLAINکلمه کلیدی EXPLAIN برای کوئری زدن بر روی یک پروفایل استفاده میشه، استفاده از اونها بهتون نشون میده که یک کوئری چطور اجرا میشه، رکورد های تقریبی در هر سطح رو بدست میاره.برای مثال با کمک این کوئری میتونین شرکت هایی که در یک شهر خاص هستند رو محاسبه کنین یادتون باشه که بر اساس نحوه مدل سازی داده ها و نحوه کوئری شما نتیجه کوئری بسیار متفاوت میتونه باشه.در زیر، در سمت چپ، نشون می‌ده که چه اتفاقی می‌افته وقتی ابتدا یک شهر رو فیلتر می‌کنیم و بعد به دنبال شرکت‌هایی می‌گردیم که به اون نود متصل به اون شهر هستند. همونطور که می بینین، تعداد رکوردها در هر مرحله در مقایسه با توضیحات سمت راست، جزئی هستن، در مقایسه با کوئری که تمام شرکت ها رو جستجو می کنه و سعی داره که اونهارو را بر اساس مقدار شهر که به عنوان ویژگی ذخیره شده فیلتر کند.این ابزار یکی از قدرتمندترین هاست، چرا که در شروع مدل کردن داده ها بهتون نشون میده که خروجی شما در زمان مصرف چطور میشه، و یا حداقل قرار که چطور باشه.ایندکس و محدودیت ها مثل پایگاه داده های RDBM، پایگاه داده Neo4j از امکان ساخت ایندکس برای ویژگی های مختلف پشتیبانی می کنه، یک ایندکس پایگاه داده کپی از داده های پایگاه داده است که کمک میکنه تا جستجوی داده های مرتبط موثر باشه. اگرچه که این مورد باعث افزایش فضای ذخیره سازی و در بلند مدت باعث کندی در ذخیره سازی میشه، بنابراین اینکه چه ستون و یا ویژگی‌هایی ایندکس بشن تصمیمی که در زمان طراحی داده ها گرفته میشه و چون شرایط همیشه پایدار نیست اصلا توصیه نمیشه.استفاده عاقلانه از ایندکس می توانه سرعت جستجوها را چندین برابر بهبود بده و باید بخش مهمی از بحث مدل سازی داده ها باشه.از طرف دیگه محدودیت‌های اون بیزینس هستن که تکرار داده‌ها را برای ویژگی‌های کلیدی محدود می‌کنند. برای مثال، اگر داده‌هایی را از منابعی دریافت کنیم که ورودی‌های متعددی برای شهری به نام لندن دارن، نمی‌خوایم اونها رو به عنوان رکوردهای جداگانه ایجاد کنیم. بطور کلی محدودیت ها به ما کمک می کنن تا یکپارچگی داده ها را حفظ کنیم.مدیریت مقادیر تاریخدر اغلب موارد، داده هایی که سعی می کنین از اونها استفاده کنین دارای مقادیر تاریخ هستن. از Neo4j  ۳.۴ ، سیستم مدت و تاریخ ها را کنترل می کنن. Neo4j اجازه می ده تا ایندکس هایی رو بر روی ویژگی های عددی ایجاد کنین و جستجوهایی بصورت محدوده ای  که از ایندکس ها استفاده می کنن اجرا کنید. ما می‌تونیم از این مزیت برای تاریخ‌ها با ذخیره آن‌ها به‌عنوان کلیدهای زمانی  حتی میلی‌ثانیه‌ای بهره ببریم و به ما امکان می‌ده تا جستجوهامون رو بصورت محدوده ای بر روی تاریخ انجام بدیم.راه حل خلاقانه دیگر درخت زمان یا Time Tree . ایده این که نود های ماه و سال تاریخ سلسله مراتبی ایجاد کنیم که از طریق یک رابطه پدر، فرزند از بالا به پایین و از طریق رابطه بعدی به نودهای همسایه در همان سطح متصل بشه. همونطور که در زیر می بینین، با فیلتر کردن سال، سپس ماه، سپس روز و غیره به ساختار جستجو برای فیلتر کردن تاریخ کمک میشه. این به کاهش چشمگیر تعداد رکوردها در هر مرحله کمک می کنه و کوئری های بهبود میده.برای ایجاد نمودار درخت زمان، محصول Graphaware Timetree وجود داره که فایل‌های خروجی رو بصورت jar را ارائه می‌کنه به عنوان پلاگین در محیط Neo4j قابل دسترسی هستش.برای ایجاد نمودار درخت زمان با هدف کلی برای شروع سریع، میتونین از کد زیر استفاده کنین. محدوده سال ها را در کد بالا تغییر بدین و نمودار درخت زمان را همونطور که در بالا نشان داده شده است ایجاد کنین.MERGE (r:TimeTreeRoot)
WITH r, range(2010, 2020) AS years, range(1,12) as months
FOREACH(year IN years |
  MERGE (y:Year {value: year})
  MERGE (r)-[:CHILD]-&gt;(y)
  FOREACH(month IN months |
    CREATE (m:Month {value: month})
    MERGE (y)-[:CHILD]-&gt;(m)
    FOREACH(day IN (CASE
                      WHEN month IN [1,3,5,7,8,10,12] THEN range(1,31)
                      WHEN month = 2 THEN
                        CASE
                          WHEN year % 4 &lt;&gt; 0 THEN range(1,28)
                          WHEN year % 100 &lt;&gt; 0 THEN range(1,29)
                          WHEN year % 400 &lt;&gt; 0 THEN range(1,29)
                          ELSE range(1,28)
                        END
                      ELSE range(1,30)
                    END) |
      CREATE (d:Day {value: day})
      MERGE (m)-[:CHILD]-&gt;(d))))

WITH *

//Create years linked list
MATCH (root:TimeTreeRoot)--&gt;(year:Year)
WITH root, year ORDER BY year.value
WITH root, collect(year) as years
WITH root, years, years[0] AS first, years[size(years)-1] AS last
MERGE (root)-[:FIRST]-&gt;(first)
MERGE (root)-[:LAST]-&gt;(last)
FOREACH(i in RANGE(0, size(years)-2) |
    FOREACH(year1 in [years[i]] |
        FOREACH(year2 in [years[i+1]] |
            MERGE (year1)-[:NEXT]-&gt;(year2))))

WITH *

//Create months linked list
MATCH (year:Year)
WITH collect(year) as years
UNWIND years AS year
	MATCH (year)--(first:Month {value: 1}), (year)--(last:Month {value: 12})
	MERGE (year)-[:FIRST]-&gt;(first)
	MERGE (year)-[:LAST]-&gt;(last)

WITH *

MATCH (year:Year)-[:CHILD]-&gt;(month:Month)
WITH year, month
ORDER BY year.value, month.value
WITH collect(month) as months
FOREACH(i in RANGE(0, size(months)-2) |
    FOREACH(month1 in [months[i]] |
        FOREACH(month2 in [months[i+1]] |
            MERGE (month1)-[:NEXT]-&gt;(month2))))

WITH *

//Create days linked list
MATCH (month:Month)
WITH collect(month) as months
UNWIND months AS month
	MATCH (month)-[:CHILD]-&gt;(day:Day)
	WITH month, collect(day) AS days
	WITH month, days[0] AS first, days[size(days)-1] AS last
	MERGE (month)-[:FIRST]-&gt;(first)
	MERGE (month)-[:LAST]-&gt;(last)

WITH *

MATCH (year:Year)-[:CHILD]-&gt;(month:Month)-[:CHILD]-&gt;(day:Day)
WITH year,month,day
ORDER BY year.value, month.value, day.value
WITH collect(day) as days
FOREACH(i in RANGE(0, size(days)-2) |
    FOREACH(day1 in [days[i]] |
        FOREACH(day2 in [days[i+1]] |
            MERGE (day1)-[:NEXT]-&gt;(day2))))

;جمع بندی:توی این نوشته سعی کردم تا بصورت ساده با دیتابیس Neo4j  آشنا بشیم و چند مورد از ملاحظاتی که در زمان طراحی پایگاه داده گراف بیس باید بهش توجه بشه بگم.باید اضافه کنم که لزوما تمامی موارد بالا کاربردی نیستن این نیازشماست در هنگام طراحی که مشخص میکنه چه کاری باید و چه کاری نباید انجام بشه.پی نوشت: طبق معمول همه نوشته ها، سعی کردم که نوشتم با زبان محاوره و ساده گفته بشه که برای همه قابل درک باشه. اگه مورد و مشکلی وجود داره خوشحال میشم که برام بنویسین تا اون رو اصلاح کنم حتما. شاد باشین (: </description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Fri, 21 Oct 2022 21:29:37 +0330</pubDate>
            </item>
                    <item>
                <title>۸ کوئری کاربردی در MongoDB</title>
                <link>https://virgool.io/@hadivarposhti/%DB%B8-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-%DA%A9%D8%A7%D8%B1%D8%A8%D8%B1%D8%AF%DB%8C-%D8%AF%D8%B1-mongodb-i5r4elmvtidr</link>
                <description>شما احتمالا کلمه Nosql رو زیاد شنیدین، NoSQL یا همون پایگاه داده غیر رابطه ای که اگر بخوایم معنی دقیقترش رو بگیم میشه ( نه فقط SQL) به این مفهوم که پایگاه های داده غیر رابطه ای امکان پیشتیبانی از SQL رو دارند اما لزوما رابطه ای عمل نمیکنند.همیشه بنظرم نوشتن مسائل کاربردی بیشتر بدرد میخوره برای همین سعی کردم تا ازچیزهایی بگم که نیاز واقعی در پروژه های واقعی بودن، اینبار بعد از مدتها میخوام راجع به یکی دیگه از پایگاه های داده NoSQL بگم که میتونه همیشه یکی از گزینه های کاربردی باشه براتون.اینبار میخوام در رابطه با Mongodb و ۸ تا از کوئری های کاربردی اون براتون بنویسم.امیدوارم که براتون مفید باشه (: پایگاه های داده و نیازسنجی :پایگاه داده NoSql روش های مختلفی برای ذخیره سازی داده ها استفاده میکنن، که بسته به نیاز پروژه مشخص میشه چه نوعی کاربردی تر مثل، جفت key-value، گراف، داکیومنت، ... . پایگاه داده NOSql  مختلفی هم وجود دارند که یکی از معروف ترین اونها MongoDB هستش. پایگاه داده مونگو، پایگاه داده ای که اطلاعات رو بصورت داکیومنت در خودش ذخیره میکنه و هر داکیومنت در مونگو شامل جفت (field -- value) . داکیومنت ها رو درون ساختاری به نام کالکشن قرار میگیرن. در مقام مقایسه داکیومنت ها به عنوان row و کالکشن ها به عنوان همه table در دیتابیس های ریلیشنال هستند.کوئری های کاربردی :خب در مرحله اول بیاین تصور کنیم که کالکشنی به اسم &quot;customer&quot; داریم که شامل فیلدهایی مثل اسم، سن ، جنسیت و مقدار آخرین خرید.{
 &amp;quot_id&amp;quot : ObjectId(&amp;quot600c1806289947de938c68ea&amp;quot),
 &amp;quotname&amp;quot : &amp;quotJohn&amp;quot,
 &amp;quotage&amp;quot : 32,
 &amp;quotgender&amp;quot : &amp;quotmale&amp;quot,
 &amp;quotamount&amp;quot : 32
}

پینوشت: درمورد مفاهیم کلی پیش فرض رو بر این گذاشتم که با اونها اشنایی اولیه رو دارین اما درصورتی که جایی پیچیده بود توی کامنت ها برام بنویسین تا براتون توضیح بدم.مثال ۱:پیدا کردن داکیومنت مرتبط با یک کاربر خاص؛ فرض کنین میخوایم داکیومنت مربوط به یک کاربر رو پیدا کنیم برای اینکار باید فیلد name  در کوئری find استفاده کنیمباید اضافه کنم که کلمه pretty و یا درسترش متد pretty باعث میشه تا جواب کوئری شما بصورت زیر باشه که باعث خوانایی بهتر جواب میشه.&gt; db.customer.find( {name: &amp;quotJohn&amp;quot} ).pretty()

{
 &amp;quot_id&amp;quot : ObjectId(&amp;quot600c1806289947de938c68ea&amp;quot),
 &amp;quotname&amp;quot : &amp;quotJohn&amp;quot,
 &amp;quotage&amp;quot : 32,
 &amp;quotgender&amp;quot : &amp;quotmale&amp;quot,
 &amp;quotamount&amp;quot : 32
}مثال ۲ :پیدا کردن داکیومنت کاربران بزرگتر از ۴۰ سال ؛ بیاین فرض کنیم که دنبال کاربری هستم که سنش بزرگتر از ۴۰ سال، برای اینکار از آرگومان &quot;gt$&quot; به معنی  ( بزرگتر از ) استفاده میکنیم.&gt; db.customer.find( {age: {$gt:40}} ).pretty()
{
 &amp;quot_id&amp;quot : ObjectId(&amp;quot600c19d2289947de938c68ee&amp;quot),
 &amp;quotname&amp;quot : &amp;quotJenny&amp;quot,
 &amp;quotage&amp;quot : 42,
 &amp;quotgender&amp;quot : &amp;quotfemale&amp;quot,
 &amp;quotamount&amp;quot : 36
}مثال ۳:و برای پیدا کردن داکیومنت کاربران کوچکتر از ۲۵ سال و جنسیت اونها (زن ) باشه ؛ بیاین فرض کنیم که دنبال کاربری هستم که سنش کوچکتر از ۲۵ سال و جنسیتش زن ، برای اینکار باید از دستور &quot;and&quot; استفاده کنیم تا بتونیم هردو شرط رو لحاظ کنیم. همچنین یادتون باشه که  آرگومان &quot;lt$&quot; به معنی  ( کوچکتر از ) که بما کاربران کمتر از ۲۵ سال رو بر میگردونه.&gt; db.customer.find( {gender: &amp;quotfemale&amp;quot, age: {$lt:25}} ).pretty()
{
 &amp;quot_id&amp;quot : ObjectId(&amp;quot600c19d2289947de938c68f0&amp;quot),
 &amp;quotname&amp;quot : &amp;quotSamantha&amp;quot,
 &amp;quotage&amp;quot : 21,
 &amp;quotgender&amp;quot : &amp;quotfemale&amp;quot,
 &amp;quotamount&amp;quot : 41
}

{
 &amp;quot_id&amp;quot : ObjectId(&amp;quot600c19d2289947de938c68f1&amp;quot),
 &amp;quotname&amp;quot : &amp;quotLaura&amp;quot,
 &amp;quotage&amp;quot : 24,
 &amp;quotgender&amp;quot : &amp;quotfemale&amp;quot,
 &amp;quotamount&amp;quot : 51
}مثال ۴:توی این مثال همون مثال قبل رو میخوام تکرار کنم با این تفاوت که اینبار از آرگومان &quot;and&quot; هم میخوام داخل کوئریم استفاده کنم.&gt; db.customer.find( {$and :[ {gender: &amp;quotfemale&amp;quot, age: {$lt:25}} ]} ).pretty()احتمالا میدونین که از آرگومان &quot;and&quot; برای ترکیب شروط استفاده میشه به این صورت که هر دوشرط باید حتما برقرار باشن. مابقی شرطمون هم مثل مثال قبل با این تفاوت که باید شروط مون رو حتما به صورت ارایه &quot; [ ] &quot; تعریف کنیم.مثال ۵: اینبار میخوام کوئری بنویسم که تمامی مشتریانی که جنسیتشون اقا یا سنشون کمتر از ۲۵ سال بمن برگردونه.توی این مثال میخوام که بهتون نمونه ای کوئری ترکیبی رو نشون بدم که با کمک آرگومان &quot;OR&quot; انجام میشه، توی این مثال کافیه که مثل قبل به جای &quot;and&quot;  از &quot;or&quot; استفاده کنیم.&gt; db.customer.find( { $or: [ { gender: &amp;quotmale&amp;quot }, {age: { $lt: 22}} ] })

{ &amp;quot_id&amp;quot : ObjectId(&amp;quot600c1806289947de938c68ea&amp;quot), &amp;quotname&amp;quot : &amp;quotJohn&amp;quot, &amp;quotage&amp;quot : 32, &amp;quotgender&amp;quot : &amp;quotmale&amp;quot, &amp;quotamount&amp;quot : 32 }
{ &amp;quot_id&amp;quot : ObjectId(&amp;quot600c19d2289947de938c68ed&amp;quot), &amp;quotname&amp;quot : &amp;quotMartin&amp;quot, &amp;quotage&amp;quot : 28, &amp;quotgender&amp;quot : &amp;quotmale&amp;quot, &amp;quotamount&amp;quot : 49 }
{ &amp;quot_id&amp;quot : ObjectId(&amp;quot600c19d2289947de938c68ef&amp;quot), &amp;quotname&amp;quot : &amp;quotMike&amp;quot, &amp;quotage&amp;quot : 29, &amp;quotgender&amp;quot : &amp;quotmale&amp;quot, &amp;quotamount&amp;quot : 22 }
{ &amp;quot_id&amp;quot : ObjectId(&amp;quot600c19d2289947de938c68f0&amp;quot), &amp;quotname&amp;quot : &amp;quotSamantha&amp;quot, &amp;quotage&amp;quot : 21, &amp;quotgender&amp;quot : &amp;quotfemale&amp;quot, &amp;quotamount&amp;quot : 41 }مثال ۶ :يکی دیگه از نکات مهمی که باید بهش اشاره کنم، امکان تجمیع مقادیر یا &quot;aggregation&quot; در mongodb هستش.برای مثال میخوام مجموع مقدار خرید تمام مشتریان زن و مرد محاسبه کنم کافیه تا از متد aggregate  استفاده کنم و کوئری بشکل زیر بنویسمش. &gt; db.customer.aggregate([
... { $group: {_id: &amp;quot$gender&amp;quot, total: {$sum: &amp;quot$amount&amp;quot} } }
... ])
{ &amp;quot_id&amp;quot : &amp;quotfemale&amp;quot, &amp;quottotal&amp;quot : 198 }
{ &amp;quot_id&amp;quot : &amp;quotmale&amp;quot, &amp;quottotal&amp;quot : 103 }خب بزارین تا کوئری بالا رو یکم بیشتر توضیح بدم. در مرحله اول، اومدم با استفاده از ستون &quot;gender$&quot;گروپ کردم به عنوان id، و قسمت دوم هم تابع مدنظرمون و ستونی که باید تجمع بشه که &quot;sum$&quot; در مثال ماست.مثال ۷:حالا بیاین یه قدم جلوتر بریم و به مثال قبل شرط هم اضافه کنیم.برای اینکار اول شرط مون رو با استفاده از دستور &quot;match&quot; مینویسیم وبعد aggregation رو روی اون اجرا میکنیم.تو مثال زیر اول میایم تمام مشتریای بزرگتر از ۲۵ سال رو انتخاب میکنیم و بعد مجموع مقدار خریدشون رو محاسبه میکنیم.&gt; db.customer.aggregate([
... { $match: { age: {$gt:25} } },
... { $group: { _id: &amp;quot$gender&amp;quot, avg: {$avg: &amp;quot$amount&amp;quot} } }
... ])
{ &amp;quot_id&amp;quot : &amp;quotfemale&amp;quot, &amp;quotavg&amp;quot : 35.33 }
{ &amp;quot_id&amp;quot : &amp;quotmale&amp;quot, &amp;quotavg&amp;quot : 34.33 }مثال ۸ :مرتب سازی ( sort )، در مثال قبل جواب کوئری ما دو رکورد بود برای همین اجباری به مرتب سازی جواب ها نبود اما ممکن شما به کوئری بر خورد کنین که چندین رکورد جواب داشته باشه در این صورت لازم که اونها رو مرتب کنین که برای اینکار بصورت زیر عمل میکنیم. نکته ای که باید اضافه کنیم اینکه مقدار ۱ که میبینین برابر با مرتب سازی بصورت asc و مقدار ۱- برابر با مرتب سازی به صورت desc.&gt; db.customer.aggregate([
... { $match: { age: {$gt:25} } },
... { $group: { _id: &amp;quot$gender&amp;quot, avg: {$avg: &amp;quot$amount&amp;quot} } },
... { $sort: {avg: 1} }
... ])
{ &amp;quot_id&amp;quot : &amp;quotmale&amp;quot, &amp;quotavg&amp;quot : 34.33 }
{ &amp;quot_id&amp;quot : &amp;quotfemale&amp;quot, &amp;quotavg&amp;quot : 35.33 }جمع بندی: در دنیای اطلاعات امروز مهم که بدونین چطوری با اطلاعاتی که میاد ستتون رفتار کنین تا بهترین جواب و بگیریم. برای این کار هم باید پایگاه داده ها رو خوب بشناسین، مهم نیست که sql  و یا nosql  باشه .من سعی کردم تا مفاهیم اولیه رو در پایگاه داده mongo رو بهتون معرفی کنم اما مسلما برای پیشرفت نیاز دارین تا خودتون رو درگیر مسائل پیچیده تر هم بکنین. مثل همیشه خوشحال میشم که فیدبک هاتون رو برام بنویسین، تا نواقص کارم رو برطرف کنم.پی نوشت: در این نوشته سعی کردم تا مفاهیم رو به زبان شاده توضیح بدم اما لازمه که اضافه کنم برای درک بهتر نیاز داره که دانش اولیه از پایگاه داده داشته باشین و در نهایت هم مثل همیشه منابع اصلی رو هم بخونین.شاد باشین (:</description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Thu, 07 Jul 2022 00:23:42 +0430</pubDate>
            </item>
                    <item>
                <title>شبیه سازی Join کوئری در الستیک سرچ</title>
                <link>https://virgool.io/@hadivarposhti/%D8%B4%D8%A8%DB%8C%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-join-%DA%A9%D9%88%D8%A6%D8%B1%DB%8C-%D8%AF%D8%B1-%D8%A7%D9%84%D8%B3%D8%AA%DB%8C%DA%A9-%D8%B3%D8%B1%DA%86-lgb511muwqt8</link>
                <description>با تمام مزایایی که دیتابیس های nosql دارن در مواردی پیش میاد که شما نیاز پیدا میکنین مفهوم دیگه ای رو که در دیتابیس RDBMS به راحتی انجام میشه در دیتابیس nosql خودتون شبیه سازی کنین.یکی از همین موارد Join کوئری بین دوتا از table هاست.به خودی خود Join کوئری بین دو یا چندتا از table ها هزینه بردار هست فرقی هم نمیکنه که شما در چه مدل دیتابیسی دارین اینکار رو میکنین اما چون نیاز وجود داره باید دید که در صورتی که این هزینه می صرفه اینکارو انجام داد در غیر اینصورت باید دنبال راه حل های دیگه بود.قبل از توضیح بیشتر اول به این مثال دقت کنین:  تصور کنین در یک فروشگاه اینترنتی ۶ table داریم که بصورت بالا باهم ارتباط دارن و درصورتی که لازم بشه شما باید بین حداقل شش table کوئری Join بزنین که باعث میشه &quot;زمان زیادی&quot; برای محاسبه مصرف بشه. این مورد مزیت رقابتی کسب و کار شما رو محیط اینترنت با مشکل مواجه میکنه.اما در هر حال همیشه شرایطی وجود داره که نمیشه نادیده گرفت و مجبورین این هزینه رو قبول کنین. الستیک سرچ بصورت پیش فرض یک پایگاه داده nosql به حساب میاد و در این شرایط به شما دو راه برای زدن کوئری Join  به شما ارائه میده استفاده از تکنیک &quot;parent-child&quot; و یا &quot;nested query&quot;.قبل از اینکه ادامه بدم باید بگم که اصلی ترین مزیت الستیک نسبت به بقیه پایگاه های داده nosql مزیت سرعت که Join کوئری به روش سنتی باعث کندی شدید در الستیک سرچ میشه برای اینکار باید حتما دو یا چند table که میخواین بین اونها Join کوئری بزنین حتما در یک Shard باشن.در این مطلب سعی میکنم تا Join کوئری بصورت مختصر و بهمراه مثال توضیح بدم، پس در ادامه با من همراه باشین.تکنیک Parent - Child در الستیک سرچ: با یک مثال شروع میکنم، تصویر زیر یک درخت خانوادگی رو از سریال گات داریم که شامل سه خانواده ، سه پدر و مادر و نه فرزند و هر عضو دارای دوتا فیلد &quot;gender&quot; و &quot;isAlive &quot;هستش.درخت خانواده با ارتباط فرزند و والدبا استفاده از این مثال میخوایم که سه تا سناریو رو دنبال کنیم ؛ارتباط والد ، فرزندارتباط والد و چند فرزندیارتباط والد و فرزندی چند لایهساخت ایندکس &quot;Family-Tree&quot;در قدم اول با استفاده از دستور زیر mapping لازم رو برای ایجاد ایندکس ایجاد میکنم، با این کار دو table مدنظر parent و child در زمان ذخیره در الستیک سرچ بصورت وابسته با هم در یک node و یک shard ذخیره میشند.curl -X PUT &#039;http://localhost:9200/family_tree&#039; -H &#039;content-type: application/json&#039; \
  -d &#039;{
    &amp;quotsettings&amp;quot: {
        &amp;quotindex&amp;quot: {
            &amp;quotnumber_of_shards&amp;quot: 2,
            &amp;quotnumber_of_replicas&amp;quot: 2
        }
    },
    &amp;quotmappings&amp;quot: {
        &amp;quotproperties&amp;quot: {
            &amp;quotfirstName&amp;quot: {
                &amp;quottype&amp;quot: &amp;quottext&amp;quot
            },
            &amp;quotlastName&amp;quot: {
                &amp;quottype&amp;quot: &amp;quottext&amp;quot
            }, 
            &amp;quotgender&amp;quot: {
                &amp;quottype&amp;quot: &amp;quottext&amp;quot
            },
            &amp;quotisAlive&amp;quot: {
                &amp;quottype&amp;quot: &amp;quotboolean&amp;quot
            },
            &amp;quotrelation_type&amp;quot: {
                &amp;quottype&amp;quot: &amp;quotjoin&amp;quot,
                &amp;quoteager_global_ordinals&amp;quot: true,
                &amp;quotrelations&amp;quot: {
                    &amp;quotparent&amp;quot: &amp;quotchild&amp;quot
                }
            }
        }
    }
}&#039;فیلد  relation-type اسم نوع کوئری ماست که اون رو برای مشخص شدن توسط الستیک اضافه میکنیم.عبارت &quot; type: join &quot; یک عبارت کلیدی برای مشخص کردن نوع کوئری توسط الستیک سرچ.عبارت &quot; parent: child &quot;  یک عبارت کلیدی برای انجین الستیک به حساب میاد که متوجه بشه بین این دو داکیومنت سرعت ذخیره سازی مهمتر از سرعت در جستجو.فیلد relation ارتباط بین داکیومنت هاست، هر داکیومنت باید دارای اسامی parent و یا child باشه تا از این mapping پیروی بکنه.ذخیره سازی داده های تیبل Parent curl -X PUT &#039;http://localhost:9200/family_tree/_doc/1?routing=Darren&#039; \
  -H &#039;content-type: application/json&#039; \
  -d &#039;{
    &amp;quotfirstName&amp;quot:&amp;quotDarren&amp;quot,
    &amp;quotlastName&amp;quot:&amp;quotFord&amp;quot,
    &amp;quotgender&amp;quot:&amp;quotMale&amp;quot,
    &amp;quotisAlive&amp;quot:false,
    &amp;quotrelation_type&amp;quot:{
        &amp;quotname&amp;quot:&amp;quotparent&amp;quot
    }
}&#039;دستور بالا داکیومنتی در الستیک سرچ میسازه که اصطلاحا parent به حساب میاد. پارامتر مهمی که باید اون رو در نظر داشته باشین پارامتر routing . هر والد اسم خودش رو به این پارامتر اختصاص میده. اینکار باعث میشه تا ما بتونیم کنترل کنیم که داکیومنت مد نظرمون توی کدوم ایندکس قرار ذخیره بشه .ذخیره سازی داده های تیبل Childcurl -X PUT &#039;http://localhost:9200/family_tree/_doc/5?routing=Darren&#039; \
  -H &#039;content-type: application/json&#039; \
  -d &#039;{
    &amp;quotfirstName&amp;quot:&amp;quotPearl&amp;quot,
    &amp;quotlastName&amp;quot:&amp;quotFord&amp;quot,
    &amp;quotgender&amp;quot:&amp;quotFemale&amp;quot,
    &amp;quotisAlive&amp;quot:true,
    &amp;quotrelation_type&amp;quot:{
        &amp;quotname&amp;quot:&amp;quotchild&amp;quot,
        &amp;quotparent&amp;quot:&amp;quot1&amp;quot
    }
}&#039;همونطور که میبینین دستور بالا برای ایجاد داکیومنت child هستش اما نکته ای که مهمه پارامتر routing. اگر این تکه کد رو با تکه کد قبلی مقایسه کنین متوجه میشین که مقدار پارامتر routing در هر دو داکیومنت یکسان این به این معنی که (همونطور که بالاتر هم اشاره کردم) هردو داکیومنت parent و child باید در یک shard قرار بگیرین، که به این طریق اونهارو در کنار هم قرار میدیم.کوئری Join بین این داکیومنت و parent داکیومنت از طریق فیلد relation-type و فیلد parent: 1 که داخلش هست اتفاق می افته.کوئری زدن بر روی داده هاحالا قسمت جذاب قضیه شروع میشه، از حالا به بعد میتونین روی ایندکسی که ساختین کوئری دلخواهتون رو بزنین.بزارین با یه مثال شروع کنیم؛ در درخت خانواده بالا sienna evans رو میخوایم که همه فرزندانش رو بدست بیاریم، برای اینکار  از طریق دستور زیر curl -X GET &#039;http://localhost:9200/family_tree/_search?pretty=true&#039; \
  -H &#039;content-type: application/json&#039; \
  -d &#039;{
   &amp;quotquery&amp;quot:{
      &amp;quotparent_id&amp;quot:{
         &amp;quottype&amp;quot:&amp;quotchild&amp;quot,
         &amp;quotid&amp;quot:&amp;quot2&amp;quot
      }
   }
}&#039;اقدام میکنیم، که نتیجه زیر رو برای ما مشخص میکنه.{
  &amp;quottook&amp;quot : 2,
  ...
    &amp;quothits&amp;quot : [
      {
        &amp;quot_index&amp;quot : &amp;quotfamily_tree&amp;quot,
        &amp;quot_type&amp;quot : &amp;quot_doc&amp;quot,
        &amp;quot_id&amp;quot : &amp;quot9&amp;quot, 
        &amp;quot_routing&amp;quot : &amp;quotSienna&amp;quot,
        &amp;quot_source&amp;quot : {
          &amp;quotname&amp;quot : &amp;quotRalph&amp;quot,
          &amp;quothouse&amp;quot : &amp;quotEvans&amp;quot,
          &amp;quotgender&amp;quot : &amp;quotMale&amp;quot,
          &amp;quotisAlive&amp;quot : true,
          &amp;quotrelation_type&amp;quot : {
            &amp;quotname&amp;quot : &amp;quotchild&amp;quot,
            &amp;quotparent&amp;quot : &amp;quot2&amp;quot
          }
        }
      }
     ] 
  ...
}ارتباط والد و چند فرزندیتوی این مدل در ابتدا به Darren Ford میخوایم Melissa Ford رو به عنوان همسر اضافه میکنیم که درخت خانواده ما به این شکل تغییر میکنه.که اینکار باعث میشه تا ایندکس ما به اینصورت تغییر بکنه.curl -X PUT &#039;http://localhost:9200/family_tree/_mapping&#039; 
  -H &#039;content-type: application/json&#039; \
  -d &#039;{
   &amp;quotproperties&amp;quot:{ 
      &amp;quotrelation_type&amp;quot:{
         &amp;quottype&amp;quot:&amp;quotjoin&amp;quot,
         &amp;quoteager_global_ordinals&amp;quot:true,
         &amp;quotrelations&amp;quot:{
            &amp;quotparent&amp;quot:[ &amp;quotchild&amp;quot, &amp;quotwife&amp;quot ]
         }
      }
   }
}&#039;لازمه که بگم الان یک ارایه از والد ها داریم که بین همسر و فرزند مرتبطه.در این مرحله اضافه شدن همسر شرایط مثل زمانی که میخواستم تیبل child رو اضافه کنم، به همون صورت و به همون شکل و فقط از کلمه wife به عنوان relation-type استفاده میکنم.کوئری زدن بر روی داده ها از طریق فیلد Wifeدر این حالت اگر بخوام والدی رو پیدا کنم که همسر داره کوئری با استفاده از عبارت has_child و استفاده از type: wife زده میشه.curl -X GET &#039;http://localhost:9200/family_tree/_search?pretty=true&#039; \
  -H &#039;content-type: application/json&#039; \
  -d &#039;{
   &amp;quotquery&amp;quot:{
      &amp;quothas_child&amp;quot:{
         &amp;quottype&amp;quot:&amp;quotwife&amp;quot,
         &amp;quotquery&amp;quot:{
            &amp;quotmatch_all&amp;quot: {}
         }
      }
   }
}&#039;با اجرای کوئری بالا مقدار Darren Ford بر میگرده.ارتباط والد و فرزندی چند لایه (نوه ها)توی این مرحله میخوام که درخت خانواده رو به شکل زیر گسترش بدم.در حالت نیاز دارم که ایندکس رو دوباره از اول بسازم، این به دلیل پیش میاد که ما برای هم فرزند قبلا یک والد داشته باشم و از اونجایی که در زمان ساخت ایندکس داکیومنت فرزند خودش والد نبوده پس مشکل از دست دادن داده دارم، برای همین باید ایندکس قبلی رو حذف کنم و ایندکس جدید رو با ساختاری متفاوت بصورت زیر بسازم.curl -X PUT &#039;http://localhost:9200/family_tree&#039; \
  -H &#039;content-type: application/json&#039; \
  -d &#039;{
   &amp;quotsettings&amp;quot:{
         ...
   },
   &amp;quotmappings&amp;quot:{
      &amp;quotproperties&amp;quot:{
         ...
         
         &amp;quotrelation_type&amp;quot:{
            &amp;quottype&amp;quot:&amp;quotjoin&amp;quot,
            &amp;quoteager_global_ordinals&amp;quot:true,
            &amp;quotrelations&amp;quot:{
               &amp;quotparent&amp;quot:[ &amp;quotchild&amp;quot, &amp;quotwife&amp;quot ],
               &amp;quotchild&amp;quot:&amp;quotgrandchild&amp;quot
            }
         }
      }
   }
}&#039;عبارت child در این ایندکس بعنوان parent استفاده میشه و در نتیجه صفات parent رو هم داراست، البته از نوع grandchild . این به ما کمک میکنه تا توالی ارتباط parent &gt; child &gt; grandchildرو حفظ کنم .مثل قبل شرایط اضافه کردن فیلد grandchild مثل اضافه کردن child میمونه باهمون شکل و فرمت.curl -X PUT &#039;http://localhost:9200/family_tree/_doc/14?routing=Darren&#039; \
  -H &#039;content-type: application/json&#039; \
  -d &#039;{
   &amp;quotfirstName&amp;quot:&amp;quotDouglas&amp;quot,
   &amp;quotlastName&amp;quot:&amp;quotFord&amp;quot,
   &amp;quotgender&amp;quot:&amp;quotMale&amp;quot,
   &amp;quotisAlive&amp;quot:true,
   &amp;quotrelation_type&amp;quot:{
      &amp;quotname&amp;quot:&amp;quotgrandchild&amp;quot,
      &amp;quotparent&amp;quot:&amp;quot5&amp;quot
   }
}&#039;در مثال بالا Douglas Ford بعنوان فرزند Pearl Ford و نوه Darren Ford، توجه داشته باشین که از همون پارامتر روتینگ Darren استفاده میکنم که برای ساختن داکیومنت والد استفاده کردیم.اینکار باعث میشه تا مطمئن بشم که ارتباط بین والد، فرزند و نوه همیشه برقرار هستش.کوئری زدن بر روی دادهای چند سطحی‌ (نوه)اگه بخوام لیست همه والد هایی رو داشته باشم که نوه دختری دارن با استفاده از دستور زیر curl -X POST &#039;http://localhost:9200/family_tree/_search&#039; \
  -H &#039;content-type: application/json&#039; \
  -d &#039;{
   &amp;quotquery&amp;quot:{
      &amp;quothas_child&amp;quot:{
         &amp;quottype&amp;quot:&amp;quotchild&amp;quot,
         &amp;quotquery&amp;quot:{
            &amp;quothas_child&amp;quot:{
               &amp;quottype&amp;quot:&amp;quotgrandchild&amp;quot,
               &amp;quotquery&amp;quot:{
                  &amp;quotmatch&amp;quot:{
                     &amp;quotgender&amp;quot:&amp;quotFemale&amp;quot
                  }
               }
            }
         }
      }
   }
}&#039;بعد از اجرای کوئری بالا Ryan Turner به عنوان تنها والدی که نوه دختری داره جواب ما میشه.لازم میدونم که باز تاکید کنم اینکه الستیک سرچ اصلا پایگاه داده منسابی برای Join کوئری نیست و درصورتی که هزینه های این کوئری براتون قابل پذیرش باشه این کار رو انجام بدین. نتیجه گیری :کوئری Join و تکنیک Parent-Child برای مدیریت ایندکس ها زمانی که پرفورمنس از زمان جستجو مهمتر باشه استفاده میشه، اما هزینه های خودش رو هم داره. برای مثال باید آگاه باشین که این مدل ذخیره سازی محدودیت های فیزیکی و پیچیدگی های خاص خودش رو داره. مورد دیگه اینکه کوئری های چند لایه این پیچیدگی ها رو بیشتر هم میکنه.درنهایت اینکه باید قبل از هرکاری نیازسنجی دقیقی داشته باشین نسبت نیازتون و سعی کنین تا طوری سیستم رو طراحی کنین تا کمترین نیاز رو برای رفتن به این سمت داشته باشین.پی نوشت: مثل همیشه خوشحال میشم که نوشته من رو بخونین و در صورتی که نقص ها، کمبودها و درصورتی که خطایی رو میبینن برام بنویسین تا اصلاحشون کنم. </description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Fri, 03 Jun 2022 21:53:31 +0430</pubDate>
            </item>
                    <item>
                <title>مپینیگ در الستیک سرچ</title>
                <link>https://virgool.io/@hadivarposhti/%D9%85%D9%BE%DB%8C%D9%86%DB%8C%DA%AF-%D8%AF%D8%B1-%D8%A7%D9%84%D8%B3%D8%AA%DB%8C%DA%A9-%D8%B3%D8%B1%DA%86-qob7bouzpeoq</link>
                <description>همونطور که قبلا توضیح دادم الستیک سرچ یک موتور جستجو که از معماری nosql برای مدیریت ذخیره سازی داده های داخلش استفاده میکنه. در حالت کلی الستیک از مفهومی به نام مپینیگ (Mapping) کمک میگیره تا بتونه داده هایی رو که ذخیره میکنه نوع داده اونها رو تشخیص بده.در تعریفی که از مفهوم کلمه مپینیگ در وبسایت الستیک اومده این کلمه اینطوری تعریف میشه: Mapping is the process of defining how a document, and the fields it contains, are stored and indexed.  مپینیگ فرایندیست برای پردازش نحوه ایجاد داکیومنت ها، فیلد ها و ذخیره سازی آنهر داکیومنت درون الستیک شامل فیلدهایست که هرکدوم نوع داده خودشون رو دارا هستند.وقتی که داده هاتون رو ذخیره میکنین درواقع دارین از مفهوم مپینگ استفاده می کنین که شامل لیست فیلدهایی که  مرتبط هستن با یک داکیومنت. درکنار مفهوم مپینگ باید با مفهومی به نام متادیتا مانند _source هم آشنا بشین که نحوه مدیریت اون داکیومنت رو سفارشی سازی میکنه.بصورت کلی مپینیگ به دو مدل تقسیم میشه: dynamic mappingexplicit mappingداینامیک مپینیگ، به شما امکان جستجوی همزمان بر روی داده هاتون در هنگام ساخته شدن ایندکس ها رو میده. در داینامیک مپینیگ نوع داده هر فیلد توسط خود الستیک سرچ مشخص میشه. در الستیک سرچ امکان ایجاد داده های تو درتو وجود داره، اینرو هم باید اضافه کنم فیلد در هنگام ذخیره سازی در بالای صف قرار میگیرند.نکته ای که باید به این قسمت اضافه کنم اینه که در داینامیک میپینگ شما میتونین با کمک condtions در الستیک سرچ فیلد های مورد نظرتون رو بصورت داینامیک به ایندکس اضافه کنین.اکسپلیسیت مپینیگ، به شما این امکان رو میده تا خودتون نوع مپینگ، اینکه چطور میخواین دادهاتون ذخیره بشن رو بصورت دقیق مشخص کنین مثل اینکه:با کدوم فیلد های string باید مثل text رفتار بشهکدوم فیلدها شامل عدد، تاریخ و یا ...فرمت ذخیره سازی برای dateو سفارشی سازی های شخصیبرای مدیریت منابع در این مدل هم شما میتونین از قابلیت runtime fields استفاده کنین. runtime fields به شما این امکان رو میده تا بدون اپدیت کردن داده های جدید رو ذخیره کنین، همچنین برای پیوستگی ساختن ایندکس ها، مدیریت منابع و پرفورمنس بهتر از این قابلیت کمک بگیرین.نتیجه گیری :در الستیک سرچ به قطعیت میشه گفت که مفهوم مپینیگ یکی از مهمترین مفاهیم به حساب میاد چرا که تعیین کننده بسیاری از موارد در هنگام ذخیره سازی داده هاست.بصورت پیش فرض زمانی که شما داده هاتون رو میخواین داخل الستیک سرچ ذخیره کنین، الستیک سرچ از داینامیک مپینیگ استفاده میکنه برای همین شما ممکن با مستقیما باهاش برخورد نداشته باشین اما یادتون باشه که مپینیگ دستتون برای بر طرف کردن خیلی از نیازهاتون باز میزاره.</description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Fri, 01 Apr 2022 17:25:54 +0430</pubDate>
            </item>
                    <item>
                <title>ردگیری رفتار ارزهای دیجیتال با کمک الستیک سرچ ( قسمت اول)</title>
                <link>https://virgool.io/@hadivarposhti/%D8%B1%D8%AF%DA%AF%DB%8C%D8%B1%DB%8C-%D8%B1%D9%81%D8%AA%D8%A7%D8%B1-%D8%A7%D8%B1%D8%B2%D9%87%D8%A7%DB%8C-%D8%AF%DB%8C%D8%AC%DB%8C%D8%AA%D8%A7%D9%84-%D8%A8%D8%A7-%DA%A9%D9%85%DA%A9-%D8%A7%D9%84%D8%B3%D8%AA%DB%8C%DA%A9-%D8%B3%D8%B1%DA%86-olhciidn63lo</link>
                <description>این روزا بحث خرید و فروش ارزهای دیجیتال به شدت داغ شده و انواع پلتفرم های موجود هم هرکدوم مشکلات خاص خودشون رو برای کاربر ایرانی  دارن تصمیم گرفتم تا با کمک الستیک سرچ یک پنل کاربری ساده برای ردگیری بازار، برای کسایی که لازمش دارن راه بندازم امیدوارم که مفید باشه.من ابدا متخصص در رابطه با ارزهای دیجیتال نیستم و صرفا علاقمند به کار کردن با داده هام پس سعی میکنم مفاهیم مرتبط با ارز دیجیتال رو تا جایی که میشه بدون تغییر بیارم تا اشتباهی پیش نیاد.این رو هم باید اضافه کنم که نوشته من در دوقسمت منتشر میشه که قسمت اول مراحل ارتباط الستیک سرچ با دیتاست ورودی و در قسمت دوم تنظیمات داخل کیبانا رو توضیح میدم.ساخت data pipeline :در مرحله اول برای اینکه بتونیم پنل کاربریمون رو بسازیم نیاز به راهی داریم که بتونیم این اطلاعات رو از طریق اون بدست بیاریم، که من api لازم رو از وبسایت coinmarketcap بدست اوردم.الستیک سرچ ورودی های مختلفی رو میتونه پردازش کنه اما برای سادگی کار از فرمت json استفاده میکنم البته اینم اضافه کنم که همه چی بستگی به نوع پاسخی داره که شما از api خودتون میگیرین ولی در هرصورت فرایند کار به یک شکل انجام میشه . برای مثال یه نمونه از فرمت داده هایی رو که از api گرفتم بشکل زیر :{        
       &amp;quotid&amp;quot: &amp;quotbitcoin&amp;quot,
        &amp;quotname&amp;quot: &amp;quotBitcoin&amp;quot,
        &amp;quotsymbol&amp;quot: &amp;quotBTC&amp;quot,
        &amp;quotrank&amp;quot: &amp;quot1&amp;quot, 
       &amp;quotprice_usd&amp;quot: &amp;quot573.137&amp;quot, 
       &amp;quotprice_btc&amp;quot: &amp;quot1.0&amp;quot,
        &amp;quot24h_volume_usd&amp;quot: &amp;quot72855700.0&amp;quot,
        &amp;quotmarket_cap_usd&amp;quot: &amp;quot9080883500.0&amp;quot, 
       &amp;quotavailable_supply&amp;quot: &amp;quot15844176.0&amp;quot,
        &amp;quottotal_supply&amp;quot: &amp;quot15844176.0&amp;quot, 
       &amp;quotpercent_change_1h&amp;quot: &amp;quot0.04&amp;quot,
        &amp;quotpercent_change_24h&amp;quot: &amp;quot-0.3&amp;quot,
        &amp;quotpercent_change_7d&amp;quot: &amp;quot-0.57&amp;quot, 
       &amp;quotlast_updated&amp;quot: &amp;quot1472762067&amp;quot
    }برای اتصال بین api و الستیگ سرچ میشه راه های محتلفی رو پیاده کرد که همش قرار دادی بین شما و طرفی که ازش api  اطلاعات رو میگیرین اینجا من از پلاگین http_poller input استفاده کردم ولی شما بسته به نیازتون میتونین از ابزارهای مختلفی استفاده کنین، که براتون داده ها رو تجمیع، رمزگشایی و به url مورد نظرتون ارسال کنه.خب توی این مرحله راجع به لاگ استش و تنظیماتش توضیح میدم.ورودی لاگ استش: برای راه اندازی لاگ استش نیاز داریم که فایل logstash.conf  رو بصورتی که میخوایم ایجاد کنیم، درمورد جزییات لاگ استش بعد توضیحات بیشتری میدم ولی فعلا باید تا اینجا بگم که فایل مورد نظرمون سه قسمت داره که قسمت اولش (input) برای تنظیم راه ورودی اطلاعات به داخل لاگ استش که به این صورت من اون رو نوشتم.input { 
      http_poller{    
                 urls =&gt; {     
                         url =&gt; &amp;quothttps://api.coinmarketcap.com/v1/ticker&amp;quot    }    
                 request_timeout =&gt; 60    
                 schedule =&gt; { cron =&gt; &amp;quot0 * * * *&amp;quot}   
                  codec =&gt; &amp;quotjson&amp;quot    
                  metadata_target =&gt; &amp;quothttp_poller_metadata&amp;quot  
          }
}بصورت کلی URL همون آدرس ورودی که قرار لاگ استش از اون دیتای مورد نظر رو بدست بیاره، request_timeout نوعی  cron job که بازه زمانی دریافت اطلاعات رو مشخص میکنه و من اون رو برای ساعتی یکبار تنظیم کردم، کدکت josn و متادیتا ها هم گزینه های اختیاری هستن که برای انالیز بهتر ورودی نوشتمشون.فیلتر لاگ استش:از اونجایی که جواب API یک فایل JSON و از اونجایی که برای بهینه کردن نتایجمون نیاز به ذخیره سازی پاسخ API به فرمت دلخواه داریم که برای این کار از فیلتر mutate استفاده میکنیم لازم اشاره کنم که تمامی فیلدها رو به دیتا تایپ integer  ذخیره می کنیم همچنین برای فیلد &quot;last_updated&quot; که اخرین وضعیت اون کوین رو برای ما مشخص میکنه رو با استفاده از دیتاتایپ date و پردازشگر UNIX  نگه میداریم.filter {
  mutate {
    convert =&gt; { &amp;quottotal_supply&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotrank&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotprice_usd&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotmarket_cap_usd&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotprice_btc&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotpercent_change_24h&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotmax_supply&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotpercent_change_7d&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotavailable_supply&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotpercent_change_1h&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quot24h_volume_usd&amp;quot =&gt; &amp;quotinteger&amp;quot }
   }
  date {
    match =&gt; [&amp;quotlast_updated&amp;quot,&amp;quotUNIX&amp;quot]
    target =&gt; &amp;quotlast_updated&amp;quot
  }
}خروجی لاگ استش :فکر میکنم این قسمت نیاز به توضیح زیادی نداشته باشه و واضح باشه که خروجی ما قرار به الستیک سرچ منتقل بشه :output {
  elasticsearch { 
    hosts =&gt; [&amp;quotlocalhost:9200&amp;quot] 
  }
}ترکیب هر سه قسمت بالا در فایل تنظیمات لاگ استش و اجرای اون باعث میشه که لاگ استش شروع به دریافت اطلاعات از وبسایت ارسال کننده داده ها و ارسال اون به الستیک سرچ. از اینجا به بعد زمانی که ایندکس اطلاعات داخل الستیک سرچ ساخته شد با استفاده از کوئری های الستیک سرچ می تونین شروع به کار کنین.و در نهایت خروجی کامل لاگ استش: input {
  http_poller {
    urls =&gt; {
      url =&gt; &amp;quothttps://api.coinmarketcap.com/v1/ticker&amp;quot
    }
    request_timeout =&gt; 60
    schedule =&gt; { cron =&gt; &amp;quot0 * * * *&amp;quot}
    codec =&gt; &amp;quotjson&amp;quot
  }
}

filter {
  mutate {
    convert =&gt; { &amp;quottotal_supply&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotrank&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotprice_usd&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotmarket_cap_usd&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotprice_btc&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotpercent_change_24h&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotmax_supply&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotpercent_change_7d&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotavailable_supply&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quotpercent_change_1h&amp;quot =&gt; &amp;quotinteger&amp;quot }
    convert =&gt; { &amp;quot24h_volume_usd&amp;quot =&gt; &amp;quotinteger&amp;quot }
    add_field =&gt; { &amp;quottoken&amp;quot =&gt; &amp;quot&lt;yourUserToken&gt;&amp;quot }
  }
  date {
    match =&gt; [&amp;quotlast_updated&amp;quot,&amp;quotUNIX&amp;quot]
    target =&gt; &amp;quotlast_updated&amp;quot
  }
}

output {
  elasticsearch {
    host =&gt; &amp;quot[&amp;quotlocalhost:9200&amp;quot]
  }
}تا اینجای کار داده های ما شروع به وارد شدن داخل الستیک سرچ کردن از اینجا به بعد باید تنظیمات داخل کیبانا رو انجام بدیم .خب همونطور که گفتم این قسمت رو همین جا تموم میکنم که نوشته بیشتر از این طولانی نشه و در قسمت  بعد تنظیمات داخل کیبانا رو  توضیح میدم، امیدوارم  که مفید باشه . در آخر هم باید اضافه کنم خوشحال میشم جایی نقصی وجود داره بهم بگین که اون تکمیل کنم.</description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Mon, 24 Jan 2022 21:47:44 +0330</pubDate>
            </item>
                    <item>
                <title>راهنمای نصب و پیاده سازی (الستیک استک) ELK با کمک داکر</title>
                <link>https://virgool.io/@hadivarposhti/%D8%B1%D8%A7%D9%87%D9%86%D9%85%D8%A7%DB%8C-%D9%86%D8%B5%D8%A8-%D9%88-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-%D8%A7%D9%84%D8%B3%D8%AA%DB%8C%DA%A9-%D8%A7%D8%B3%D8%AA%DA%A9-elk-%D8%A8%D8%A7-%DA%A9%D9%85%DA%A9-%D8%AF%D8%A7%DA%A9%D8%B1-qg3gppvfsh3q</link>
                <description> این چند مدت که شروع کردم به نوشتن تلاشی بوده برای منتقل کردن دونسته هام به بقیه، دوتا نکته هست که فکر میکنم لازمه تا باز بگم یکی اینکه تمام این سری نوشته ها براساس تجربه های شخصی خودم در پیاده سازی و استفاده از این ابزار توی پروژه های واقعیه و شاید از جنبه های مختلف برای نیاز شما مفید نباشه، پس در استفاده از این ابزار حتما قبلش نیاز سنجی برای کاری که میخواین انجام بدین بکنین. دوم و از همه مهمتر اینکه همیشه سورس اصلی رو بخونین چرا که چیزهایی رو پیدا میکنین که شاید توی نوشته هایی مثل این پیداش نکنین.این قسمتی که شروع کردم به نوشتنش به پیشنهاد دوستیه که به تازگی باهاش آشنا شدم و صرفا میخوام روش نصب و راه اندازی خانواده ابزار الستیک رو با استفاده از داکر توضیح بدم. راستی اگه خواستین از الستیک و کیبانا به صورت سرویس روی کامپیوتر ویندوزی هم استفاده کنین از نوشته مصطفی هم میتونین چیزای خوبی یاد بگیرین.الستیک استک : الستیک سرچ، لاگ‌استش، کیباناخانواده ELK (الستیک استک) روی انواع مختلفی از سیستم عامل ها و با استفاده از روش های مختلفی قابل نصب شدن هستش اما ساده ترین و راحترین روش استفاده از بستر داکر برای راه اندازی و پیاده سازیه.چرا داکر؟ مهمترین دلایلی که میتونم بگم برای اینکه داکر یکی از بهترین گزینه ها برای اینکاره:  پایداری ، استحکام ، امنیت - و ماهیت توزیع شده داکر که می تونه راهی بسیار آسون برای راه اندازی خانواده الستیک باشه.راه اندازی :خب روی داکر روش های مختلفی میشه خانواده الستیک رو نصب و راه انداخت مثلا میتونین هرکدوم از ابزار هارو بصورت ایمیج های جداگونه پول بگیرین و کانتینرش رو با کمک داکر کامپوز راه بندازین. برای این نوشتار من از این ایمیج که هرسه ابزار رو بصورت همزمان و موازی اجرا میکنه،  استفاده کردم.قبل از هرچیزی :باید بگم که من این پروسه رو روی سیستم عامل مک او اس اجرا کردم که بصورت پیش فرض فایل جاوا و JVM رو نصب داره اما اگه شما میخواین از این نوشتار کمک بگیرین برای نصب روی یه سیستم عامل دیگه حتما چک کنین که جاوا و jvm  رو نصب داشته باشین.اولین قدم :برای شروع از دستور زیر کمک میگیریم  تا ریپوزیتوری مورد نظرمون رو روی سیستم عامل دلخواهمون کلون بگیریم: و اگه در طول کلون گرفتن به مشکلی برنخورین باید همچین چیزی رو توی ترمینال باهاش روبرو بشینgit clone https://github.com/deviantony/docker-elk.git

remote: Counting objects: 1112,  done.
remote: Total 1112 (delta 0), reused 0 (delta 0), pack-reused 1112
Receiving objects: 100% (1112/1112), 234.87 KiB | 84.00 KiB/s,  done.
Resolving deltas: 100% (414/414),  done.
Checking connectivity... done.توی مرحله بعد دستور زیر رو اجرا میکنین تا وارد پوشه ای بشین که کلون گرفتین :cd  /docker-elkو با دستور زیر داکر الستیک رو میاریدش بالا :docker-compose  up -dیه نکته تا یادم نرفته اگه دستور بالا رو بدون فلگ (( d- )) بزنین میتونین لاگ های داکرتون رو ببینین تا اگه مشکلی زمان راه اندازی وجود داره متوجهش بشین، فقط یادتون نره که بعدش اگه اون صفحه از ترمینال رو بخواین ببندین و یا دستور دیگه اجرا کنین داکرتون چون توی بکگراند اجرا نمیشه مجبورین بیارینش پایین .اطمینان از راه افتادن :از لحظه ای که شروع به کلون گرفتن میکنین تا زمانی که داکرتون شروع به اینیت شدن میکنه ممکنه که چند دقیقه ای طول بکشه که بستگی به سرعت اینترنت داره، بعدش برای اینکه مطمئن بشین که داکرتون بالا اومده با استفاده از دستور زیر میتونین لیست کانتینر هاتون رو چک کنین.docker ps
CONTAINER ID        IMAGE                             COMMAND                  CREATED             STATUS              PORTS                                            NAMES
a1a00714081a        dockerelk_kibana                  &amp;quot/bin/bash /usr/loca…&amp;quot   54 seconds ago      Up 53 seconds       0.0.0.0:5601-&gt;5601/tcp                           dockerelk_kibana_1
91ca160f606f        dockerelk_logstash                &amp;quot/usr/local/bin/dock…&amp;quot   54 seconds ago      Up 53 seconds       5044/tcp, 0.0.0.0:5000-&gt;5000/tcp, 9600/tcp       dockerelk_logstash_1
de7e3368aa0c        dockerelk_elasticsearch           &amp;quot/usr/local/bin/dock…&amp;quot   55 seconds ago      Up 54 seconds       0.0.0.0:9200-&gt;9200/tcp, 0.0.0.0:9300-&gt;9300/tcp   dockerelk_elasticsearch_1یه نکته ای که بر اساس تجربه بهش رسیدم اینه که همیشه چک کنین اگه اولویت بالا اومدن ابزار خانواده الستیک استک ( این اولویت بر اساس اولویتی که داخل فایل داکر کامپوز یا داکر استکه نوشته شده) چیزی به جز ( الستیک - لاگ استش - کیبانا) بود همیشه یه جایی از تنظیماتتون ایراد داره.احتمالا باید بدونین که الستیک پورت پیش فرضی که استفاده میکنه پورت (۹۲۰۰/۹۳۰۰) ، لاگ استش از پورت پیش فرض (۵۰۰۰/۵۰۴۴) و کیبانا هم از پورت پیش فرض (۵۶۰۱) استفاده میکنه و شما با کمک دستور زیر میتوین مطمئن بشین که همه چی درسته. curl  http://localhost:9200/الستیک بصورت پیش فرض از نام کاربری و رمز عبور :user: elasticpassword: changemeاستفاده میکنه.و در نهایت هم میتونین با استفاده از ادرس زیر به کیبانا دسترسی داشته باشین:  http://localhost:5601جمع بندی :تا اینجای کار شما خانواده الستیک استک رو بر روی بستر داکر اوردین بالا از حالا به بعد اصل ماجرا شروع میشه که چطوری بخواین دیتاتون رو بفرستین داخل الستیک، که اینکار توی این سناریو با کمک لاگ استش عملی میشه که اونرو سعی میکنم بعدا هم خود از لاگ‌استش رو بیشتر بگم و هم روش استفاده ازش رو توی ساختارمون توضیح بدم. امیدوارم که تا اینجای کار براتون مفید باشه، سعی میکنم بیشتر و با جزیات کاملتر باز هم ادامه بدم.پی نوشت: فکر میکنم لازمه تا چنتا نکته رو اینجا واضح تر توضیح بدم:شما برای اینکه مطمئن بشین از اینکه ابزارهاتون به درستی کار میکنن میتوین به دو صورت اونها رو امتحان کنین یا مثل بالا با استفاده از دستور curl  توی محیط ترمینال و یا با استفاده از مرورگر و رفتن به پورتی که اون ابزار داره استفاده میکنه.در رابطه با نام کاربری و رمزعبور، الستیک بصورت پیش فرض برای افزایش امنیتش مخصوصا زمانی که میخواین از اون در سرور استفاده کنین پیشنهاد میده که این قابلیت رو فعال کنین، به این صورت که این ابزار ها با کمک این نام کاربری و رمز عبور بتونن با همدیگه ارتباط امن بگیرن، البته که شما میتوین اونها رو تغییر و یا غیر فعالشون هم بکنین.نکته اخر هم اینکه من توی پیاده سازیم از فایل داکر کامپوز استفاده کردم اما شما میتونین از فایل داکر استک هم استفاده کنین ولی به دو چیز باید توجه داشته باشین یکی اینکه فرمت هردوتا فایل yml که حتما باید تحت ساختار خودشون باشن هرمدل، تغییری اگه باعث بشه ساختار فایل عوض بشه مثل اضافه شدن یک اسپیس اضافی و ... میتونه باعث بشه که داکرتون بالا نیاد و مورد دوم هم اینکه همیشه به مقدار رمی که به jvm  اختصاص میدین داخل فایل های اجرایی داکر دقت کنین چون ممکنه که مقدارش از میزان مورد نیازتون بیشتر و کمتر باشه و در ساده ترین حالت شما با خطای پول کانکشن مواجه بشین.  </description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Fri, 01 Oct 2021 17:09:36 +0330</pubDate>
            </item>
                    <item>
                <title>فایل بیت چیه و به چه دردی میخوره؟</title>
                <link>https://virgool.io/coderlife/%D9%81%D8%A7%DB%8C%D9%84-%D8%A8%DB%8C%D8%AA-%DA%86%DB%8C%D9%87-%D9%88-%D8%A8%D9%87-%DA%86%D9%87-%D8%AF%D8%B1%D8%AF%DB%8C-%D9%85%DB%8C%D8%AE%D9%88%D8%B1%D9%87-v8iegrfqevet</link>
                <description>خب از اونجایی که یه مدت دارم سعی میکنم چیزایی که یادگرفتم رو بنویسم تا یادم نره اینبار میخوام راجع به ابزاری به اسم فایل بیت بنویسم چرایی استفاده ازش رو توضیح بدم.تعریف:فایل بیت بنا به تعریفی که خود وبسایت رسمیش میده : یک انتقال دهنده سبک برای ارسال و متمرکز کردن داده هاست. فایل بیت به عنوان یک عامل بر روی سرور نصب و لاگ فایل ها و یا محل لاگ فایل هارا مورد بررسی قرار میدهد، لاگ هارا جمع آوری کرده و آنها رو به الستیک و یا لاگ استش ارسال میکند.نحوه عملکرد فایل بیتحالا داستان چیه؟ داستان اینه که اصلا فایل بیت به چه دردی میخوری در صورتی که خود ابزار، لاگ استش هم یکی از وظایفش جمع آوری لاگ هاست.(در رابطه با لاگ استش سعی میکنم بیشتر توضیح بدم بعدا)در حالت کلی وقتی متوجه شدم که این دو ابزار دارن تقریبا یه کار انجام میدن یکی از ابروهامو بالا انداختم که الان واقعا چرا باید فایل بیت نصب کنم ولی این قضیه فرای مقایسه است و ما در بیشتر سناریو ها که مستقیما درگیر استفاده از الستیک استک هستیم نیاز به استفاده از فایل بیت در کنار لاگ استش داریم، توضیح میدم چرا. در واقع قبل از تولد فایل بیت، لاگ استش توسط جوردن سیسل برای مدیریت، خواندن لاگ فایل های سنگین از ورودی های مختلف بوجود اومد و بعد از اینکه هم جوردن به تیم الستیک اضافه شد لاگ استش از یه ابزار مستقل تبدیل به قسمتی از یه ابزار جامع تر به اسم الستیک استک ( لاگ استش‌، الستیک ، کیبانا) شد.نقش لاگ استش توی این ابزار به این صورت بود که برای پیاده سازی یک سیستم متمرکز جمع اوری لاگ ها نیاز به ابزاری بود که لاگ هارو بتونه از منابع مختلفی بخونه و اونها رو بصورت متمرکز به یکجا منتقل کنه، بصورت کلی این اتفاق عالی برای هر برنامه نویسیه که لاگ های پروژش متمرکز جمع بشن اما ایراد ماجرا برمیگرده به پرفورمنس لاگ استش.لاگ استش برای اجرا شدن روی سرور نیاز به JVM داره و این نیاز در کنار روبی میشه اصلی‌ترین عامل افزایش مصرف مموری [این وسط ها این رو هم اضافه کنم که عدم تنظیم درست در زمان نصب در کنار افزایش مصرف مموری باعث بوجود اومدن خطای Pool connection  هم میشه ] خصوصا زمانی که چنتا ورودی داشته باشین و فیلترینگ روی ورودی ها اعمال کنین.[این رو هم باید اضافه کنم که منظورم از روبی پروژه هایی هستش که سورس کدشون به زبان روبی نوشته شده]پروتکل Lumberjack و بهینه سازی های اولیه :لامبرجک بعنوان اولین تجربه ها برای ایجاد یه لاگ کالکتور سبک قبل از ارسال به یه پلتفرم دیگه برای پردازش به حساب میاد. ایده پشت لامبرجک این بودش که اگه یه پروتکل شبکه توسعه بدیم، برای جمع آوری حجم زیادی از داده ها هم کارامدتره هم حافظه کمتری مصرف میکنه و هم از رمزگذاری هم پشتیبانی میکنه. البته لامبرجک بعدا به logstash-forwarder  تغییر اسم داد، ایده پشت سرش هم یه مقداری تغییر کرد (که بعدا راجع بهش بیشتر توضیح میدم، فعلا تا همین جا کافیه)تولد Beats :نسخه دوم لامبرجک یا همون Logstash-Forwarder که الان دیگه منسوخ شده شده پایه اصلی خانواده Beats.تفاوت اصلی بین این دو نسخه توی ویژگی هایی مثل پشتیبانی از json تودرتو و یا کنترل بهتر فشار بودش.(البته خانواده ‌Beats گستره ای از محصولات مختلف هستن  و از اونجایکه توی این نوشته فقط فایل بیت هدف اصلی راجع به اونها هم بعدا توضیح میدم).خب پس کی از لاگ استش و یا فایل بیت استفاده کنیم :ساده ترین جواب اینه که وقتی لاگ هاتون هنوز زیاد نشدن،شما نیاز به استفاده از هردوی اونهارو دارین اما چرا؟ چون از لاگ استش به عنوان ابزاری برای جمع آوری و متمرکز کردن لاگ ها و از فایل بیت به عنوان ابزاری برای انتقال لاگ ها نیاز دارین چرا که فایل بیت یک ابزار کاملا سبک ، که از روش های رمزنگاری مثل ssl  پشتیبانی میکنه و بشدت هم قابل اعتماده ( اینم باید اضافه کنم که ساختار فایل بیت اینطوری که یک  queue  از اخرین وضعیت لاگ ها توی خودش داره که همیش از طریق اون از آخرین لاگ موجود توی سیستم خبر دار در نتیجه شما هیچوقت لاگ تکراری به خاطر بازخوانی دوباره لاگ فایل ها ندارین)توی تصویر زیر رابطه بین لاگ استش و فایل بیت رو بهتر متوجه میشین:تنظیمات فایل بیت برای اتصال به لاگ استش :فایل بیت تنظیم میشه که لاگ هارو بسمت لاگ استش بفرسته تا از طریق فایل بیت بره به سمت الستیک، یادتون باشه که حتما پورت خروجی فایل بیت رو روی ۵۰۴۴  قرار بدین. در زیر من میخوام لاگ های apache  رو برای خودم جمع آوری کنم filebeat.inputs:
- input_type: log
  paths:
    - /var/log/httpd/access.log

document_type: apache-access
fields_under_root: true

output.logstash:
  hosts: [&amp;quot127.0.0.1:5044&amp;quot] یچیز رو هم که باز باید اینجا اضافه کنم ورودی هایی که فایل بیت ازشون پشتیبانی میکنه به سه دسته تقسیم میشه : خوندن از فایل های بصورت استاتیک خوندن از فایل ها بصورت استریم برای مواردی که حجم فایل ها زیاده و از طریق سوکت ( که فعلا در حالت آزمایشی ولی توی پروژه من که بدون مشکل داره کار میکنه، باز برای رفتن سمتش با احتیاط برین)تنظیمات لاگ استش برای دریافت از فایل بیت و ارسال به الستیک:توی تنظیمات لاگ استش شما باید ورودی تون رو روی پورت ۵۰۴۴ قرار بدین، برای لاگ ها فیلتر بنویسین تا بر اساس اون بتونین ایندکس دلخواهتون رو بسازین و خروجی رو هم روی پورت ۹۲۰۰ تنظیم کنین تا خروجی فایل بیت بشه الستیک. input {
  beats {
    port =&gt; 5044
  }
 }

filter {
  grok {
    match =&gt; { &amp;quotmessage&amp;quot =&gt; &amp;quot%{IPORHOST:clientip} %{USER:ident} %{USER:auth} \[%{HTTPDATE:time}\] &amp;quot(?:%{WORD:verb} %{NOTSPACE:request}(?: HTTP/%{NUMBER:httpversion})?|%{DATA:rawrequest})&amp;quot %{NUMBER:response} (?:%{NUMBER:bytes}|-)&amp;quot }
  }
  date {
    match =&gt; [ &amp;quottimestamp&amp;quot , &amp;quotdd/MMM/yyyy:HH:mm:ss Z&amp;quot ]
  }
 }

output {
  elasticsearch { hosts =&gt; [&amp;quotlocalhost:9200&amp;quot] }
  }جمع بندی :روش دیگه ای که از طریق اون میتونین لاگ هارو جمع کنین و توی این مقاله نمی گنجه بهش میگن Ingest node  که البته برای حجم زیاده دیتا اصلا پیشنهاد نمیکنمش.توی این مقاله سعی کردم که کلیتی از چرایی استفاده از فایل بیت بگم و اینکه مثال هایی هم از مدل تنظیماتشون بیارم که شاید بدردتون بخوره. البته طبق روال باز هم میگم لزوما استفاده از این سناریو چیز قطعی نیست و شما حتما باید قبل از شروع نیازسنجی لازم برای پروژتون رو انجام بدین. دست آخر هم اگه حوصله خوندن ندارین این اینفوگرافی رو ببینین که مقایسه ای کرده بین لاگ استش و فایل بیت که میتونه دید خوبی برای شروع بهتون بده. مقایسه بین لاگ استش و فایل بیتپی‌نوشت :کلمه harvesting که توی تصویر دوم دیدین، رو اینطوری میشه تعریف کرد که ؛ به پروسه خوانش لاگ ها توسط فایل بیت harvesting گفته میشه به زبون ساده تر هرباری که فایل بیت داره لاگ هارو میخونه درواقع داره harvest میکنه.لازمه که یه نکته ای رو هم اینجا اضافه کنم همونطور که بالاتر هم گفتم لامبرجک پایه اولیه  از خانواده Beats  به حساب میاد اما ارتباطش صرفا نوعی پروتکل ارتباطی اختصاصی بین لاگ استش و فایل بیته که قرار نیازمندی هایی مثل امکان رمزگذاری رو پوشش بده . </description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Thu, 23 Sep 2021 23:58:33 +0330</pubDate>
            </item>
                    <item>
                <title>لاگ استش، اتصال پایگاه داده RDBMS به پایگاه داده NoSQl</title>
                <link>https://virgool.io/@hadivarposhti/%D9%84%D8%A7%DA%AF-%D8%A7%D8%B3%D8%AA%D8%B4-%D8%A7%D8%AA%D8%B5%D8%A7%D9%84-%D9%BE%D8%A7%DB%8C%DA%AF%D8%A7%D9%87-%D8%AF%D8%A7%D8%AF%D9%87-rdbms-%D8%A8%D9%87-%D9%BE%D8%A7%DB%8C%DA%AF%D8%A7%D9%87-%D8%AF%D8%A7%D8%AF%D9%87-nosql-zbowsnx5cayy</link>
                <description>احتمالا براتون پیش اومده که لازم باشه تا توی پروژتون جایی پایگاه داده پروژه ریلیشنال رو به یک پایگاه داده نوریلیشنال وصل کنین. خب این قضیه از جنبه های مختلفی باید بررسی بشه تا بشه براش یک راه حل منطقی با هزینه درست بدست آورد.این جنبه ها میتونه شامل براورد هزینه های این کار برای پروژه باشه (زمان ، ...)  یا حتی scale پایگاه داده اون پروژه که ایا امکان انجام همچین کاری رو داره یا نه . بطورکلی این مسأله چیزی که باید با دقت بهش فکر کرد. اما بطورکلی یکی از راههایی که وجود داره برای اینکار استفاده از ابزار لاگ استش برای این کاره.لاگ استش:بانگاهی به وبسایت الستیک، لاگ استش اینطوری تعریف میشه:لاگ استش قسمتی از الستیک استک می باشد که همراه با مجموعه ابزار بیت عرضه میشود.لاگ استش یک پردازش گر داده سمت سرور میباشد که داده هارا از ورودی های مختلف بصورت همزمان دریافت و تبدیل و به خروجی دلخواه ارسال مینماید.خب توی این سناریو که میخوام اینجا راجع بهش بنویسم اینطوری که من یه ‍‍پروژه ای دارم که بنا به نیاز پروژه میخوام قسمتی از اون رو وصل کنم به الستیک سرچ. این پروژه یک پروژه در حوزه حمل و نقل به این صورت که  یک فروشگاه زنجیره ای بزرگ که خورده فروشی داره مجبور محصول رو بصورت عمده از تولید کننده بخره، داخل انبارش ذخیره کنه و در مرحله اخر در زمان نیاز محصولات رو بین فروشگاه هاش تقسیم کنه. بصورت پیش فرض من برای همچین سناریویی پارامتر های مختلفی دارم که به ازای هربار کوئری روی دیتابیس مجبورم از اونها استفاده کنم در نتیجه جداول زیادی درگیر میشن تا محاسبه لازم انجام بشه، پس لود سنگینی رو دیتابیس دارم. کارفرما میخواد که یک فیچر جدید که یک باکس جستجو رو به پروژش اضافه کنم در نتیجه مجبورم از جداول مختلفی استفاده کنم که این ویژگی اضافه بشه،حالا راه حل چیه ؟همونطوری که قبلا هم گفتم برای اینکار راه حل های مختلفی وجود داره و باید جنبه های مختلفی رو سنجید تا به راه حل درست رسید در هر حال یکی از این راه حل ها استفاده از کتابخانه JDBC  که میخوام راجع بهش بنویسم.کتابخانه ‌jdbc :برای اینکه این نوشته طولانی نشه پیشنهاد میکنم توضیحات این کتابخانه از داخل وبسایتش بخونین تا با جزییاتش بیشتر آشنا بشین.من برای اینکه بتونم باکس جستجو رو پیاده سازی کنم میخوام که باکس جستجو از اینجین الستیک استفاده کنه برای اینکار نیاز دارم به دیتاهای داخل پایگاه دادم، پس چطوری باید اونهارو بکشم بیرون و بریزمشون داخل الستیک .همونطوری که در تعریف لاگ استش نوشتم :  داده هارا از ورودی های مختلف بصورت همزمان دریافت و تبدیل و به خروجی دلخواه ارسال مینماید.پس توی این معماری بین الستیک و پایگاه داده، لاگ استش قرار میگیره، که دیتا هارو بخونه و به الستیک منتقل کنه اما هنوز یه مشکل بزرگتر داریم و اون اینکه پایگاه داده من داخل پروژه یک پایگاه داده ریلیشنال اما الستیک یک پایگاه داده نو ریلیشنال پس راه حل چیه ؟ اینجاست که از کتابخانه jdbc  باید استفاده کنیم. معماری لازم برای ارتباط بین پایگاه داده و الستیک به فرم زیر میباشد.برای اینکار نیاز داریم تا تنظیمات لاگ استش رو تغییر بدیم به صورت زیر:input {
   jdbc {
        jdbc_user =&gt; &amp;quotpostgres&amp;quot
        jdbc_driver_class =&gt; &amp;quotorg.postgresql.Driver&amp;quot
        jdbc_connection_string =&gt; &amp;quotjdbc:postgresql://localhost:5432/project_development
user=hadii&amp;quot
        jdbc_driver_library =&gt; &amp;quot/usr/share/logstash/logstash-core/lib/jars/postgresql-42.2.22.jar&amp;quot     
        jdbc_password =&gt; &amp;quotpostgres&amp;quot
        statement =&gt; &amp;quotselect *, id from orders;&amp;quot
        #schedule =&gt; &amp;quot0 0 * * MON&amp;quot
    }
}
output {
      elasticsearch {
        hosts =&gt; &amp;quothttp://elasticsearch:9200&amp;quot
        index =&gt; &amp;quotshipment_order_%{+YYYY_MM_dd}&amp;quot
    }
    stdout {}
}خب برای درک بهتر تنظیمات بالا اونها رو بصورت خط به براتون توضیح میدم:بصورت کلی لاگ استش دارای یک ورودی و یک خروجی میباشد که اصلاحالا به اونها input و output میگیم.input { }
output { }در  مرحله بعد jdbc تنظیمات اتصال به لاگ استش که هرکدوم بصورت زیر تعریف میشن:jdbc {         
jdbc_user =&gt; &amp;quotpostgres&amp;quot        نام کاربری پایگاه داده داخل پروژتونه   jdbc_driver_class =&gt; &amp;quotorg.postgresql.Driver&amp;quot       کلاس کتابخونه jdbc این کلاس به ازای پایگاه داده های مختلف فرق میکنه  jdbc_connection_string =&gt; &amp;quotjdbc:postgresql://localhost:5432/project_development user=hadii&amp;quot        این قسمت URL  پایگاه داده پروژست که داخل پروژه تعریف شده ، فراموش نکنین که در اخر این URL حتما اسم یوزر پایگاه دادتون رو هم اضافه کنن jdbc نیاز داره تا بهش وصل بشه jdbc_driver_library =&gt; &amp;quot/usr/share/logstash/logstash-core/lib/jars/postgresql-42.2.22.jar&amp;quot       مسیر قرار گیری فایل کتابخونست و چون من اینجا از داکر استفاده کرده بودم مسیر داخل داکر رو وارد کردم، یادتون نره که  اگه مثل من از داکر استفاده کردین توی فایل راه انداز داکر ادرس بیرون فایل کتابخونه رو هم اضافه کنینjdbc_password =&gt; &amp;quotpostgres&amp;quot   رمز عبور پایگاه داده پروژتونه       statement =&gt; &amp;quotselect *, id from orders;&amp;quot        این قسمت رو به دو صورت میتونین استفاده کنین یا مستقیما کوئری sql رو اینجا بنویسین تا کتابخونه jdbc  اون رو اجرا کرده و دیتای مد نظر رو براتون بیاره و یا اینکه اگه این کوئری ممکن تغییر بکنه میتونین اون رو داخل یک فایل بزارین و ادرس و اون فایل رو بدین تا فایل پارس شده کوئری اجرا و در نهایت دیتا به الستیک منقل بشه.#schedule =&gt; &amp;quot0 0 * * MON&amp;quot
 }  این خط رو هم که من اون رو کامنت کردم برای تعریف زمان اجرای فچ کردن دیتاست .و در نهایت هم خروجی لاگ استش شما میشه الستیک سرچ که بصورت زیر من اون رو تعریف کردمoutput {      
       elasticsearch {         
        hosts =&gt; &amp;quothttp://elasticsearch:9200&amp;quot         
        index =&gt; &amp;quotshipment_order_%{+YYYY_MM_dd}&amp;quot    
 }جمع بندی :همونطور که بارها هم در طول این نوشته توضیح دادم برای استفاده از این کتابخونه حتما باید جنبه های مختلف رو بررسی کنین بعد اقدام به استفاده بکنین چرا که هربار استفاده از این کتابخونه لود اضافی سمت پایگاه داده اصلیتون میاره که ممکن تاثیر منفی روی پروژتون داشته باشه.</description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Fri, 10 Sep 2021 21:25:09 +0430</pubDate>
            </item>
                    <item>
                <title>نحوه پیاده سازی elasticsearch در پروژه روبی آن ریلز - قسمت دوم</title>
                <link>https://virgool.io/@hadivarposhti/%D9%86%D8%AD%D9%88%D9%87-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-elasticsearch-%D8%AF%D8%B1-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D8%B1%D9%88%D8%A8%DB%8C-%D8%A2%D9%86-%D8%B1%DB%8C%D9%84%D8%B2-%D9%82%D8%B3%D9%85%D8%AA-%D8%AF%D9%88%D9%85-u2kcemzymdns</link>
                <description>توی قسمت اول تا اینجا پیش اومدیم که الستیک سرچ داخل پروژه کاملا پیاده سازی شد و قادر بود تا غلط های تایپی رو هم تا دو حرف تشخیص بده . این رو دوباره باید اضافه کنم که تمام این پروسه شامل تجربه من از نحوه پیاده سازی و قطعا بهتر هم میشه اون رو اجرا کرد . توی قسمت دوم قرار شد تا راجع به بهینه سازی متد جستجو و پیاده سازی ساختار پیشنهاد هم توضیح بدم . اول از همه این ایده ممکنه پیش بیاد که قرار باشه روی چندتا از مدل هایی که داخل پروژه داریم و فیلد هایی که دارن جستجو کنیم پس مدل هایی رو که مدنظر داریم رو هم باید آماده کنیم . اگر مدل از قبل ساخته شده  باشه که هیچی اگر نه مدل رو مثل کد زیر به ساختار پروژه اضافه میکنیم : # models/

class Tag  &lt; ActiveRecord
 validates :name, presence: true
endمن برای محتوایی که توی مدل Content  داشتم میخواستم که Tag هایی هم تعریف کنم تا دسته بندی محتوا برام راحتر باشه پس مدل Tag رو ساختم . همونطور که میبینین مدل Tag  یک فیلد جدید داره که قرار روی اون جستجو بشه . دوباره مثل قبل شروع میکنیم تا مدلی رو که قرار روش کار کنیم رو آمادش کنیم . class Tag  &lt; ActiveRecord 
  include Searchable
  validates :name, presence: true
 
  settings index: { number_of_shards: 1 } do
    mappings dynamic: &#039;false&#039; do
      indexes :name
    end
  end
endحالا لازمه تا فایل Importer  رو هم که قبلتر نوشتیم رو آپدیت کنیم .# to change this line
[Content].each do |model_to_search|

# into this one
[Content, Tag].each do |model_to_search|فایل AutoCompleter  رو هم به فرم زیر تغییر میدیم .MODELS_TO_SEARCH = [Content, Tag].freezeو فیلدی رو که میخوایم روش جستجو کنیم هم به فیلد های قبلی اضافه میکنیم .# Here we should all the fields that will be part of the search  

&amp;quotfields&amp;quot: %w[body title type description name]و یه تغییری هم باید داخل فایل HintBuilder بدیم :# instead of directly calling KarateDojo
def call
  ContentResultBuilder.new(@record).autocomplete_hint
end

# we will use a method to call each one of the ResultBuilders
def call
  result_builder.autocomplete_hint
end

private

def result_builder
  &amp;quot#{@record.class}ResultBuilder&amp;quot.constantize.new(@record)
end

# and as a consequence we will need to create another result builder

class TagResultBuilder &lt; ResultBuilderBase
  def autocomplete_hint
    &amp;quot#{record.title}, #{record.body},#{record.type}, #{record.description},#{record.name}&amp;quot
  end
endخب تا این جا همه شرایط احتمالی رو پیش بینی کردیم و درصورتی که کلمه ای مثل Cancer  رو مثلا جستجو کنین نتیجه ای مثل نتیجه زیر میبینین :[
  {
    hint: &amp;quotCancer&amp;quot,
    record_type: &amp;quotContent&amp;quot,
    record_id: 1
  },
  {
    hint: &amp;quotCancer&amp;quot,
    record_type: &amp;quotTag&amp;quot,
    record_id: 1
  }
]تا اینجای کار همه چی اونطور که میخواستیم پیش رفت حالا اگر بخوایم تا موتور جستجوگر ما توانایی پیشنهاد رو هم داشته باشه باید دوباره تغییراتی توی کارهایی که کردیم بدیم، در ابتدا از فایل Searchable شروع میکنیم  :# models/concerns/searchable.rb 

# outside the included block
  ngram_filter = { type: &#039;nGram&#039;, min_gram: 2, max_gram: 20 }
  ngram_analyzer = {
      type: &#039;custom&#039;,
      tokenizer: &#039;standard&#039;,
      filter: %w[lowercase asciifolding ngram_filter]
  }
  whitespace_analyzer = {
      type: &#039;custom&#039;,
       tokenizer: &#039;whitespace&#039;,
       filter: %w[lowercase asciifolding]
  }

# inside the included block  

settings analysis: {
      filter: {
         ngram_filter: ngram_filter
       },
       analyzer: {
          ngram_analyzer: ngram_analyzer,
          whitespace_analyzer: whitespace_analyzer
       }
  }قبل از هرچیزی دررابطه با مفاهیم جدیدی که توی تیکه کد بالا نوشتم باید توضیحاتی بدم :ngram- filter : اختصاص حداقل و حداکثر کاراکتر‌ها برای جستجو در هر بار اجرای جستجوngram-analyzer : نحوه نشانه گذاری کاراکتر ها برای ngram-filter whitespace-analyzer : خرد کردن متن به بخش های کوچکتر هرموقع که به فضای سفید رسید .توی مرحله بعدی برای تکمیل کارمون لازمه که علاوه بر تغییراتی رو که توی فایل Searchable دادیم ، تغییراتی توی مدل یا مدلهایی که قرار روی اونها کار کنیم هم انجام بدیم : # models/Content.rb

settings index: { number_of_shards: 1 } do
    mappings dynamic: &#039;false&#039; do
          indexes :name, type: &#039;text&#039;, analyzer: &#039;ngram_analyzer&#039;,
                         search_analyzer: &#039;whitespace_analyzer&#039;
          indexes :title, type: &#039;text&#039;, analyzer: &#039;ngram_analyzer&#039;,
                         search_analyzer: &#039;whitespace_analyzer&#039;
          indexes :description, type: &#039;text&#039;,
                              analyzer: &#039;ngram_analyzer&#039;,
                               search_analyzer: &#039;whitespace_analyzer&#039;
          indexes :body, type: &#039;text&#039;, analyzer: &#039;ngram_analyzer&#039;,
                          search_analyzer: &#039;whitespace_analyzer&#039;
          indexes :type, type: &#039;text&#039;, analyzer: &#039;ngram_analyzer&#039;,                       
                           search_analyzer: &#039;whitespace_analyzer&#039;
    end
  endخب فکر میکنم تا اینجا برای این قسمت کافی باشه ، توی قسمت سوم راجع به نحوه تست کردن کارهایی که تا اینجا انجام دادیم توضیح میدم ، و در کنارش در رابطه با بعضی از مشکلاتی که ممکن درهنگام کار کردن با الستیک پیش بیاد و نحوه برطرف کردن اونها مینویسم . </description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Fri, 23 Oct 2020 21:33:56 +0330</pubDate>
            </item>
                    <item>
                <title>نحوه پیاده سازی elasticsearch در پروژه روبی آن ریلز - قسمت اول</title>
                <link>https://virgool.io/@hadivarposhti/%D9%86%D8%AD%D9%88%D9%87-%D9%BE%DB%8C%D8%A7%D8%AF%D9%87-%D8%B3%D8%A7%D8%B2%DB%8C-elasticsearch-%D8%AF%D8%B1-%D9%BE%D8%B1%D9%88%DA%98%D9%87-%D8%B1%D9%88%D8%A8%DB%8C-%D8%A2%D9%86-%D8%B1%DB%8C%D9%84%D8%B2-%D9%82%D8%B3%D9%85%D8%AA-%D8%A7%D9%88%D9%84-mn4ncskquyl0</link>
                <description>این هفته من درگیر تحویل پروژه ای بودم که با فریمورک ریلز اون رو توسعه میدن. یکی از نیازهایی که کارفرمای پروژه داشت این بود که بتونه داخل پروژش امکان جستجو هم داشته باشه . برای همین شروع کردم به خوندن و پیاده سازی این نوشته حاصل تجربه من از این پیاده سازییه .قبل از هرچیزی باید بگم که من داخل پروژم مدلی دارم به اسم Content که شامل محتوای متنی ، ویدیویی و صوتی، همه این محتواها فیلد های body ، title ،description و type  دارن ، کاربر قرار که بیاد و با نوشتن کلمه دلخواهش بتونه محتوای موردنظرش رو پیدا کنه . برای شروع اولین چیزی که لازم داریم استفاده از gem هاست . با یک بررسی ساده بین gem های موجود انتخاب من سه gem  زیر بودن :#Gemfile

gem &#039;elasticsearch-rails&#039;
gem &#039;elasticsearch-model&#039;

group :test do
  gem &#039;elasticsearch-extensions&#039;
endElasticsearch-rails : که شامل ویژگی های مختلفی برای اپلیکشین روبی آن ریلز  Elasticsearch-model : ابزاری برای ادغام الستیک سرچ با مدل های روبی آن ریلزElasticsearch-extentions : و این مورد هم برای تست استفاده میشه خب توی این مرحله باید دستور bundle install رو داخل ترمینال بزنیم تا gem هامون نصب بشن .توی این مرحله باید انتخاب کنین که برای کدوم یکی از مدل های داخل اپلیکیشن میخواین قابلیت سرچ رو قرار بدین از اونجایی که ممکن در آینده نیازهای مختلفی داشته باشین برای راحتی کار در پوشه Concern  داخل مدل ها فایلی رو به اسم searchable.rb  میسازیم که بعدا برای هر مدلی که لازم بود بتونیم ازش استفاده کنیم .# models/concerns/searchable.rb

require &amp;quotelasticsearch/model&amp;quot

module Searchable
  extend ActiveSupport::Concern

included do
    include Elasticsearch::Model
    after_commit :index_document, if: :persisted?
    after_commit on: [:destroy] do
      __elasticsearch__.delete_document
    end
  end

private

def index_document
    __elasticsearch__.index_document
  end
endدر توضیح اسنیپ کدی که بالا نوشتم باید بگم که دستور Elasticsearch::Model مدلی که درونش قرار سرویس سرچ داشته باشیم رو قادر میکنه تا از ‌متد های gem ما که قبلا اونها رو نصب کردیم استفاده کنه .قسمت after_commit این امکان رو میده تا هرگونه تغییری که شامل حذف یا اضافه و یا تغییر در محتوای قابل جستجوی ما داخل مدل رو به صورت خودکار index یا delete  در داخل الستیک سرچ هم انجام بده . در این مرحله باید concern  رو که نوشتیم به مدل مورد نظرمون اضافه کنیم . # /models/Content.rb

class Content &lt; ApplicationRecord
  include Searchable

# we are assuming this fields as string fields
  validates :body, :title, :description, :type, presence: true

settings index: { number_of_shards: 1 } do
    mappings dynamic: &#039;false&#039; do
      indexes :body
      indexes :title
      indexes :description 
      indexes :type 
    end
  end
end
تا اینجای کار فایل  searchable  رو که قبلتر نوشته بودیم رو به مدلمون اضافه کردیم ، مقادیرش رو validate  میکنیم و میرسیم به یه قسمت جدید Settings . این قسمت مثل فایل تنظیمات برای کار ما عمل میکنه و به الستیک میگه که چه فایل هایی رو باید index گذاری کنه . تا اینجا Elastic  رو داخل اپ ریلز پیاده کردیم حالا باید به این نکته فکر کنیم که ممکنه داخل دیتابیس داده ای باشه ، از اونجایی که Elastic  بصورت پیش فرض هیچ اطلاعاتی رو Index نمیکنه پس باید یک فایل Importer  بنویسیم که اطلاعات رو بصورت دستی داده های از قبل موجود رو داخل  Elastic وارد کنه .# poros/elasticsearch_data_importer.rb

module ElasticsearchDataImporter
    def self.import
      [Content].each do |model_to_search|
        model_to_search.__elasticsearch__.create_index!(force: true)

          model_to_search.find_in_batches do |records|
             bulk_index(records, model_to_search)
          end
      end
 end

def self.prepare_records(records)
      records.map do |record|
        {
          index: {
            _id: record.id,
            data: record.__elasticsearch__.as_indexed_json
          }
        }
      end
end

def self.bulk_index(records, model)
      model.__elasticsearch__.client.bulk(
          index: model.__elasticsearch__.index_name,
          type: model.__elasticsearch__.document_type,
          body: prepare_records(records)
      )
    end
end
توی تیکه کد بالا اول گفتم که به ازای مدلی که داریم محتوای داخل مدل رو Index  بکنه و در مرحله بعد هم تمام رکورد ها رو یکجا وارد دیتابیس الستیک بکنه . برای اجرای این کد باید دستور زیر رو داخل ترمینال وارد کنین . # You need to have ElasticSearch running on port 9200 to make this 
# work.
ElasticsearchDataImporter.importتا اینجا بیشتر کارهارو انجام دادیم و الان لازم که خود سرچ رو بنویسیم . قبل از اینکه بخوایم بریم سراغ Controller بنظرم خوبه که یه تستی از کاری که تا اینجا کردیم رو هم انجام بدیم .همونطور که قبل تر گفتم من داخل پروژم یک مدل دارم به اسم Content که فیلد‌های type ، body ، description و title داریم که قرار روی اونها جستجو بشه .خب با زدن دستور rails console وارد محیط کنسول ریلز میشیم تا یک رکورد جدید بسازیم و بتونیم روی اون تست کنیم . Content.create(title: &#039;covid-19&#039;, type: Article, body: &#039;covid-19 is a diseases&#039;, description:&#039;diseases is dangerous&#039;)از طریق دستور زیر داخل اطلاعاتی که الستیک اونها رو Index کرده جستجو میکنیم . # This should return how many records were found
Content.__elasticsearch__.search(&#039;covid-19&#039;).results.totalتا اینجا میتونیم مطمئن باشیم که همه چی درسته اما چیزی که واقعا بهش احتیاج داریم Controller  که بتونیم مدلی رو که قرار روش جستجو بشه رو تعریف کنیم .# controllers/content_controller.rb
class ContentController &lt; ApplicationController
  def search
    search_result = Elasticsearch::Model.search(params[:query].to_s, [Content] ).records.records
    render json: search_result, status: :found
    end
end

# routes.rb
resources :content, only: [] do
       get &#039;search&#039;, on: :collection
end
کنترلر Content جایی که میخواستیم کابر بتونه جستجو انجام بده پس متد search  رو براش تعریف کردیم و متد  Elasticsearch::Model.search قرار میدیم تا کاربر بتونه از این طریق جستجوی مورد نظرش رو بر روی اطلاعات داخل مدل انجام بده . تا اینجای کار همه چی به خوبی پیاده شده اما به این صورت که کابر باید دقیقا کلمه covid-19  کامل وارد کنه تا نتیجه درست رو ببینه اما اگر کابر کلمه رو درست وارد نکرد چی ؟برای حل این مشکل نیاز داریم تا جستجو رو بهینه کنیم  پس :# services/autocompleter.rb

class Autocompleter
   MODELS_TO_SEARCH = [Content].freeze
   attr_accessor :query  

def initialize(query)
     @query = query
end  

def self.call(query)
     new(query).call
end  

def call
     results.map do |result|
       {
          hint: build_hint(result),
          record_type: result.class.name,
          record_id: result.id
       }
    end
 end  

private  

def results
    Elasticsearch::Model.search(search_query, MODELS_TO_SEARCH).records
 end  

def build_hint(record)
     HintBuilder.call(record)
 end  

def search_query
    {
       &amp;quotsize&amp;quot: 50,
          &amp;quotquery&amp;quot: {
             &amp;quotfunction_score&amp;quot: { 
                 &amp;quotquery&amp;quot: {
                     &amp;quotbool&amp;quot: {
                         &amp;quotmust&amp;quot: [multi_match]
                       }
                   },
              &amp;quotfunctions&amp;quot: priorities
             }
          }
     }
 end  

def multi_match
     {
          &amp;quotmulti_match&amp;quot: {
               &amp;quotquery&amp;quot: @query,
              &amp;quotfields&amp;quot: %w[body title description body],
              &amp;quotfuzziness&amp;quot: &#039;auto&#039;
             }
        }
end 

 def priorities
      [
         {
           &amp;quotfilter&amp;quot: {
              &amp;quotterm&amp;quot: { &amp;quot_type&amp;quot: &#039;text&#039; }
            },
        &amp;quotweight&amp;quot: 5000
      }
    ]
     end
endبا کدی که بالا نوشتم نتیجه کار رو بهینه تر کردیم اما بعضی از مفاهیمی رو که استفاده کردم رو در زیر توضیح میدم : size : . تعداد نتایجی که در هربار جستجو  قرار نمایش داده بشه به کاربر function_score : به شما اجازه میده تا امتیاز محتوایی که بدست اومده رو مشخص کنین اینکار با کمک  وزن  که پایین تر راجع بهش میگم به بهبود نتایج جستجو در آینده کمک میکنه . multi-match : به شما اجازه میده در صورتی که بخواین داخل چنتا فیلد از مدلتون رو جستجو کنین به الستیک اعلام کنین که این فیلد ها رو برام ایندکس کن چرا که میخوام داخل اونها جستجو کنم .query : . مقداری که کاربر از ورودی میفرسته تا داخل الستیک جستجو انجام بشه fuzziness : وظیفه فازینس اینه که مشکلات نوشتاری  حل بکنه مقداری که براش تعریف کردم برای اینه که تا دو حرف رو بتونه بصورت پایه پوشش بده . functions : به ما کمک میکنه تا الویت هایی که برای این جستجو لازم داریم رو مشخص کنیم .HintBuilder : مسئول ساخت نتیجه جستجو در داخل فایل های ایندکس شدس که از طریق یه فایل دیگه به کاربر نمایش داده میشه .# services/hint_builder.rb 

class HintBuilder
  attr_accessor :record   

def initialize(record)
     @record = record
  end 

 def self.call(record)
       new(record).call
end 

     def call
        ContentResultBuilder.new(@record).autocomplete_hint
     end
end

# services/result_builder_base.rb

class ResultBuilderBase
    def initialize(record)
        @record = record
    end  

private  

   attr_reader :record
end

# services/Content_result_builder.rb

class ContentResultBuilder &lt; ResultBuilderBase
       def autocomplete_hint
            &amp;quot#{record.title}, #{record.body}, #{record.description}, #{record.type}&amp;quot
       end
endHintBuilder نتیجه جستجو رو با کمک  ContentResultBuilder  که از ResutBuilderBase ارث میبره و title ، body ،  description و type  رو بر میگردونه .  باید اضافه کنم که من اینجا صرفا نتیجه جستجو کامل نوشتم که بدونین میشه اضافه کرد و کاملا دلبخواه و باتوجه به نیازتون میتونین موارد رو کم یا زیاد کنین . به اینجا که رسیدیم باید دوباره متد سرچ رو که داخل کنترلر نوشتیم رو اپدیت کنیم به صورتی که پایین تر مینویسم : # controllers/content_controller.rb

def search
    search_result = Autocompleter.call(params[:query].to_s)
    render json: search_result, status: :found
endبا همه کارهایی که تا اینجا کردیم سرچ به خوبی پیاده سازی شد و جستجوگر ما قادر که غلط های تایپی رو هم تا دو حرف تشخیص بده . خب تا همین جا کافیه برای قسمت اول برای قسمت دوم راجع به بهینه نویسی متد سرچ و همچنین پیاده سازی ساختار پیشنهاد بصورت حداقلی داخل الستیک مینویسم .باید اضافه کنم که این ساختار صرفا نتیجه تجربه من از پیاده سازی و قطعا راه صدرصدی نیست ، ممکنه که شما بتونین تا اینجای راه رو هم بهینه تر بنویسین .</description>
                <category>Hadi Varposhti</category>
                <author>Hadi Varposhti</author>
                <pubDate>Sat, 17 Oct 2020 22:42:06 +0330</pubDate>
            </item>
            </channel>
</rss>