بسم الله الرحمن الرحیم
# اولین گام : ایجاد ایندکس
با دستور زیر ایندکس یا همان مخزن داده را ایجاد کنید :
```r
PUT movies
{
}
```
# ورود داده
به عنوان اولین گام در کار با الاستیک سرچ، باید چند داده وارد کنیم تا بتوانیم امکانات مختلف الاستیک سرچ را بررسی نمائیم. در پنل سمت چپ ، کدهای زیر را تایپ کرده، آنها دکمه سبز رنگ جلوی آنرا بزنید تا اجرا شود :
```r
PUT /movies/_doc/1
{
"title": "The Godfather",
"director": "Francis Ford Coppola",
"year": 1972
}
```
نتیجه زیر را مشاهده خواهید نمود :
## چند نکته در مورد دستور فوق و نتیجه آن :
* برای ورود داده در الاستیک لازم نیست قبل از آن، دیتابیسی ساخته شود یا تنظیمی صورت گیرد. کافیست با ذکر نام ایندکس یا همان دیتابیس، نام جدول یا گروه داده و نهایتاً یک شماره، از الاستیک بخواهید که داده های شما را ذخیره کند. اگر این ایندکس یا گروه داده درون آن ایندکس، وجود نداشت، به طور خودکار ساخته خواهد شد.
* داده ها باید به شکل جی سان باشند.
* هنگام ارسال دستور به الاستیک سرچ درون کیبانا آدرس سرور ضروری نیست اما در محیط هایی مانند PostMan باید آدرس به صورت کامل نوشته شود: http://localhost:9200/movies/movie/1
* بخش پرس و جوی کیبانا به صورت هوشمند، مثالهای موجود با curl و نیز آدرس کامل وب سرور الاستیک را اصلاح کرده و نسخه بدون ایرادی از کوئری را به شما تحویل می دهد. بنابراین با خیال جمع، کوئری های خود را از مستندات و آموزش های مختلف الاستیک درون این محیط کپی کرده، اجرا کنید.
* ذکر شماره شناسه هنگام درج یک داده جدید، ضروریست .
* در نوشتن دستورات، کیبانا به شما کمک خواهد کرد. کافیست ابتدای یک ایندکس یا یک گروه یا یک دستور را بنویسید تا برایتان پیشنهاد مناسب نمایش داده شود و یا کلید CTRL+SPACE را بزنید تا به شما پیشنهاد مناسب را نشان دهد.
* اگر خطایی در دستور اجرایی وجود داشته باشد، مثلاً شناسه را ذکر نکرده باشید، خطا به صورت کد HTTP و یک توضیح معمولاً مناسب ، برگشت داده می شود.
* سمت راست که نتیجه اجرای یک کوئری است همان جی سانی است که سرور به عنوان خروجی به ما برمی گرداند.برخی از این فیلد ها در زیر مشخص شده اند :
قبل از اینکه به سراغ جستجو و ارسال کوئری برویم ، دوباره دکمه سبز رنگ اجرایی را بزنید. یعنی همین دستور را دوباره اجرا کنید. بدون هیچ خطایی اجرا می شود و همان نتایج قبلی نمایش داده می شود با دو تغییر مهم : اول اینکه نسخه رکورد، به شماره ۲ تغییر می یابد و دوم اینکه نوع عملیات می شود به روزرسانی . اگر فیلد جدیدی به داده ها اضافه کنید و یا یکی از خصوصیات داده را تغییر دهید و درخواست را با دکمه سبزرنگ اجرا، به سرور ارسال کنید، یک نسخه جدید از داده ها ایجاد و ذخیره خواهد شد. می توانید هنگام تعیین کنید که نسخه های مختلف یک رکورد نگهداری شود یا نه .
حذف Type در نسخههای جدید الستیک
در نسخه های جدید الستیک سرچ، نیاز به ذکر نام جدول یا تایپ نیست و بهتر است به جای دستور فوق شکل زیر نوشته شود که در آن از اندپوینت _doc استفاده شده است. در ادامه مقاله ،بهتر است از این سبک نگارش استفاده کنید.
حذف تایپ در الستیک سرچ و جایگزین شدن آن با اندپوینت _doc
نکته : برای به روزرسانی و ایجاد رکورد از هر دو دستور PUT و POST می توانید استفاده کنید اما بهتر است برای ایجاد از PUT و برای به روزرسانی از POST استفاده کنید. در حالت کلی،اگر آی دی و شناسه یک رکورد مشخص باشد، از PUT استفاده کنید (هم برای ایجاد و هم به روز رسانی ) و اگر می خواهید شناسه را خود الستیک سرچ اختصاص دهد از پست به صورت زیر استفاده کنید :
```r
POST /movies/_doc
{
"title": "The Godfather",
"director": "Francis Ford Coppola",
"year": 1972
}
```
در این صورت، یک ID به صورت خودکار به این سند افزوده میشود اما این عمل تکرار پذیر نیست یعنی به ازای هر بار اجرای آن، سند جدیدی در دیتابیس ذخیره میشود بنابراین تنها زمانی از این حالت استفاده کنید که مطمئن باشید نیاز به تکرار عملیات نخواهید داشت.
قبل از ادامه آموزش، مقادیر زیر را هم وارد کنید تا برای جستجو، تعدادی داده داشته باشیم (منبع داده ها) :
نکته : می توانید همه دستورات را کپی و در پنل دستورات، بچسبانید اما هر دستور را جداگانه باید اجرا کنید. بنابراین نیاز به پاک کردن هر دستور و نوشتن دستور بعدی نیست و در ادامه لیست دستورات می توانید کار با الاستیک را ادامه دهید.
```r
PUT movies/_doc/2
{
"title": "Lawrence of Arabia",
"director": "David Lean",
"year": 1962,
"genres": ["Adventure", "Biography", "Drama"]
}
PUT movies/_doc/3
{
"title": "To Kill a Mockingbird",
"director": "Robert Mulligan",
"year": 1962,
"genres": ["Crime", "Drama", "Mystery"]
}
PUT movies/_doc/4
{
"title": "Apocalypse Now",
"director": "Francis Ford Coppola",
"year": 1979,
"genres": ["Drama", "War"]
}
PUT movies/_doc/5
{
"title": "Kill Bill: Vol. 1",
"director": "Quentin Tarantino",
"year": 2003,
"genres": ["Action", "Crime", "Thriller"]
}
PUT movies/_doc/6
{
"title": "The Assassination of Jesse James by the Coward Robert Ford",
"director": "Andrew Dominik",
"year": 2007,
"genres": ["Biography", "Crime", "Drama"]
}
```
# جستجو و بازیابی اطلاعات
هدف از این آموزش، کار با کیبانا است اما بهتر است چند کوئری ساده از الاستیک را هم با هم مرور کنیم. ابتدا می خواهیم اطلاعات یک فیلم خاص با شناسه مشخص را جستجو کنیم. از دستور زیر بهره می بریم :
GET /movies/_doc/1
نتیجه جستجوی یک شناسه خاص به صورت زیر نمایش داده می شود. ابتدا نام ایندکس ، نام گروه ، شناسه درخواستی ، نتیجه جستجو و در صورت پیدا کردن رکورد مورد نظر ، نتیجه به صورت پیش فرض درون فیلد source_ قرار می گیرد :
جستجوی یک شناسه خاص در الاستیک سرچ
برای جستجوی اطلاعات معمولاً از ساختار زیر استفاده می کنیم :
```r
POST movies/_search
{
"query"{
//Type your query DSL here
}
}
```
در جلوی عبارت query به زبان DSL (Domain Specific Language) که زبان توصیف پرس و جوهای الاستیک سرچ است، به ورود مشخصات داد های مورد نظر خود می پردازیم.
POST or GET
برای انجام جستجو، متد پیشنهادی برای ارسال دستور به الستیک سرچ، متد GET است یعنی در دستور فوق به جای POST از GET استفاده کنید. با توجه به اینکه ممکن است برخی نرم افزارها یا کتابخانههای قدیمی ارسال ریکوئست، از دستور GET با داشتن BODY پشتیبانی نکنند، متد POST هم می تواند برای این منظور استفاده شود اما در حالت کلی، سعی کنید جستجوها را با GET ارسال نمایید. در ادامه مقاله، از هر دو روش استفاده شده است.
در ادامه چند پرس و جوی رایج را در الاستیک سرچ با هم بررسی می کنیم.
# جستجو به کمک query_string
یکی از ساده ترین راه ها برای جستجو در الاستیک بخصوص زمانی که جستجوی متن برای ما اهمینت دارد، استفاده از پرس و جوی query_string است. این نوع از پرس و جو، بسیار انعطاف پذیر بوده و به کمک تنها یک رشته، تمام نیازهای جستجوی ما را به راحتی برآورده می کند. کافیست درون فیلد query از این پرس و جو، کلمه یا عبارت مورد نظر خود برای یافتن را وارد کنیم و همزمان، شرطها و فیلترهای خود را هم درون رشته جستجو، به الاستیک ارسال نمائیم.
با یک مثال ساده شروع می کنیم. می خواهیم هر فیلمی که در آن کلمه kill به کار رفته باشد (در هر فیلدی) را بیابیم . ذستور زیر را اجرا می کنیم :
```r
POST movies/_doc/_search
{
"query": {
"query_string": {
"query": "kill"
}
}
}
```
این پرس و جو کلمه kill را در تمامی فیلدهای گروه movie جستجو نموده و به هر رکورد یک امتیاز اختصاص می دهد و نهایتاً طبق آن، به ترتیب، نتایج را به ما نشان خواهد داد. پنجره نتیجه شما باید حاوی داده های زیر باشد که فیلدهای اصلی آن در خود شکل توضیح داده شده است :
تحلیل نتایج الاستیک سرچ
## چند نکته در باره کوئری فوق :
* در انتهای جستجوها و کوئری ها از پایانگر _search استفاده می کنیم. پایانگرها یا EndPoint ها، مشخص کننده نوع درخواست ارسالی به الاستیک هستند.
* اگر بخواهیم در تمامی گروه های ایندکس moviesجستجو کنیم، نام گروه را باید حذف کنیم و اگر بخواهیم در تمامی ایندکس ها (همه دیتابیس ها) جستجو کنیم، نام ایندکس را هم حذف می کنیم و به جای آن از _all استفاده میکنیم. در این حالت دستور اصلی کوئری ما به این شکل خواهد بود: POST _all/_search
* در صورت اجرای چند باره کوئری فوق، زمان پاسخگویی کاهش می یابد که نشاندهنده این نکته است که کوئری ها در حافظه کش می شوند و اجرای مجدد همان کوئری بسیار سریعتر از اجرای اولیه آن خواهد بود.
اگر بخواهیم جستجوی خود را به یک فیلد خاص محدود کنیم مثلاً فقط در عنوان فیلم ها جستجو کند، از ساختار زیر استفاده می کنیم :
```r
GET /movies/_search
{
"query": {
"query_string": {
"query": "ford",
"fields": ["title"]
}
}
}
```
با اجرای دستور فوق که جستجوی کلمه ford است و این کلمه هم در فیلد کارگردان و هم در عنوان فیلم آمده است، تنها یک فیلم ظاهر خواهد شد که با شرایط تعیین شده کاملاً مطابق است. می توانید در لیست [“title”] بقیه فیلدهای مد نظر خود را هم وارد نمایید که با ویرگول از هم جدا شده اند.
مثال فوق را به راحتی می توانیم به صورت زیر هم بازنویسی کنیم :
```r
"query_string" : {"query" : "title:ford"}
```
تمام فیلمهایی که در نام آنها kill یا ford به کار رفته است : (AND و OR و TO و سایر عملگرها با حروف بزرگ نوشته می شوند)
```r
"query_string" : {"query" : "title:ford OR kill"}
```
تمام فیلم هایی که عنوان kill داشته و کارگردان آنها Tarantino است :
```r
"query_string" : { "query": "(title:kill) AND (director:Tarantino)" }
```
تمام فیلم های ژانر درام در سال ۱۹۶۲ :
```r
"query_string" : { "query":"(genres:Drama) AND (year:1962 )" }
```
تمام فیلم های ژانر درام تا سال ۲۰۰۵ (می توانید به جای * ابتدای بازه را قرار دهید) :
```r
"query_string" : { "query":"(genres:Drama) AND (year:[* TO 2005] )" }
```
تمام فیلم های درام یا فیلم هایی که در نام آنها کلمه kill به کار رفته باشد :
```r
"query_string" : {"query": "(genres:Drama) OR (title:kill)" }
```
اگر بخواهیم در مثال بالا، به عناوین حاوی kill اولویت بیشتری بدهیم از عملگر ^ که عملگر Boost نامیده می شود استفاده می کنیم. با اینکار در محاسبه امتیاز نهایی هر رکورد و اولویت نمایش دادن آن، امتیاز عنوان را بیشتر در نظر می گیرد و فیلمی بیشترین امتیاز را خواهد داشت که هم ژانر درام باشد و هم در عنوان آن kill به کار رفته باشد :
```r
"query_string" : {"query": "(genres:Drama) OR (title:kill^2)" }
```
برای مشاهده مثالهای بیشتر به مستندات اصلی الاستیک سرچ در خصوص Query_Srting مراجعه نمایید.
ساخت یک درخت جستجو
روش دیگر برای ساخت یک پرس و جو، ایجاد درخت جستجو به صورت مرحله به مرحله و فیلد به فیلد است. در این حالت معمولاً خود جستجو روی یک داده خاص را با عبارت match که برابری یک فیلد را با یک مقدار بررسی میکند و اعمال فیلتر روی رکوردها را قبل از بررسی و جستجو، با عبارت filter انجام میدهیم.
دستور زیر تمام فیلم های ژانر درام را بر می گرداند :
```r
GET /movies/_search
{
"query": {
"match": {
"genres": "drama"
}
}
}
```
دستور فوق تمامی فیلمها را بررسی کرده و فیلد genres آنها را با کلمه Drama مقایسه می کند. این دستور در حالت کلی تر به صورت زیر نوشته میشود که امکان افزودن تنظیماتی را به هر فیلد به صورت جزییتر به ما میدهد :
```r
POST /movies/_search
{
"query": {
"match": {
"genres": {
"query": "drama"
}
}
}
}
```
عبارت جلوی match، آنالیز شده و سپس جستجو می شود یعنی تک تک کلمات آن جدا شده، ایست واژه ها (مانند از، به، که و …) حذف و سپس کلمات با همدیگر OR می شوند یعنی هر کدام از آنها مطابقت داشت، فیلم مورد نظر جزء خروجی قرار خواهد گرفت. برای تغییر این روال و تعیین حداقل تعداد کلمات مطابقت داده شده و یا AND کردن آنها به مستندات match مراجعه کنید.
مثال زیر، عملگر را هم هنگام جستجو برای لغات داخل کوئری مشخص میکند :
```r
POST movies/_search
{
"query": {
"match": {
"title": {
"query": "Kill Bill",
"operator": "or"
}
}
}
}
```
اگر بخواهیم همزمان بر روی دو فیلد یا بیشتر جستجو انجام دهیم، می توانیم از عبارت multi_match به صورت زیر استفاده کنیم :
```r
POST /movies/_search
{
"query": {
"multi_match": {
"query": "ford",
"fields": [
"title^3",
"director"
]
}
}
}
```
در دستور فوق اولویت عنوان فیلم را سه برابر نام کارگردان، تعیین کرده ایم.
روش دیگر برای انجام کوئری فوق، ترکیب چند عبارت match با عمگرهای OR و AND است که در کوئری زیر که یک کوئری Boolean با کلید واژه مهم bool، نشان داده شده است :
```r
POST /movies/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"genres": "drama"
}
},
{
"match": {
"title": {
"query":"kill",
"boost": 3
}
}
}
]
}
}
}
```
دقت کنید که در دستور فوق، تمامی رکوردهای جدول فیلم بررسی می شود و دنبال فیلم های درام با عنوانی شامل kill می گردد.
نکته مهم : در عبارات منطقی و ساخت جستجوهای پیشرفته، must را معادل AND ، عبارت should را معادل OR و must_not را معادل NOT در نظر بگیرید. با این توصیف دستور زیر تمامی فیلم هایی که در
( عنوان آنها کلمه Ford یا Kill به کار رفته است ) و ( ژانر آنها اسرارآمیز نیست)
را بر می گرداند.
نکته دیگر اینکه درون یک عبارت bool هر کدام از عبارت های must، must_not و should تنها یک بار باید ظاهر شود.
```r
GET /movies/_search
{
"query": {
"bool": {
"must": {
"bool": {
"should": [
{
"match": {
"title": "ford"
}
},
{
"match": {
"title": "kill"
}
}
]
}
},
"must_not": {
"match": {
"genres": "Mystery"
}
}
}
}
}
```
جستجوی فیلدهای غیرمتنی
برای جستجوی فیلدهای غیر متنی از نوع کوئری Term استفاده میکنیم .
```r
GET /movies/_search
{
"query": {
"term": {
"year": {
"value": "2003",
"boost": 1.0
}
}
}
}
```
البته اگر دقیقا به دنبال یک عبارت یا کلمه باشیم، میتوانیم از کوئریهای Term برای فیلدهای متنی هم استفاده کنیم. مثلا نام یک شهر، میتواند با این نوع کوئری ، جستجو شود.
اعمال فیلتر در جستجو
بسیاری از اوقات می خواهیم، رکوردها و داده های جستجو شونده را محدود کنیم مثلاً دنبال فیلم های سال ۱۹۷۲ هستیم که فلان کارگردان نوشته است. در این حالت، علاوه بر اینکه باید پرس و جو را به نام کارگردان محدود کنیم و در بالا به آن اشاره شده است، گام دیگری که باید برداریم، این است که تنها در داده های این سال جستجو را آغاز کنیم. فیلترینگ، سرعت جستجو را بسیار بالا می برد چون تعداد سندهای لازم برای جستجو را کاهش می دهد. اعمال محدودیت روی رکوردهای مورد جستجو را فیلترینگ می نامیم و به صورت زیر آنرا اعمال می کنیم :
```r
GET /movies/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "kill"
}
}
],
"filter": {
"bool": {
"must": [
{
"range": {
"year": {
"gte": 1960
}
}
},
{
"term": {
"genres": {
"value": "drama"
}
}
}
]
}
}
}
}
}
```
دستور فوق ابتدا تمام فیلم های تولید شده بعد از سال ۱۹۶۰ و از ژانر درام را فیلتر کرده ، سپس به دنبال فیلمهایی که عنوان آنها شامل kill باشد، می گردد.
میتوانیم کوئری فوق را به صورت خلاصه شده زیر هم بنویسیم یعنی عبارت filter را حذف کنیم اما در حالت کلی، جستجوهای ما در الستیک سرچ از دو بخش اصلی تشکیل شده اند : بخش متنی و بخش غیرمتنی که فیلتر ما را تشکیل میدهد و ابتدا دادهها بر اساس این فیلتر، انتخاب شده و سپس فرآیند بازیابی متن و امتیازدهی به نتایج انجام میشود.
```r
GET /movies/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"title": "kill"
}
},
{
"range": {
"year": {
"gte": 1960
}
}
},
{
"term": {
"genres": {
"value": "drama"
}
}
}
]
}
}
}
```
در مثال فوق، اگر بخواهیم تنها بر اساس ژانر و سال یعنی بخش داخل فیلتر ، داده ها را جستجو کنیم و جستجوی متن، انجام ندهیم، دو راه داریم : اول اینکه داخل بخش must که همان پرس و جوی اصلی ما بعد از اعمال فیلتر است، اعلام کنیم که همه داده ها را برگرداند :
```r
"must": [
{
"match_all": {}
}
]
```
و یا اینکه کلا بخش پرس و جو را حذف کنیم و جستجوی ما بر اساس مقادیر فیلدها مانند سال و رشته و ژانر و … باشد.
```r
GET /movies/_search
{
"query": {
"bool": {
"must": [
{
"range": {
"year": {
"gte": 2000
}
}
},
{
"term": {
"genres": {
"value": "drama"
}
}
}
]
}
}
}
```
دقت کنید که در بخش فیلتر ما معمولاً از عبارت term یا terms برای بررسی برابری یک فیلد با یک یا چند مقدار استفاده می کنیم. دقت کنید که برای فیلدهای متنی در صورتی از term استفاده کنید که یا تنها یک کلمه باشد و یا نوع آن فیلد را keyword تعیین کنید که در ادامه توضیح داده خواهد شد.
چند دستور کاربردی
اگر بخواهیم غلطهای املایی در کلمات مورد جستجو را هم الاستیک بخواهیم در نظر بگیرد و یک جستجوی فازی (تقریبی) به ما برگرداند از عبارت “fuzziness”: “AUTO” مشابه با کوئری زیر که در آن فیلم پدرخوانده به اشتباه به صورت godfater آمده است، بهره می بریم :
```r
GET /movies/_search
{
"query": {
"match": {
"title": {
"query": "godfater",
"fuzziness": "AUTO"
}
}
}
}
```
خیلی از اوقات به دنبال یک عبارت هستیم مثلاً می خواهیم فیلم های kill bill را دقیقاً به همین شکل بیابیم. کوئری زیر به الاستیک اعلام می کند که به دنبال یک عبارت هستیم و باید دقیقاً کلمات ذکر شده پشت سرهم ظاهر شده باشند و نهایتاً سه فضای خالی بین هر کلمه تا بعدی را می توانیم قبول کنیم :
```r
GET /movies/_search
{
"query": {
"match_phrase": {
"title": {
"query": "Kill Bill",
"slop": 2
}
}
}
}
```
## یک اشکال رایج
اگر شما به دنبال جستجوی کارگردانی با نام Francis Ford Coppola باشید و آنرا به صورت زیر جستجو کنید، علیرغم اینکه این مقدار در دیتابیس شما وجود دارد، اما جوابی به شما برگشت نخواهد کرد.
```r
GET /movies/_search
{
"query": {
"term": {
"director": {
"value": "Francis Ford Coppola"
}
}
}
}
```
دلیل این موضوع هم این است که الاستیک سرچ به صورت پیش فرض، متن ها را آنالیز و پردازش کرده، آنها را به کلمات تشکیل دهنده شان تجزیه و تک تک کلمات را جداگانه شاخص زنی می کند. اگر می خواهید کل متن را یکجا ایندکس کنید، باید نوع آنرا keyword قرار دهید. با انجام اینکار دیگر روی تک تک کلمات آن فیلد، امکان جستجو نخواهید داشت که این هم خود مشکلی دیگر است.
راه حل الاستیک برای حل این مشکل، تعیین فیلدهای چندمقداره است که علاوه بر مقدار اصلی آنها، می توان نوع دومی را هم به با نام دیگری به آنها اضافه کرد تا هر یک جداگانه آنالیز و ایندکس شوند. دستور زیر یک نام دوم original با نوع keyword برای کارگردان تعیین می کند. (به پایانگر mapping در انتهای دستور اصلی توجه کنید )
```r
PUT /movies/_mapping
{
"movie": {
"properties": {
"director": {
"type": "text",
"fields": {
"original": {
"type": "keyword"
}
}
}
}
}
}
```
البته برای اعمال این تغییرات احتمالا نیاز به عملیات reindex خواهید داشت (انتقال داده ها به یک جدول جدید) و یا ابتدا این دستور را برای یک جدول جدید اجرا کرده و سپس عملیات ورود داده را از ابتدا روی آن انجام دهید . روش ساده تری برای حل این مشکل در ادامه ،بیان شده است که می توانید از آن استفاده کنید.
حال کافیست اگر بخواهیم نام کامل کارگردان را جستجو کنیم، به صورت زیر عمل نماییم :
```r
GET /movies/_search
{
"query": {
"term": {
"director.original": "Francis Ford Coppola"
}
}
}
```
راه ساده تر این کار استفاده از field.keyword بود که برای همه فیلدها در دسترس است و نیاز به استفاده از فیلدهای چندمقداره برای کارگردان هم نبود. یعنی در مثال فوق کافی بود به جای "director":"Francis Ford Coppola" و بدون تغییر در نوع داده، عبارت "director.keyword":"Francis Ford Coppola" را بنویسیم .
برای مشاهده و مرور دقیق تر این دستورات و نیز آشنایی اولیه با چند دستور کاربردی دیگر از الاستیک سرچ می توانید از این مقاله آموزشی هم استفاده کنید.
در تهیه این آموزش از مثالهای این سایت استفاده شده است.
```r
GET movies/_search
{
"query": {
"term": {
"director.keyword": "Francis Ford Coppola"
}
}
}
```
پی نوشت :
در نسخه ۶٫۳٫۰ الاستیک، قابلیت نوشتن کوئری به زبان SQL هم به الاستیک سرچ افزوده شده است که برای علاقهمندان این موتور جستجو، میتواند اطمینان خاطری باشد در پاسخگویی سریع به نیازهای جستجوی روزانه .
صلوات