تحذير
لا يتم الحفاظ على جوهرة حاليًا ويتم تعليق التطوير. إذا كنت مهتمًا بالتولي ، فلا تتردد في التواصل معي.
هل تحتاج إلى بحث سريع عن النص الكامل عن البرنامج النصي الخاص بك ، ولكن Solr و Elasticsearch هي مبالغة؟ ؟
أنت في المكان المناسب. Tantiny هي مكتبة بحث كاملة عن النص عن Ruby استنادًا إلى Tanti v y (بديل رائع لـ Apache Lucene مكتوب في الصدأ). إنه أمر رائع بالنسبة للحالات التي تتطلب فيها مهمتك في متناول اليد البحث عن النص الكامل ، ولكن تكوين محرك بحث موزع بالكامل سيستغرق وقتًا أكثر من المهمة نفسها. وحتى إذا كنت تستخدم بالفعل مثل هذا المحرك في مشروعك (وهو أمر محتمل للغاية ، في الواقع) ، قد يكون من الأسهل استخدام Tantiny بدلاً من ذلك لأنه على عكس Solr و Elasticsearch ، فإنه لا يحتاج إلى أي شيء للعمل (لا يوجد خادم أو عملية منفصلة أو أي شيء آخر) ، فهو قابل للتضمين بحت. لذلك ، عندما تجد نفسك في موقف عند استخدام محرك البحث المفضل لديك ، سيكون أمرًا صعبًا/غير محدد أو سيتطلب إعدادًا إضافيًا يمكنك دائمًا العودة إلى حل سريع وقذر غير مرن وسريع.
Tantiny ليس ارتباطات الياقوت بالضبط إلى tantivy ، ولكنها تحاول أن تكون قريبة. تتمثل الفلسفة الرئيسية في توفير وصول منخفض المستوى إلى فهرس Tantivy المقلوب ، ولكن مع واجهة برمجة تطبيقات Ruby-esque لطيفة ، والافتراضات المعقولة ، ووظائف إضافية يتم رشها في الأعلى.
ألقِ نظرة على مثال أساسي:
index = Tantiny :: Index . new ( "/path/to/index" ) { text :description }
index << { id : 1 , description : "Hello World!" }
index << { id : 2 , description : "What's up?" }
index << { id : 3 , description : "Goodbye World!" }
index . reload
index . search ( "world" ) # 1, 3 أضف هذا السطر إلى Gemfile في التطبيق الخاص بك:
gem "tantiny"ثم تنفذ:
$ bundle install
أو تثبيته بنفسك على النحو التالي:
$ gem install tantiny
لا يتعين عليك تثبيت Rust على نظامك لأن Tantiny سيحاول تنزيل الثنائيات المسبقة المسبقة التي تم استضافتها على إصدارات GitHub أثناء التثبيت. ومع ذلك ، إذا لم يتم العثور على ثنائيات مسبقة التجميع لنظامك (وهو مزيج من النظام الأساسي والهندسة المعمارية وروبي) ، فستحتاج إلى تثبيت الصدأ أولاً.
تحذير
يتم دعم إصدارات الصدأ حتى 1.77 فقط. انظر هذه المشكلة لمزيد من التفاصيل.
مهم
من فضلك ، تأكد من تحديد النسخة الصغيرة عند إعلان الاعتماد على tantiny . واجهة برمجة التطبيقات هي موضوع للتغيير ، وإلى أن يصل إلى 1.0.0 ، من المرجح أن يدل عثرة في الإصدار الثانوي على تغيير كسر.
يجب عليك تحديد طريق إلى المكان الذي سيتم تخزين الفهرس وكتلة تحدد المخطط:
Tantiny :: Index . new "/tmp/index" do
id :imdb_id
facet :category
string :title
text :description
integer :duration
double :rating
date :release_date
endفيما يلي أوصاف لكل نوع حقل:
| يكتب | وصف |
|---|---|
| بطاقة تعريف | يحدد مكان تخزين معرفات المستندات (الإعدادات الافتراضية إلى :id ). |
| الوجه | الحقول مع قيم مثل /animals/birds (أي الفئات الهرمية). |
| خيط | الحقول مع النص غير المميز. |
| نص | الحقول مع نص يتم رمزه بواسطة الرمز المميز المحدد. |
| عدد صحيح | الحقول مع قيم عدد صحيح. |
| مزدوج | الحقول مع قيم تعويم. |
| تاريخ | الحقول مع إما نوع DateTime أو شيء يتحول إليه. |
يمكنك إطعام الفهرس أي نوع من الكائنات التي لها طرق محددة في المخطط الخاص بك ، ولكن تجزئة العاديات تعمل أيضًا:
rio_bravo = OpenStruct . new (
imdb_id : "tt0053221" ,
type : '/western/US' ,
title : "Rio Bravo" ,
description : "A small-town sheriff enlists a drunk, a kid and an old man to help him fight off a ruthless cattle baron." ,
duration : 141 ,
rating : 8.0 ,
release_date : Date . parse ( "March 18, 1959" )
)
index << rio_bravo
hanabi = {
imdb_id : "tt0119250" ,
type : "/crime/Japan" ,
title : "Hana-bi" ,
description : "Nishi leaves the police in the face of harrowing personal and professional difficulties. Spiraling into depression, he makes questionable decisions." ,
duration : 103 ,
rating : 7.7 ,
release_date : Date . parse ( "December 1, 1998" )
}
index << hanabi
brother = {
imdb_id : "tt0118767" ,
type : "/crime/Russia" ,
title : "Brother" ,
description : "An ex-soldier with a personal honor code enters the family crime business in St. Petersburg, Russia." ,
duration : 99 ,
rating : 7.9 ,
release_date : Date . parse ( "December 12, 1997" )
}
index << brotherمن أجل تحديث المستند ، فقط أضفه مرة أخرى (طالما أن المعرف هو نفسه):
rio_bravo . rating = 10.0
index << rio_bravoيمكنك أيضًا حذفه إذا كنت تريد:
index . delete ( rio_bravo . imdb_id ) إذا كنت بحاجة إلى إجراء عمليات كتابة متعددة (أي أكثر من واحدة) ، فيجب عليك دائمًا استخدام transaction :
index . transaction do
index << rio_bravo
index << hanabi
index << brother
end تتغير مجموعة المعاملات وتلتزم بها الفهرس دفعة واحدة. هذا أكثر فعالية بشكل كبير من إجراء هذه التغييرات واحدة تلو الأخرى. في الواقع ، يتم لف جميع عمليات الكتابة (أي << delete ) في معاملة ضمنية عندما تتصل بها خارج المعاملة ، لذا فإن الاتصال << 10 مرات خارج المعاملة هو نفس الشيء الذي يقوم به 10 معاملات منفصلة.
Tantiny هو آمن مؤشر الترابط مما يعني أنه يمكنك مشاركة مثيل واحد بأمان من الفهرس بين مؤشرات الترابط. يمكنك أيضًا تفرخ عمليات منفصلة يمكن أن تكتب إليها والقراءة من نفس الفهرس. ومع ذلك ، أثناء القراءة من الفهرس يجب أن تكون متوازية ، فإن الكتابة إليها ليست كذلك . عندما تتصل transaction أو أي عملية أخرى تعدل الفهرس (أي << delete ) ، فإنه سيغلق الفهرس لمدة العملية أو انتظر عملية أو مؤشر ترابط آخر لإطلاق القفل. الاستثناء الوحيد لذلك هو عندما تكون هناك عملية أخرى مع فهرس مع كاتب حصري يعمل في مكان ما في هذه الحالة ستفشل الأساليب التي تعدل الفهرس على الفور.
وبالتالي ، من الأفضل أن يكون لديك عملية كاتبة واحدة والعديد من عمليات القارئ إذا كنت ترغب في تجنب منع المكالمات. الطريقة المناسبة للقيام بذلك هي تعيين exclusive_writer على true عند تهيئة الفهرس:
index = Tantiny :: Index . new ( "/path/to/index" , exclusive_writer : true ) { }وبهذه الطريقة ، لن يتم الحصول على كاتب الفهرس إلا مرة واحدة مما يعني أن الذاكرة الخاصة به وفهرسة لا يتم تخصيصها إلا مرة واحدة أيضًا. وإلا يتم الحصول على كاتب فهرس جديد في كل مرة تقوم فيها بعملية كتابة.
تأكد من تحديث الفهرس الخاص بك عن طريق إعادة تحميله أولاً:
index . reloadوالبحث عنه (أخيرًا!):
index . search ( "a drunk, a kid, and an old man" )بشكل افتراضي ، سيُرجع معرفات من 10 مستندات مطابقة ، ولكن يمكنك تخصيصها:
index . search ( "a drunk, a kid, and an old man" , limit : 100 ) قد تتساءل ، كيف يقوم بالضبط بإجراء البحث؟ حسنًا ، يتمثل السلوك الافتراضي في استخدام smart_query Search (انظر أدناه للحصول على التفاصيل) على جميع حقول text المحددة في المخطط الخاص بك. لذلك ، يمكنك تمرير المعلمات التي يقبلها smart_query هنا:
index . search ( "a dlunk, a kib, and an olt mab" , fuzzy_distance : 1 )ومع ذلك ، يمكنك تخصيصه عن طريق تكوين الاستعلام الخاص بك من لبنات البناء الأساسية:
popular_movies = index . range_query ( :rating , 8.0 .. 10.0 )
about_sheriffs = index . term_query ( :description , "sheriff" )
crime_movies = index . facet_query ( :cetegory , "/crime" )
long_ass_movies = index . range_query ( :duration , 180 .. 9999 )
something_flashy = index . smart_query ( :description , "bourgeoisie" )
index . search ( ( popular_movies & about_sheriffs ) | ( crime_movies & ! long_ass_movies ) | something_flashy )أنا أعلم ، طعم غريب! لكن رائع ، هاه؟ ألقِ نظرة على جميع الاستفسارات المتاحة أدناه.
| استفسار | سلوك |
|---|---|
| all_query | إرجاع جميع المستندات المفهرسة. |
| فارغة | لا تعود بالضبط لا شيء (يستخدم داخليًا). |
| term_query | المستندات التي تحتوي على المصطلح المحدد. |
| fuzzy_term_query | المستندات التي تحتوي على المصطلح المحدد على مسافة Levenshtein. |
| phrase_query | المستندات التي تحتوي على تسلسل محدد من المصطلحات. |
| regex_query | المستندات التي تحتوي على مصطلح يطابق Regex المحدد. |
| prefix_query | المستندات التي تحتوي على مصطلح مع البادئة المحددة. |
| Range_query | المستندات التي مع integer ، double أو date الحقل ضمن النطاق المحدد. |
| facet_query | المستندات التي تنتمي إلى الفئة المحددة. |
| smart_query | مزيج من term_query ، fuzzy_term_query و prefix_query . |
ألقِ نظرة على ملف التوقيعات لمعرفة ما تقبله المعلمات.
يمكن لجميع الاستعلامات البحث على حقول متعددة (باستثناء facet_query لأنها لا معنى لها).
لذلك ، الاستعلام التالي:
index . term_query ( %i[ title description ] , "hello" )يعادل:
index . term_query ( :title , "hello" ) | index . term_query ( :description , "hello" ) تدعم جميع الاستعلامات المعلمة boost التي تسمح بوضع المستندات في البحث:
about_cowboys = index . term_query ( :description , "cowboy" , boost : 2.0 )
about_samurai = index . term_query ( :description , "samurai" ) # sorry, Musashi...
index . search ( about_cowboys | about_samurai )smart_query سيقوم smart_query Search باستخراج المصطلحات من سلسلة الاستعلام الخاصة بك باستخدام رمز الرمز المميز للحقل المعني والبحث في الفهرس عن المستندات التي تحتوي على هذه المصطلحات عبر term_query . إذا تم تحديد معلمة fuzzy_distance فسوف تستخدم fuzzy_term_query . أيضًا ، يتيح أن يكون المصطلح الأخير غير مكتمل باستخدام prefix_query .
لذلك ، الاستعلام التالي:
index . smart_query ( %i[ en_text ru_text ] , "dollars рубли eur" , fuzzy_distance : 1 )يعادل:
t1_en = index . fuzzy_term_query ( :en_text , "dollar" )
t2_en = index . fuzzy_term_query ( :en_text , "рубли" )
t3_en = index . fuzzy_term_query ( :en_text , "eur" )
t3_prefix_en = index . prefix_query ( :en_text , "eur" )
t1_ru = index . fuzzy_term_query ( :ru_text , "dollars" )
t2_ru = index . fuzzy_term_query ( :ru_text , "рубл" )
t3_ru = index . fuzzy_term_query ( :ru_text , "eur" )
t3_prefix_ru = index . prefix_query ( :ru_text , "eur" )
( t1_en & t2_en & ( t3_en | t3_prefix_en ) ) | ( t1_ru & t2_ru & ( t3_ru | t3_prefix_ru ) ) لاحظ كيف يتم تنقيط الكلمات "الدولارات" و "рии" بشكل مختلف اعتمادًا على الحقل الذي نبحث عنه. هذا على افتراض أن لدينا حقول en_text و ru_text في مخططنا التي تستخدم الرمز المميزات للغتلة الإنجليزية والروسية على التوالي.
regex_query يقبل regex_query نمط regex ، ولكن يجب أن يكون regex الصدأ ، وليس Regexp ruby. لذلك ، بدلاً من index.regex_query(:description, /hel[lp]/) تحتاج إلى استخدام index.regex_query(:description, "hel[lp]") . كملاحظة جانبية ، يكون regex_query سريعًا جدًا لأنه يستخدم صندوق FST داخليًا.
لذلك ، ذكرنا المميزات أكثر من مرة بالفعل. ما هم؟
الرمز المميز هو ما يستخدمه Tantivy لتقطيع النص على مصطلحات لإنشاء فهرس مقلوب. ثم يمكنك البحث في الفهرس بهذه الشروط. من المفهوم المهم أن نفهم حتى لا تشعر بالارتباك hello index.term_query(:description, "Hello") Hello يجب عليك استخراج المصطلحات من الاستعلام قبل البحث في الفهرس. حاليا ، فقط smart_query يفعل ذلك من أجلك. وأيضًا ، فإن نوع الحقل الوحيد المميز هو text ، لذلك بالنسبة لحقول string ، يجب عليك استخدام المطابقة الدقيقة (IE index.term_query(:title, "Hello") ).
بشكل افتراضي ، يتم استخدام Tokenizer simple ، ولكن يمكنك تحديد الرمز المميز المطلوب عالميًا عبر خيارات الفهرس أو محليًا عبر خيارات الحقل المحددة:
en_stemmer = Tantiny :: Tokenizer . new ( :stemmer )
ru_stemmer = Tantiny :: Tokenizer . new ( :stemmer , language : :ru )
Tantiny :: Index . new "/tmp/index" , tokenizer : en_stemmer do
text :description_en
text :description_ru , tokenizer : ru_stemmer
endيقطع الرمز البسيط النص على علامات الترقيم والمساحات البيضاء ، ويزيل الرموز الطويلة ، ويؤثر على النص.
tokenizer = Tantiny :: Tokenizer . new ( :simple )
tokenizer . terms ( "Hello World!" ) # ["hello", "world"]يشبه المميزات STEMMER تمامًا مثل Tokenizer البسيط ، ولكن مع تنظيم إضافي وفقًا للغة المحددة (الافتراضيات للغة الإنجليزية).
tokenizer = Tantiny :: Tokenizer . new ( :stemmer , language : :ru )
tokenizer . terms ( "Привет миру сему!" ) # ["привет", "мир", "сем"]ألقِ نظرة على المصدر لمعرفة اللغات التي يتم دعمها.
Ngram Tokenizer يقطع نصك على ngrams من الحجم المحدد.
tokenizer = Tantiny :: Tokenizer . new ( :ngram , min : 5 , max : 10 , prefix_only : true )
tokenizer . terms ( "Morrowind" ) # ["Morro", "Morrow", "Morrowi", "Morrowin", "Morrowind"] ربما لاحظت أن طريقة search تُرجع معرفات المستندات فقط. هذا حسب التصميم. لا يتم تخزين المستندات نفسها في الفهرس. Tantiny هي مكتبة الحد الأدنى ، لذلك تحاول الحفاظ على الأمور بسيطة. إذا كنت بحاجة إلى استرداد مستند كامل ، فاستخدم متجرًا رئيسيًا مثل Redis بجانب.
بعد التحقق من الريبو ، قم بتشغيل bin/setup لتثبيت التبعيات. بعد ذلك ، قم بتشغيل rake build لبناء ملحقات أصلية ، ثم rake spec لتشغيل الاختبارات. يمكنك أيضًا تشغيل bin/console للحصول على مطالبة تفاعلية تتيح لك التجربة.
نحن نستخدم الالتزامات التقليدية لإنشاء Changelog تلقائيًا ، وتصوير النسخة الدلالية ، ونشر الجوهرة وإصدارها. كل ما عليك فعله هو التمسك بالاتفاقية وستهتم CI بكل شيء آخر من أجلك.
يتم الترحيب بتقارير الأخطاء وطلبات السحب على Github على https://github.com/baygeldin/tantiny.
الجوهرة متوفرة كمصدر مفتوح بموجب شروط ترخيص معهد ماساتشوستس للتكنولوجيا.