경고
보석은 현재 유지되지 않았으며 개발이 보류됩니다. 인계에 관심이 있으시면 언제든지 저에게 연락하십시오.
Ruby 스크립트에 대한 빠른 텍스트 검색이 필요하지만 Solr 및 Elasticsearch는 과잉입니까? ?
당신은 올바른 장소에 있습니다. Tantiny는 Tanti V Y (Rust로 작성된 Apache Lucene의 멋진 대안)을 기반으로 Ruby의 최소한의 전체 텍스트 검색 라이브러리입니다. 당면한 작업이 전체 텍스트 검색이 필요한 경우에 좋습니다. 그러나 본격적인 분산 검색 엔진을 구성하는 데는 작업 자체보다 시간이 더 걸립니다. 그리고 프로젝트에서 이미 그러한 엔진을 사용하더라도 (실제로는 가능성이 높습니다) Solr 및 Elasticsearch와 달리 작동하는 것이 필요하지 않기 때문에 (별도의 서버 나 프로세스가 없음), 순전히 임베드 가능합니다. 따라서 선택한 검색 엔진을 사용할 때 상황에서 자신을 발견하면 까다 롭거나 불일치하거나 추가 설정이 필요할 때 항상 유연하고 빠른 빠르고 더러운 솔루션으로 되돌릴 수 있습니다.
Tantiny는 Tantivy에 대한 Ruby의 바인딩은 아니지만 가까이 다가 가려고합니다. 주요 철학은 Tantivy의 거꾸로 된 색인에 대한 낮은 수준의 액세스를 제공하지만 멋진 Ruby-esque API, 현명한 불이행 및 추가 기능이 상단에 뿌려집니다.
가장 기본적인 예를 살펴보십시오.
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 이 라인을 응용 프로그램의 보석에 추가하십시오.
gem "tantiny"그런 다음 실행 :
$ bundle install
또는 직접 설치하십시오.
$ gem install tantiny
Tantiny는 설치 중에 Github 릴리스에서 호스팅 된 사전 컴파일 된 바이너리를 다운로드하려고 시도하기 때문에 시스템에 Rust를 설치할 필요가 없습니다. 그러나 시스템에 미리 컴파일 된 바이너리가 발견되지 않은 경우 (플랫폼, 아키텍처 및 루비 버전의 조합) 먼저 Rust를 설치해야합니다.
경고
최대 1.77 의 Rust 버전 만 지원됩니다. 자세한 내용은이 문제를 참조하십시오.
중요한
tantiny 에 대한 의존성을 선언 할 때는 Minor 버전을 지정하십시오. 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가 저장된 위치를 지정합니다 (기본값으로 :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 개의 가장 잘 어울리는 문서의 ID를 반환하지만 사용자 정의 할 수 있습니다.
index . search ( "a drunk, a kid, and an old man" , limit : 100 ) 검색을 정확히 어떻게 수행합니까? 기본 동작은 스키마에 정의 된 모든 text 필드에서 smart_query 검색 (자세한 내용은 아래 참조)을 사용하는 것입니다. 따라서 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 거리 내에 지정된 용어가 포함 된 문서. |
| 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 검색은 해당 필드 토큰 화제를 사용하여 쿼리 문자열에서 용어를 추출하고 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 패턴을 받아들이지 만 Ruby Regexp 가 아니라 Rust Regex 여야합니다. 따라서 index.regex_query(:description, /hel[lp]/) 대신 index.regex_query(:description, "hel[lp]") 사용해야합니다. 참고로, regex_query 는 내부적으로 FST 상자를 사용하기 때문에 매우 빠릅니다.
그래서 우리는 이미 두 번 이상 토큰 화제를 언급했습니다. 그들은 무엇입니까?
토큰 화제는 Tantivy가 텍스트를 사용하여 반전 된 색인을 구축하기 위해 사용하는 것입니다. 그런 다음이 용어로 색인을 검색 할 수 있습니다. index.term_query(:description, "Hello") hello Hello 아니기 때문에 아무것도 반환하지 않으면 혼란스러워하지 않도록 이해하는 것이 중요한 개념입니다. 인덱스를 검색하기 전에 쿼리에서 용어를 추출해야합니다. 현재 smart_query 만이 당신을 위해 그렇게합니다. 또한 토큰 화 된 유일한 필드 유형은 text 이므로 string 필드의 경우 정확한 일치를 사용해야합니다 (예 : index.term_query(:title, "Hello") ).
기본적으로 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 Tokenizers는 단순한 토큰 화기와 똑같지 만 지정된 언어 (영어 기본값)에 따라 추가 스템이 있습니다.
tokenizer = Tantiny :: Tokenizer . new ( :stemmer , language : :ru )
tokenizer . terms ( "Привет миру сему!" ) # ["привет", "мир", "сем"]어떤 언어가 지원되는지 확인하려면 소스를 살펴보십시오.
Ngram 토큰 화기는 지정된 크기의 Ngram에 텍스트를 자릅니다.
tokenizer = Tantiny :: Tokenizer . new ( :ngram , min : 5 , max : 10 , prefix_only : true )
tokenizer . terms ( "Morrowind" ) # ["Morro", "Morrow", "Morrowi", "Morrowin", "Morrowind"] search 방법이 문서 ID 만 반환한다는 것을 알 수 있습니다. 이것은 디자인에 의한 것입니다. 문서 자체는 색인에 저장되지 않습니다 . Tantiny는 최소한의 도서관이므로 물건을 단순하게 유지하려고합니다. 전체 문서를 검색 해야하는 경우 Redis와 함께 Key-Value Store를 사용하십시오.
레포를 확인한 후 bin/setup 실행하여 종속성을 설치하십시오. 그런 다음 rake build 실행하여 기본 확장을 구축 한 다음 rake spec 테스트를 실행하십시오. 실험 할 수있는 대화식 프롬프트를 위해 bin/console 실행할 수도 있습니다.
우리는 기존의 커밋을 사용하여 ChangeLog를 자동으로 생성하고 시맨틱 버전을 범하고 보석을 게시하고 출시합니다. 당신이해야 할 일은 컨벤션을 고수하기 만하면 CI는 당신을 위해 다른 모든 것을 돌볼 것입니다.
https://github.com/baygeldin/tantiny (https://github.com/baygeldin/tantiny)에서 버그 보고서 및 풀 요청은 환영합니다.
보석은 MIT 라이센스의 조건에 따라 오픈 소스로 제공됩니다.