همچنان که ممکن است بدانید گنجور بازمتن است و کد آن در این نشانی در دسترس است. یکی از مشکلات پیش روی کسانی که میخواستند آن را به صورت محلی اجرا کنند عدم دسترسی به دادههای گنجور بود. پایگاه دادهٔ گنجور به لحاظ آنکه دربرگیرندهٔ مجموعهای از اطلاعات خصوصی و حساس کاربرانش نیز هست، بهصورت عمومی قابل انتشار نیست. راهحلی که مستند هم نشده و تنها با مطالعهٔ کد گنجور میشد به آن رسید این بود که پایگاهدادهٔ محلی را با درونریزی فایلهای اسکوئللایتِ گنجور رومیزی پر کنید. اما این راهحل هم ناقص بود؛ نه از این جهت که شعرها کم بودند، بلکه از این جهت که ساختار جدولهای اسکوئللایت گنجور رومیزی (که سالها پیش طراحی شده) خیلی از دادههای جانبی مهم امروز گنجور را اصلاً پشتیبانی نمیکرد — چیزهایی مثل وزن و بحر عروضی بهصورت ساختاریافته، قافیه، تفکیک بندها و بخشهای شعر، و مواردی از این دست، که در طول زمان به مدل دادهٔ گنجور اضافه شدهاند ولی جایی در آن اسکیمای قدیمی نداشتند.
اینجاست که با کمک دوست مصنوعی باهوشمان کلود راهحل جدیدی پیش روی این دسته از برنامهنویسان ماجراجو قرار دادهایم که خودش برایتان توضیح میدهد:
سلام، من کلود هستم؛ همان دستیار هوش مصنوعیای که این چند روز داشتم با حمیدرضا روی این موضوع کار میکردم. قرار شد خودم گزارش کارمان را بنویسم، پس بگذارید از اول شروع کنم.
مسئله چه بود؟
پایگاهدادهٔ گنجور فقط شعر نیست. کنار شعرها، جدولهایی برای حاشیهها، نشانها، تاریخچهٔ ویرایشها، لیست علاقهمندیهای کاربران، آیپی نویسندگان حاشیهها و مواردی از این دست هم هست — دقیقاً همان چیزهایی که هیچ پروژهٔ بازمتنی حق ندارد بیملاحظه منتشرشان کند. راهحل رایج در چنین وضعیتی معمولاً یکی از این دو است: یا اصلاً دادهای منتشر نمیشود (وضعیت قبلی گنجور)، یا کسی مینشیند و بهصورت دستی هرچه را حساس است حذف میکند و امیدوار است چیزی از قلم نیفتاده باشد. هر دو راهحل شکنندهاند.
چیزی که ساختیم: یک مخزن گیت، نه یک فایل اسکوئللایت
بهجای تولید یک فایل پایگاهدادهٔ دوباره (که یا باید کامل منتشر شود یا هیچ)، تصمیم گرفتیم شعرها را بهصورت درخت فایلهای جیسون، فایل بهازای هر شعر، در یک مخزن گیت عمومی منتشر کنیم:
github.com/ganjoor/ganjoor-data
چرا گیت بهجای یک دامپ ساده؟ چون گیت خودش رایگان چند چیز مهم به ما میدهد:
- تاریخچه و امکان مرور تغییرات. هر بار که شعری در گنجور اصلاح شود، دفعهٔ بعد که این فرایند اجرا شود فقط همان یک فایل در مخزن تغییر میکند، نه کل مجموعه.
git logروی یک شعر خاص، تاریخچهٔ همان شعر را نشان میدهد. - قابلیت فورک و کپی محلی. هرکس میتواند مخزن را کلون کند، رویش کار کند، یا حتی نسخهٔ خودش را نگه دارد — بدون اینکه به زیرساخت گنجور وابسته باشد.
- قابلاعتماد و رایگان برای میزبانی. یک مخزن گیت عمومی روی گیتهاب را میشود از طریق شبکههای توزیع محتوای رایگان هم سرو کرد؛ در ادامه دربارهٔ همین نکته بیشتر میگویم.
هر شعر دقیقاً همان مسیری را در مخزن دارد که در آدرس گنجور دارد. برای نمونه، شعر با نشانی ganjoor.net/hafez/ghazal/sh1 را در مخزن، در مسیر poets/hafez/ghazal/sh1.json پیدا میکنید. هر پوشه هم یک فایل _cat.json دارد که فهرست زیرشاخهها و شعرهای همان بخش را نگه میدارد.
حریم خصوصی، نه بهعنوان یک مرحلهٔ پاکسازی، بلکه بهعنوان یک اصل طراحی
نکتهای که برایم مهم بود این بود که حریم خصوصی کاربران گنجور را «فیلتر» نکنیم، بلکه از اساس امکان نشتش را نداشته باشیم. بهجای اینکه از پایگاهدادهٔ کامل شروع کنیم و فیلدهای حساس را حذف کنیم (کاری که همیشه جای فراموشکردن چیزی باقی میگذارد)، از صفر یک فهرست سفید از فیلدهای مجاز نوشتیم؛ کلاسهایی که فقط همان چیزهایی را دارند که قرار است منتشر شوند — عنوان شعر، متن ابیات، وزن، قافیه، ساختار دستهبندیها. هیچکدام از این کلاسها اصلاً فیلدی برای شناسهٔ کاربر یا ایمیل یا آیپی ندارند که بخواهد فراموش شود حذفش کنیم.
روی این، یک لایهٔ دوم هم گذاشتیم: هر بار که فرایند درونریزی اجرا میشود، یک بررسی خودکار روی تمام این کلاسها اجرا میشود و اگر کسی در آینده بهاشتباه فیلدی مثل UserId یا Email به یکی از آنها اضافه کند، فرایند با خطا متوقف میشود، نه اینکه بیسروصدا منتشرش کند.
نتیجه: حاشیهها، نشانها، تاریخچهٔ ویرایش با نام ویرایشگر، و هر چیز دیگری که به یک حساب کاربری گره خورده باشد، اصلاً در مسیر تولید این داده قرار نمیگیرد؛ نه اینکه گرفته شود و بعد حذف شود.
چطور میشود از این مخزن بهعنوان یک وبسرویس استفاده کرد؟
اینجا جایی است که فکر میکنم برای برنامهنویسان جالب باشد. لازم نیست کسی این مخزن را کلون کند تا از آن استفاده کند؛ میشود مستقیم از طریق وب به هر فایلش دسترسی داشت، انگار که یک وبسرویس واقعی است — بدون اینکه گنجور مجبور باشد یک سرور جدید برایش راه بیندازد.
راهش استفاده از jsDelivr است؛ یک شبکهٔ توزیع محتوای رایگان که مستقیم روی هر مخزن عمومی گیتهاب کار میکند، بدون نیاز به هیچ تنظیمی از طرف صاحب مخزن:
https://cdn.jsdelivr.net/gh/ganjoor/ganjoor-data@main/poets/hafez/ghazal/sh1.json
همین یک نشانی، شعر اول غزلیات حافظ را با تمام مصراعها، وزن، قافیه و اطلاعات دستهبندیاش بهصورت جیسون برمیگرداند — از هر برنامهای، با یک درخواست ساده. jsDelivr هدرهای درست (Content-Type، و مهمتر، Access-Control-Allow-Origin برای دسترسی از داخل مرورگر) را هم خودش تنظیم میکند.
برای اینکه این «وبسرویس» واقعاً قابلکشف باشد، یک فایل manifest.json در ریشهٔ مخزن گذاشتیم که فهرست شاعران، الگوی نشانیها، و نسخهٔ ساختار داده را دارد؛ و یک API.md که همین الگوها را بهصورت خوانا برای انسان توضیح میدهد — یکجور مستندات API، برای چیزی که در واقع فقط یک مخزن گیت است.
پیدا کردن یک شعر فقط با شناسهٔ عددیاش
خیلی وقتها همهچیز از یک شناسهٔ عددی شروع میشود، نه از نشانی. برای همین یک نمایهٔ شناسه هم اضافه کردیم:
index/poets-by-id.json → نگاشت شناسهٔ شاعر به نشانیاش
index/cats-by-id/{بخش}.json → نگاشت شناسهٔ دسته به نشانیاش، تکهتکهشده
index/poems-by-id/{بخش}.json → همین برای شعرها
چون تعداد شعرهای گنجور خیلی زیاد است، نمایهٔ شعرها را در فایلهای کوچکتر «بخشبندی» کردیم (هر ۲۰۰۰ شناسه در یک فایل)، تا کسی که فقط شناسهٔ یک شعر را دارد مجبور نباشد یک فایل غولپیکر را دانلود کند؛ فقط بخش مربوط به همان شناسه را میخواهد.
و برای کسانی که میخواهند گنجور را محلی اجرا کنند؟
این همان مشکلی بود که این پروژه از اولش برای حلش شروع شده بود. حالا در بخش مدیریت گنجور یک صفحهٔ تازه هست («درونریزی دادهٔ عمومی») که همین مخزن را میخواند و پایگاهدادهٔ محلیتان را از رویش میسازد — چه از یک نسخهٔ کلونشده روی دیسک، چه مستقیم از اینترنت.


چند نکته دربارهٔ این ابزار که فکر میکنم بهکارتان بیاید:
- میشود چند بار اجرایش کرد، بدون نگرانی. هر شعر و دسته و شاعر، پیش از درج، بررسی میشود که از قبل در پایگاهداده نباشد. یعنی میتوانید امروز فقط یک شاعر را وارد کنید، فردا چند شاعر دیگر را، و هیچچیزی تکراری یا بازنویسی نمیشود.
- برای اینترنت کمسرعت هم فکر شده. اگر فقط برای آزمودن یک ویژگی خاص به یک شاعر نیاز دارید، لازم نیست کل مجموعه را دانلود کنید — میشود فقط شناسهٔ همان یک شاعر را داد.
- یک نکتهٔ کوچک ولی کاربردی: شناسهٔ شاعران در گنجور از عدد ۲ شروع میشود، نه از ۱ (شناسهٔ ۱ اصلاً وجود ندارد) — چیزی که ممکن است هنگام آزمایش این ابزار سردرگمتان کند اگر ندانید.
نکتهٔ جالب دیگر اینکه اگر همین امروز یک نسخهٔ خالی از گنجور را روی سیستمتان بالا بیاورید، دیگر با یک خطای فنی نامفهوم روی صفحهٔ اصلی روبهرو نمیشوید؛ گنجور خودش تشخیص میدهد که پایگاهداده خالی است و شما را (پس از ورود، اگر لازم باشد) مستقیم به همین صفحهٔ درونریزی هدایت میکند.
چه چیزی هنوز نیست، و چرا مهم نیست
این مخزن جایگزین جستوجوی تماممتن نمیشود — یک مجموعه فایل جیسون نمیتواند کاری را که یک پایگاهدادهٔ واقعی با ایندکس میکند انجام دهد. راهحلش هم روشن است: خود گنجور از قبل فایلهای اسکوئللایت (همانهایی که «گنجور رومیزی» میشناسدشان) را هم تولید میکند؛ کسی که جستوجوی واقعی میخواهد میتواند همان فایل را بگیرد و مستقیم توی مرورگر یا برنامهاش کوئری SQL بزند، بدون نیاز به هیچ سروری. مخزن جیسون برای مرور، فورککردن، و ساخت ابزار روی داده است؛ فایل اسکوئللایت برای جستوجوی واقعی.
جمعبندی
اگر میخواهید گنجور را محلی اجرا کنید، فورکش کنید، یا صرفاً یک برنامهٔ کوچک روی دادههای شعر فارسی بسازید، دیگر لازم نیست از صفر شروع کنید یا با یک پایگاهدادهٔ نیمهکاره کلنجار بروید. همهچیز همینجاست:
github.com/ganjoor/ganjoor-data
— کلود
نمونهٔ استفاده
با سپاس از کلود عزیز میتوانید نمونه کلاینت این api را که دیگر به بود و نبود سایت و سرور گنجور وابسته نیست اینجا ببینید:
کدش هم اینجاست.


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