پایگاه داده Neo4j میشه اینطوری تعریف کرد که،
"نوعی پایگاه داده است که از طریق عملیات کراد، Create,Read,Update و Delete داده ها را بصورت گراف ذخیره میکند."
پایگاه داده گراف که بهش پایگاه داده گراف گرا هم گفته میشه، نوعی از پایگاه داده از جنس Nosql که از تئوری گراف برای ذخیره، مپ کردن و کوئری نوشتن ازش استفاده میشه.پایگاه داده گراف اساسا مجوعه از node ها و edge هاست.
هر گراف ترکیبی از دو المان: node (یا vertex) و relationship (یا edge) ساخته میشه. هر نود بیانگر یک موجودیت (شخص، مکان، اشیا ...) و هر ایج بیانگر نحوه ارتباط بین دو نود.هدف اصلی توی این ساختار اجازه دادن به کاربر که هرمدل سناریوی رو بتونه مدل کنه.از یک سیستم جاده ای، شبکه ای از ابزارها، تاریخچه پزشکی بیماران و یا هرچیزی که بوسیله روابط (ایج) قابل ارائه باشه.
برخلاف پایگاه داده های رابطه ای (RDBMS) پایگاه داده گراف بیس، با روابط بین جداول هم مثل یه شهروند درجه یک رفتار میکنه، به همین دلیل نیازی به استفاده از رویکردهای پیچیده تری مثل Join کوئری ها یا استفاده از کلید های خارجی برای پیدا کردن داده های مرتبط نیست. از اونجایی که به محض اینکه متوجه بشیم دوتا موجودیت باهم مرتبط هستن بهم وصلشون میکنیم پس نیاز به Join کوئری ضروری نیست اصلا، و از اونجایی که پایگاه داده های گراف گرا توی هسته خودشون از تفکر شی گرا استفاده می کنن، مدل داده ای که برای اونها ترسیم میکنین همون مدل داده ای که توی پایگاه داده های معمولی طراحی میشه.
مدل کردن دادها بخش اساسی از هر پروژه ای چرا که روش رسیدن به دادها و فراخوانی اونها رو معین میکنه و چون اغلب ایجاد تغییرات بزرگ ممکن امکان پذیر نباشد، مطمئناً آسون نیست، و این روند را مهم تر می کند.
پروسه مدل کردن داده ها در دیتابیس های گراف بیس (و خصوصا Neo4j ) اغلب شامل یک تصمیم مهم و اون هم اینکه کی باید یک داده رو (data point) رو یک نود در نظر گرفت و کی باید یک ایج، و اغلب یک طراحی هوشمندانست که توی کوئری نوشتن های بعدی بسیار کمک میکنه.
پایگاه داده Neo4j پروسه مدل سازی دادها رو بصورت ساختن یک وایتبرد مطرح میکنه، این به این معنی که نود ها و ایج هایی که می تونیم روی تخته سفید یا به عنوان جملات انگلیسی ایجاد کنیم، اغلب نحوه مدل سازی داده ها را مشخص می کنن.
اغلب دادههایی که داریم را میشه به عنوان یک ویژگی یا یه نود به خودی خود نشون داد. به عنوان مثال در مورد داده های تراکنش های ایمیل، می توانیم داده ها رو به این صورت نشون بدیم.
اگرچه که این مدل واضحی اما اگر بخوایم اون رو گسترش بدیم تا اطلاعات دیگه ای مثل CC، BCC ... این امکان برامون وجود نداره پس مدلمون رو به صورت زیر تغییرش میدیم.
مثال دیگه ای که میشه زد میشه به ارتباط بین شهر و شرکت اشاره کرد.
ما میتونیم مقدار شهر رو به نود شرکت بصورت مستقیم وصل کنیم. به تصویر زیر توجه کنین.
برای کوئری زدن بر روی چنین داده هایی که نیاز به فیلتر کردن بر اساس یک مکان داره، باید تمام نودهای یک شرکت در نمودار رو پیمایش کنین و بعدش بر اساس مقدار شهر اونهارو فیلتر کنین.همونطور که توی تصویر بالا مشخص، از آنجایی که تعداد نود های شرکت به طور بالقوه می تونند بسیار زیاد باشن، کوئری برای همه شرکت ها در یک شهر خاص، از نظر زمان و مصرف CPU، منابع زیادی را می طلبه.
برای اینکار میتونیم نمودار بالا رو بصورت زیر تغییرش بدیم.
حالا میشه کوئری مون رو تغییر داد تا ابتدا شهر ها راو فیلتر کنه و فقط به دنبال شرکت هایی بگرده که به نود اون شهر متصل هستن. این زمان محاسبه کوئری ما را به طور قابل توجهی بهبود می ده.
کلمه کلیدی 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]->(y) FOREACH(month IN months | CREATE (m:Month {value: month}) MERGE (y)-[:CHILD]->(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 <> 0 THEN range(1,28) WHEN year % 100 <> 0 THEN range(1,29) WHEN year % 400 <> 0 THEN range(1,29) ELSE range(1,28) END ELSE range(1,30) END) | CREATE (d:Day {value: day}) MERGE (m)-[:CHILD]->(d)))) WITH * //Create years linked list MATCH (root:TimeTreeRoot)-->(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]->(first) MERGE (root)-[:LAST]->(last) FOREACH(i in RANGE(0, size(years)-2) | FOREACH(year1 in [years[i]] | FOREACH(year2 in [years[i+1]] | MERGE (year1)-[:NEXT]->(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]->(first) MERGE (year)-[:LAST]->(last) WITH * MATCH (year:Year)-[:CHILD]->(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]->(month2)))) WITH * //Create days linked list MATCH (month:Month) WITH collect(month) as months UNWIND months AS month MATCH (month)-[:CHILD]->(day:Day) WITH month, collect(day) AS days WITH month, days[0] AS first, days[size(days)-1] AS last MERGE (month)-[:FIRST]->(first) MERGE (month)-[:LAST]->(last) WITH * MATCH (year:Year)-[:CHILD]->(month:Month)-[:CHILD]->(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]->(day2)))) ;
توی این نوشته سعی کردم تا بصورت ساده با دیتابیس Neo4j آشنا بشیم و چند مورد از ملاحظاتی که در زمان طراحی پایگاه داده گراف بیس باید بهش توجه بشه بگم.باید اضافه کنم که لزوما تمامی موارد بالا کاربردی نیستن این نیازشماست در هنگام طراحی که مشخص میکنه چه کاری باید و چه کاری نباید انجام بشه.
پی نوشت: طبق معمول همه نوشته ها، سعی کردم که نوشتم با زبان محاوره و ساده گفته بشه که برای همه قابل درک باشه. اگه مورد و مشکلی وجود داره خوشحال میشم که برام بنویسین تا اون رو اصلاح کنم حتما.
شاد باشین (: