Ruby wrapper ที่เรียบง่าย แต่ทรงพลังรอบๆ RediSearch ซึ่งเป็นเครื่องมือค้นหาที่อยู่ด้านบนของ Redis
ประการแรก จำเป็นต้องติดตั้ง Redis และ RediSearch
คุณสามารถดาวน์โหลด Redis ได้จาก https://redis.io/download และดูคำแนะนำในการติดตั้งได้ที่นี่ หรือคุณสามารถติดตั้งผ่าน Homebrew บน macOS หรือ Linux
หากต้องการติดตั้ง RediSearch โปรดดูที่ https://oss.redislabs.com/redisearch/Quick_Start.html เมื่อคุณสร้าง RediSearch แล้ว หากคุณไม่ได้ใช้ Docker คุณสามารถอัปเดตไฟล์ redis.conf ของคุณเพื่อให้โหลดโมดูล RediSearch ด้วย loadmodule /path/to/redisearch.so
เสมอ (บน macOS สามารถดูไฟล์ redis.conf ได้ที่ /usr/local/etc/redis.conf
)
หลังจากที่ Redis และ RediSearch เปิดใช้งานแล้ว ให้เพิ่มบรรทัดต่อไปนี้ใน Gemfile ของคุณ:
gem 'redi_search'
แล้ว:
❯ bundle
หรือติดตั้งด้วยตัวเอง:
❯ gem install redi_search
และต้องการมัน:
require 'redi_search'
เมื่อติดตั้ง Gem แล้วและจำเป็น คุณจะต้องกำหนดค่าด้วยการกำหนดค่า Redis ของคุณ หากคุณอยู่บน Rails สิ่งนี้ควรอยู่ในเครื่องมือเริ่มต้น ( config/initializers/redi_search.rb
)
RediSearch . configure do | config |
config . redis_config = {
host : "127.0.0.1" ,
port : "6379"
}
end
RediSearch เกี่ยวข้องกับดัชนีการค้นหา ดังนั้นเรามาเริ่มต้นด้วยการกำหนดว่าดัชนีการค้นหาคืออะไร ตาม Swifttype:
ดัชนีการค้นหาคือเนื้อหาของข้อมูลที่มีโครงสร้างซึ่งเครื่องมือค้นหาอ้างถึงเมื่อค้นหาผลลัพธ์ที่เกี่ยวข้องกับข้อความค้นหาเฉพาะ ดัชนีเป็นส่วนสำคัญของระบบการค้นหา เนื่องจากต้องปรับแต่งให้เหมาะกับวิธีการดึงข้อมูลเฉพาะของอัลกอริทึมของเครื่องมือค้นหา ในลักษณะนี้ อัลกอริธึมและดัชนีจะเชื่อมโยงถึงกันอย่างแยกไม่ออก Index ยังสามารถใช้เป็นคำกริยา (indexing) ซึ่งหมายถึงกระบวนการรวบรวมข้อมูลเว็บไซต์ที่ไม่มีโครงสร้างในรูปแบบที่มีโครงสร้างซึ่งปรับให้เหมาะกับอัลกอริทึมของเครื่องมือค้นหา
วิธีหนึ่งในการคิดเกี่ยวกับดัชนีคือการพิจารณาการเปรียบเทียบต่อไปนี้ระหว่างโครงสร้างพื้นฐานการค้นหาและระบบการจัดเก็บเอกสารในสำนักงาน ลองนึกภาพคุณมอบกระดาษ (เอกสาร) จำนวนหนึ่งปึกให้กับนักศึกษาฝึกงาน และบอกให้พวกเขาจัดระเบียบกระดาษเหล่านี้ในตู้เก็บเอกสาร (ดัชนี) เพื่อช่วยให้บริษัทค้นหาข้อมูลได้อย่างมีประสิทธิภาพมากขึ้น ผู้ฝึกงานจะต้องคัดแยกเอกสารและทำความเข้าใจข้อมูลทั้งหมดที่อยู่ในนั้นก่อน จากนั้นจึงตัดสินใจเลือกระบบในการจัดเรียงเอกสารในตู้เก็บเอกสาร จากนั้นจึงตัดสินใจว่าอะไรคือเอกสาร วิธีที่มีประสิทธิภาพสูงสุดในการค้นหาและเลือกจากไฟล์เมื่ออยู่ใน Cabinet ในตัวอย่างนี้ กระบวนการจัดระเบียบและจัดเก็บเอกสารสอดคล้องกับกระบวนการจัดทำดัชนีเนื้อหาเว็บไซต์ และวิธีการค้นหาไฟล์ที่จัดระเบียบเหล่านี้และค้นหาไฟล์ที่เกี่ยวข้องมากที่สุดนั้นสอดคล้องกับอัลกอริธึมการค้นหา
ซึ่งจะกำหนดเขตข้อมูลและคุณสมบัติของเขตข้อมูลเหล่านั้นในดัชนี สคีมาคือ DSL แบบธรรมดา แต่ละฟิลด์สามารถเป็นหนึ่งในสี่ประเภท: ภูมิศาสตร์ ตัวเลข แท็ก หรือข้อความ และสามารถมีตัวเลือกได้มากมาย ตัวอย่างง่ายๆ ของสคีมาคือ:
RediSearch :: Schema . new do
text_field :first_name
text_field :last_name
end
ตัวเลือกที่รองรับสำหรับแต่ละประเภทมีดังนี้:
โดยไม่มีตัวเลือก: text_field :name
text_field :name, weight: 2
text_field :name, phonetic: 'dm:en'
text_field :name, sortable: true
sortable
เพื่อสร้างฟิลด์ที่การอัปเดตโดยใช้ PARTIAL จะไม่ทำให้เกิดการจัดทำดัชนีเอกสารใหม่ทั้งหมด หากฟิลด์มี no_index
และไม่มีการ sortable
ดัชนีก็จะไม่สนใจฟิลด์นั้นtext_field :name, no_index: true
text_feidl :name, no_stem: true
โดยไม่มีตัวเลือก: numeric_field :price
numeric_field :id, sortable: true
sortable
เพื่อสร้างฟิลด์ที่การอัปเดตโดยใช้ PARTIAL จะไม่ทำให้เกิดการจัดทำดัชนีเอกสารใหม่ทั้งหมด หากฟิลด์มี no_index
และไม่มีการ sortable
ดัชนีก็จะไม่สนใจฟิลด์นั้นnumeric_field :id, no_index: true
โดยไม่มีตัวเลือก: tag_field :tag
tag_field :tag, sortable: true
sortable
เพื่อสร้างฟิลด์ที่การอัปเดตโดยใช้ PARTIAL จะไม่ทำให้เกิดการจัดทำดัชนีเอกสารใหม่ทั้งหมด หากฟิลด์มี no_index
และไม่มีการ sortable
ดัชนีก็จะไม่สนใจฟิลด์นั้นtag_field :tag, no_index: true
tag_field :tag, separator: ','
โดยไม่มีตัวเลือก: geo_field :place
geo_field :place, sortable: true
sortable
เพื่อสร้างฟิลด์ที่การอัปเดตโดยใช้ PARTIAL จะไม่ทำให้เกิดการจัดทำดัชนีเอกสารใหม่ทั้งหมด หากฟิลด์มี no_index
และไม่มีการ sortable
ดัชนีก็จะไม่สนใจฟิลด์นั้นgeo_field :place, no_index: true
Document
คือการแสดง Ruby ของแฮช Redis
คุณสามารถดึง Document
โดยใช้วิธี . .get
class
get(index, document_id)
ดึง Document
เดียวใน Index
สำหรับ document_id
ที่กำหนด คุณยังสามารถสร้างอินสแตนซ์ Document
โดยใช้เมธอดคลาส . .for_object(index, record, only: [])
ใช้อินสแตนซ์ Index
และวัตถุ Ruby ออบเจ็กต์นั้นจะต้องตอบสนองต่อฟิลด์ทั้งหมดที่ระบุใน Schema
ของ Index
ยอมรับ only
อาร์เรย์ของฟิลด์จากสคีมาและจำกัดฟิลด์ที่ส่งไปยัง Document
เมื่อคุณมีอินสแตนซ์ของ Document
แล้ว มันจะตอบสนองต่อฟิลด์ทั้งหมดที่ระบุใน Schema
ของ Index
เป็นวิธีการและ document_id
document_id
จะถูกเติมหน้าด้วยชื่อของ Index
โดยอัตโนมัติ เว้นแต่ว่าจะต้องไม่ซ้ำกันอยู่แล้ว เราเติมชื่อ Index
นำหน้าเพราะถ้าคุณมี Document
สองฉบับที่มีรหัสเดียวกันใน Index
ที่แตกต่างกัน เราไม่ต้องการให้ Document
แทนที่กัน นอกจากนี้ยังมีเมธอด #document_id_without_index
ซึ่งจะลบชื่อดัชนีที่นำหน้าออก
ในที่สุดก็มีวิธี #del
ที่จะลบ Document
ออกจาก Index
ในการเริ่มต้น Index
ให้ส่งชื่อของ Index
เป็นสตริงหรือสัญลักษณ์และบล็อก Schema
RediSearch :: Index . new ( name_of_index ) do
text_field :foobar
end
create
false
หากมีดัชนีอยู่แล้ว ยอมรับตัวเลือกบางส่วน:max_text_fields: #{true || false}
add_field
no_offsets: #{true || false}
no_highlight
temporary: #{seconds}
seconds
วินาที ตัวจับเวลาที่ไม่ได้ใช้งานภายในจะถูกรีเซ็ตทุกครั้งที่มีการค้นหาหรือเพิ่มดัชนี เนื่องจากดัชนีดังกล่าวมีน้ำหนักเบา คุณจึงสามารถสร้างดัชนีดังกล่าวได้หลายพันรายการโดยไม่มีผลกระทบด้านประสิทธิภาพเชิงลบno_highlight: #{true || false}
no_highlight
ก็มีความหมายโดยนัยเช่นกัน no_offsets
no_fields: #{true || false}
no_frequencies: #{true || false}
drop(keep_docs: false)
Index
จากอินสแตนซ์ Redis ส่งคืนบูลีน มีวิธีการ Bang ที่จะทำให้เกิดข้อยกเว้นเมื่อเกิดความล้มเหลว จะส่งคืนค่า false
หาก Index
ถูกทิ้งไปแล้ว รับคีย์เวิร์ดตัวเลือก arg, keep_docs
ซึ่งโดยค่าเริ่มต้นจะลบแฮชของเอกสารทั้งหมดใน Redisexist?
Index
info
Index
fields
Index
add(document)
Document
มีวิธีการ Bang ที่จะทำให้เกิดข้อยกเว้นเมื่อเกิดความล้มเหลวadd_multiple(documents)
Document
นี่เป็นวิธีที่มีประสิทธิภาพมากขึ้นในการเพิ่มเอกสารหลายรายการใน Index
ยอมรับตัวเลือกเดียวกันกับ add
del(document)
Document
ออกจาก Index
document_count
Document
ใน Index
add_field(name, type, **options, &block)
Index
index.add_field(:first_name, :text, phonetic: "dm:en")
reindex(documents, recreate: false)
recreate
เป็น true
Index
จะถูกทิ้งและสร้างใหม่ การค้นหาเริ่มต้นจาก RediSearch::Index
พร้อมด้วยส่วนคำสั่งที่สามารถเชื่อมโยงเข้าด้วยกันได้ เมื่อค้นหา อาร์เรย์ของ Document
จะถูกส่งกลับซึ่งมีวิธีการอ่านสาธารณะสำหรับฟิลด์สคีมาทั้งหมด
main ❯ index = RediSearch :: Index . new ( "user_idx" ) { text_field :name , phonetic : "dm:en" }
main ❯ index . add RediSearch :: Document . for_object ( index , User . new ( "10039" , "Gene" , "Volkman" ) )
main ❯ index . add RediSearch :: Document . for_object ( index , User . new ( "9998" , "Jeannie" , "Ledner" ) )
main ❯ index . search ( "john" )
RediSearch ( 1.1 ms ) FT . SEARCH user_idx `john`
=> [ #<RediSearch::Document:0x00007f862e241b78 first: "Gene", last: "Volkman", document_id: "10039">,
#<RediSearch::Document:0x00007f862e2417b8 first: "Jeannie", last: "Ledner", document_id: "9998">]
ข้อความค้นหาแบบวลีง่ายๆ - hello AND world
index . search ( "hello" ) . and ( "world" )
ข้อความค้นหาวลีที่ตรงทั้งหมด - hello FOLLOWED BY world
index . search ( "hello world" )
แบบสอบถามแบบยูเนี่ยน - hello OR world
index . search ( "hello" ) . or ( "world" )
แบบสอบถามเชิงลบ - hello AND NOT world
index . search ( "hello" ) . and . not ( "world" )
ทางแยกและสหภาพที่ซับซ้อน:
# Intersection of unions
index . search ( index . search ( "hello" ) . or ( "halo" ) ) . and ( index . search ( "world" ) . or ( "werld" ) )
# Negation of union
index . search ( "hello" ) . and . not ( index . search ( "world" ) . or ( "werld" ) )
# Union inside phrase
index . search ( "hello" ) . and ( index . search ( "world" ) . or ( "werld" ) )
ข้อกำหนดทั้งหมดรองรับตัวเลือกบางรายการที่สามารถนำไปใช้ได้
คำนำหน้า : จับคู่คำทั้งหมดที่ขึ้นต้นด้วยคำนำหน้า (คล้ายกับ like term%
ใน SQL)
index . search ( "hel" , prefix : true )
index . search ( "hello worl" , prefix : true )
index . search ( "hel" , prefix : true ) . and ( "worl" , prefix : true )
index . search ( "hello" ) . and . not ( "worl" , prefix : true )
ข้อกำหนดเพิ่มเติม : เอกสารที่มีข้อกำหนดเพิ่มเติมจะมีอันดับสูงกว่าที่ไม่มี
index . search ( "foo" ) . and ( "bar" , optional : true ) . and ( "baz" , optional : true )
คำคลุมเครือ : การแข่งขันจะดำเนินการตามระยะทางเลเวนชไทน์ (LD) ระยะทาง Levenshtein สูงสุดที่รองรับคือ 3
index . search ( "zuchini" , fuzziness : 1 )
ข้อความค้นหายังสามารถกำหนดขอบเขตเป็นฟิลด์เฉพาะโดยใช้คำสั่ง where
:
# Simple field specific query
index . search . where ( name : "john" )
# Using where with options
index . search . where ( first : "jon" , fuzziness : 1 )
# Using where with more complex query
index . search . where ( first : index . search ( "bill" ) . or ( "bob" ) )
การค้นหาช่องตัวเลขต้องใช้ช่วง:
index . search . where ( number : 0 .. 100 )
# Searching to infinity
index . search . where ( number : 0 .. Float :: INFINITY )
index . search . where ( number : - Float :: INFINITY .. 0 )
slop(level)
in_order
slop
เราตรวจสอบให้แน่ใจว่าคำค้นหาปรากฏในลำดับเดียวกันใน Document
เช่นเดียวกับในการสืบค้น โดยไม่คำนึงถึงออฟเซ็ตระหว่างคำเหล่านั้นno_content
Document
เท่านั้นไม่ใช่เนื้อหา สิ่งนี้มีประโยชน์หากใช้ RediSearch บนโมเดล Rails โดยที่แอตทริบิวต์ Document
ไม่สำคัญและกำลังแปลงเป็นวัตถุ ActiveRecord
language(language)
Document
เป็นภาษาจีน ควรตั้งค่าเป็นภาษาจีนเพื่อให้โทเค็นคำค้นหาถูกต้อง หากมีการส่งภาษาที่ไม่รองรับ คำสั่งจะส่งคืนข้อผิดพลาดsort_by(field, order: :asc)
:asc
หรือ :desc
limit(num, offset = 0)
num
ที่ระบุที่ offset
ขีดจำกัดเริ่มต้นตั้งไว้ที่ 10
count
Document
ที่พบในคำค้นหาhighlight(fields: [], opening_tag: "<b>", closing_tag: "</b>")
fields
คืออาร์เรย์ของฟิลด์ที่จะเน้นverbatim
no_stop_words
with_scores
Document
สามารถใช้เพื่อรวมผลลัพธ์จากหลาย ๆ อินสแตนซ์ สิ่งนี้จะเพิ่มวิธี score
ให้กับอินสแตนซ์ Document
ที่ส่งคืนreturn(*fields)
Document
จะถูกส่งกลับexplain
การตรวจการสะกดเริ่มต้นจาก RediSearch::Index
และให้คำแนะนำสำหรับคำค้นหาที่สะกดผิด ใช้อาร์กิวเมนต์ distance
ที่เป็นทางเลือกซึ่งเป็นระยะทาง Levenshtein สูงสุดสำหรับคำแนะนำในการสะกด โดยจะส่งคืนอาร์เรย์โดยที่แต่ละองค์ประกอบประกอบด้วยคำแนะนำสำหรับคำค้นหาแต่ละคำ และคะแนนมาตรฐานตามการปรากฏในดัชนี
main ❯ index = RediSearch :: Index . new ( "user_idx" ) { text_field :name , phonetic : "dm:en" }
main ❯ index . spellcheck ( "jimy" )
RediSearch ( 1.1 ms ) FT . SPELLCHECK user_idx jimy DISTANCE 1
=> [ #<RediSearch::Spellcheck::Result:0x00007f805591c670
term : "jimy" ,
suggestions :
[ #<struct RediSearch::Spellcheck::Suggestion score=0.0006849315068493151, suggestion="jimmy">,
#<struct RediSearch::Spellcheck::Suggestion score=0.00019569471624266145, suggestion="jim">]>]
main ❯ index . spellcheck ( "jimy" , distance : 2 ) . first . suggestions
RediSearch ( 0.5 ms ) FT . SPELLCHECK user_idx jimy DISTANCE 2
=> [ #<struct RediSearch::Spellcheck::Suggestion score=0.0006849315068493151, suggestion="jimmy">,
#<struct RediSearch::Spellcheck::Suggestion score=0.00019569471624266145, suggestion="jim">]
การบูรณาการกับ Rails นั้นง่ายมาก! เรียก redi_search
ด้วยอาร์กิวเมนต์คีย์เวิร์ดส schema
มาจากภายในโมเดลของคุณ อดีต:
class User < ApplicationRecord
redi_search do
text_field :first , phonetic : "dm:en"
text_field :last , phonetic : "dm:en"
end
end
สิ่งนี้จะเพิ่มเมธอด User.search
และ User.spellcheck
โดยอัตโนมัติซึ่งทำงานเหมือนกับที่คุณเรียกใช้บน Index
ซ์ดัชนี
User.reindex(recreate: false, only: [])
จะถูกเพิ่มและทำงานคล้ายกับ RediSearch::Index#reindex
ความแตกต่างบางประการได้แก่:
Document
เป็นพารามิเตอร์แรก ขอบเขต search_import
จะถูกเรียกโดยอัตโนมัติ และบันทึกทั้งหมดจะถูกแปลงเป็น Document
sonly
ซึ่งคุณสามารถระบุฟิลด์จำนวนจำกัดที่จะอัปเดตได้ มีประโยชน์หากคุณแก้ไขสคีมาและต้องการจัดทำดัชนีเฉพาะฟิลด์ใดฟิลด์หนึ่งเท่านั้น ในขณะที่กำหนดสคีมา คุณสามารถเลือกส่งผ่านบล็อกได้ หากไม่มีการส่งผ่านบล็อก name
จะถูกเรียกบนโมเดลเพื่อรับค่า หากบล็อกถูกส่งผ่าน ค่าของฟิลด์จะได้รับจากการเรียกบล็อก
class User < ApplicationRecord
redi_search do
text_field :name do
" #{ first_name } #{ last_name } "
end
end
end
คุณสามารถแทนที่ขอบเขต search_import
บนโมเดลเพื่อกระตือรือร้นในการโหลดความสัมพันธ์เมื่อจัดทำดัชนี หรือสามารถใช้เพื่อจำกัดเรกคอร์ดให้จัดทำดัชนีได้
class User < ApplicationRecord
scope :search_import , -> { includes ( :posts ) }
end
เมื่อค้นหา ตามค่าเริ่มต้น ชุด Document
จะถูกส่งกลับ การเรียก #results
ในคำค้นหาจะดำเนินการค้นหา จากนั้นค้นหาระเบียนที่พบทั้งหมดในฐานข้อมูลและส่งกลับความสัมพันธ์ ActiveRecord
ชื่อ Index
เริ่มต้นสำหรับโมเดล Index
s คือ #{model_name.plural}_#{RediSearch.env}
วิธี redi_search
ใช้อาร์กิวเมนต์ที่เป็นทางเลือก index_prefix
ซึ่งจะถูกเติมหน้าชื่อดัชนี:
class User < ApplicationRecord
redi_search index_prefix : 'prefix' do
text_field :first , phonetic : "dm:en"
text_field :last , phonetic : "dm:en"
end
end
User . search_index . name
# => prefix_users_development
เมื่อรวม RediSearch เข้ากับโมเดล บันทึกจะถูกสร้างดัชนีโดยอัตโนมัติหลังจากการสร้างและอัปเดต และจะถูกลบออกจาก Index
เมื่อถูกทำลาย
มีวิธีการอำนวยความสะดวกอีกสองสามวิธีที่เปิดเผยต่อสาธารณะ:
search_document
RediSearch::Document
remove_from_index
Index
add_to_index
Index
search_index
RediSearch::Index
หลังจากตรวจสอบ repo แล้ว ให้รัน bin/setup
เพื่อติดตั้งการขึ้นต่อกัน จากนั้น รัน rake test
เพื่อรันทั้งการทดสอบหน่วยและการทดสอบการรวม หากต้องการรันทีละรายการ คุณสามารถรัน rake test:unit
หรือ rake test:integration
คุณยังสามารถเรียกใช้ bin/console
เพื่อรับข้อความโต้ตอบที่จะช่วยให้คุณสามารถทดลองได้
หากต้องการติดตั้ง gem นี้ลงในเครื่องของคุณ ให้รัน bundle exec rake install
หากต้องการออกเวอร์ชันใหม่ ให้ดำเนินการ bin/publish (major|minor|patch)
ซึ่งจะอัปเดตหมายเลขเวอร์ชันใน version.rb
สร้างแท็ก git สำหรับเวอร์ชัน กด git commits และแท็ก และพุชไฟล์ .gem
ไปที่ rubygems .org และ GitHub
ยินดีรายงานข้อผิดพลาดและคำขอดึงข้อมูลบน GitHub โครงการนี้มีวัตถุประสงค์เพื่อเป็นพื้นที่ที่ปลอดภัยและเป็นมิตรสำหรับการทำงานร่วมกัน และผู้มีส่วนร่วมจะต้องปฏิบัติตามจรรยาบรรณของผู้ร่วมให้ข้อมูล
อัญมณีนี้มีให้ใช้งานในรูปแบบโอเพ่นซอร์สภายใต้เงื่อนไขของใบอนุญาต MIT
ทุกคนที่โต้ตอบในโค้ดเบส เครื่องมือติดตามปัญหา ห้องสนทนา และรายชื่ออีเมลของโครงการ RediSearch จะต้องปฏิบัติตามหลักจรรยาบรรณ