คำเตือน
อัญมณีไม่ได้รับการดูแลในปัจจุบันและการพัฒนาถูกระงับไว้ หากคุณสนใจที่จะรับช่วงต่ออย่าลังเลที่จะติดต่อฉัน
ต้องการการค้นหาข้อความเต็มรูปแบบที่รวดเร็วสำหรับสคริปต์ทับทิมของคุณ แต่ Solr และ Elasticsearch เป็น overkill? -
คุณอยู่ในสถานที่ที่เหมาะสม Tantiny เป็นไลบรารีการค้นหาข้อความแบบเต็มรูปแบบที่เรียบง่ายสำหรับทับทิมโดยใช้ Tanti V Y (ทางเลือกที่ยอดเยี่ยมสำหรับ Apache Lucene ที่เขียนด้วย Rust) เป็นเรื่องที่ดีสำหรับกรณีที่งานของคุณในมือต้องใช้การค้นหาข้อความแบบเต็ม แต่การกำหนดค่าเครื่องมือค้นหาแบบกระจายเต็มรูปแบบจะต้องใช้เวลามากกว่างานเอง และแม้ว่าคุณจะใช้เอ็นจิ้นดังกล่าวในโครงการของคุณแล้ว (ซึ่งมีแนวโน้มสูงจริง ๆ แล้ว) มันอาจจะง่ายกว่าที่จะใช้ tantiny แทนเพราะไม่เหมือนกับ Solr และ Elasticsearch มันไม่ต้องการ อะไร เลย (ไม่มีเซิร์ฟเวอร์แยกต่างหากหรือกระบวนการหรืออะไรก็ตาม) ดังนั้นเมื่อคุณพบว่าตัวเองอยู่ในสถานการณ์เมื่อใช้เครื่องมือค้นหาที่คุณเลือกจะเป็นเรื่องยุ่งยาก/ไม่สะดวกหรือจะต้องมีการตั้งค่าเพิ่มเติมคุณสามารถเปลี่ยนกลับไปเป็นโซลูชันที่รวดเร็วและสกปรกที่ไม่ยืดหยุ่นและรวดเร็ว
Tantiny ไม่ได้ผูกทับทิมกับ tantivy อย่างแน่นอน แต่มันพยายามที่จะใกล้ชิด ปรัชญาหลักคือการให้การเข้าถึงดัชนีคว่ำของ Tantivy ในระดับต่ำ แต่ด้วย API 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 API นั้นมีการเปลี่ยนแปลงและจนกว่าจะถึง 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 ของเอกสารถูกเก็บไว้ (ค่าเริ่มต้นเป็น :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เพื่ออัปเดตเอกสารเพียงเพิ่มอีกครั้ง (ตราบใดที่ ID เหมือนกัน):
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 (ดูรายละเอียดด้านล่าง) ผ่านฟิลด์ 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 | ส่งคืนเอกสารที่จัดทำดัชนีทั้งหมด |
| empty_query | ส่งคืนไม่มีอะไรเลย (ใช้ภายใน) |
| term_query | เอกสารที่มีคำที่ระบุ |
| Fuzzy_term_query | เอกสารที่มีคำที่ระบุภายในระยะทาง Levenshtein |
| วลี _Query | เอกสารที่มีลำดับคำที่ระบุ |
| regex_query | เอกสารที่มีคำที่ตรงกับ Regex ที่ระบุ |
| คำนำหน้า _Query | เอกสารที่มีคำว่ามีคำนำหน้าที่ระบุ |
| range_query | เอกสารที่มีฟิลด์ integer double หรือ date ภายในช่วงที่ระบุ |
| facet_query | เอกสารที่เป็นของหมวดหมู่ที่ระบุ |
| Smart_Query | การรวมกันของ term_query , fuzzy_term_query และ prefix_query |
ดูไฟล์ Signatures เพื่อดูว่าพารามิเตอร์ใดที่สอบถามได้
การสืบค้นทั้งหมดสามารถค้นหาในฟิลด์หลายฟิลด์ (ยกเว้น facet_query เพราะมันไม่สมเหตุสมผลที่นั่น)
ดังนั้นคำถามต่อไปนี้:
index . term_query ( %i[ title description ] , "hello" )เทียบเท่ากับ:
index . term_query ( :title , "hello" ) | index . term_query ( :description , "hello" ) แบบสอบถามทั้งหมดรองรับพารามิเตอร์ boost ที่อนุญาตให้ Bump Documents ตำแหน่งในการค้นหา:
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 จะดึงคำศัพท์จากสตริงการสืบค้นของคุณโดยใช้ tokenizers ในฟิลด์ที่เกี่ยวข้องและค้นหาดัชนีสำหรับเอกสารที่มีคำเหล่านั้นผ่าน 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 ในสคีมาของเราที่ใช้ tokenizers stemmer ภาษาอังกฤษและรัสเซียตามลำดับ
regex_query regex_query ยอมรับรูปแบบ regex แต่จะต้องเป็น Rust Regex ไม่ใช่ Ruby Regexp ดังนั้นแทนที่จะเป็น index.regex_query(:description, /hel[lp]/) คุณต้องใช้ index.regex_query(:description, "hel[lp]") ในฐานะที่เป็นบันทึกด้านข้าง regex_query ค่อนข้างเร็วเพราะใช้ลัง FST ภายใน
ดังนั้นเราได้พูดถึงโทเคนิเซอร์มากกว่าหนึ่งครั้งแล้ว พวกเขาคืออะไร?
Tokenizers เป็นสิ่งที่ Tantivy ใช้ในการสับข้อความของคุณลงในคำศัพท์เพื่อสร้างดัชนีกลับด้าน จากนั้นคุณสามารถค้นหาดัชนีตามข้อกำหนดเหล่านี้ มันเป็นแนวคิดที่สำคัญที่จะต้องเข้าใจเพื่อที่คุณจะไม่สับสนเมื่อ index.term_query(:description, "Hello") ไม่กลับมาทำอะไรเพราะ Hello ไม่ใช่คำ แต่ hello คือ คุณต้องแยกข้อกำหนดจากแบบสอบถามก่อนค้นหาดัชนี ปัจจุบันมีเพียง smart_query เท่านั้นที่ทำเพื่อคุณ นอกจากนี้ประเภทของฟิลด์เดียวที่เป็นข้อความคือ text ดังนั้นสำหรับฟิลด์ string คุณควรใช้การจับคู่ที่แน่นอน (เช่น 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
endTokenizer ง่าย ๆ จะตัดข้อความบนเครื่องหมายวรรคตอนและช่องว่าง, ลบโทเค็นที่ยาวและทำให้ข้อความลดลง
tokenizer = Tantiny :: Tokenizer . new ( :simple )
tokenizer . terms ( "Hello World!" ) # ["hello", "world"]Stemmer Tokenizers เป็นเหมือน 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 ข้างๆ
หลังจากตรวจสอบ repo ให้เรียกใช้ bin/setup เพื่อติดตั้งการพึ่งพา จากนั้นเรียกใช้ rake build เพื่อสร้างส่วนขยายดั้งเดิมจากนั้น rake spec เพื่อเรียกใช้การทดสอบ นอกจากนี้คุณยังสามารถเรียกใช้ bin/console สำหรับพรอมต์แบบโต้ตอบที่จะช่วยให้คุณสามารถทดลองได้
เราใช้ Commental Commits เพื่อสร้างการเปลี่ยนแปลงโดยอัตโนมัติชนเวอร์ชันความหมายและเพื่อเผยแพร่และปล่อยอัญมณี สิ่งที่คุณต้องทำคือยึดติดกับการประชุมและ CI จะดูแลทุกอย่างให้คุณ
รายงานข้อผิดพลาดและคำขอดึงยินดีต้อนรับบน GitHub ที่ https://github.com/baygeldin/tantiny
อัญมณีมีให้เป็นโอเพ่นซอร์สภายใต้ข้อกำหนดของใบอนุญาต MIT