YC Vibe Check, linus.zone/entr 및 개인 생산성 소프트웨어와 같은 개인 프로젝트에서 사용되는 간단한 색인 없는 JavaScript용 텍스트 검색입니다. 내부적으로 어떻게 작동하는지 이해하려면 주석이 달린 소스를 읽어보세요.
몇 가지 간단한 예부터 시작해 보겠습니다.
import { search } from 'libsearch' ; // on Node.js
const { search } = window . libsearch ; // in the browser
const articles = [
{ title : 'Weather in Berkeley, California' } ,
{ title : 'University report: UC Berkeley' } ,
{ title : 'Berkeley students rise in solidarity...' } ,
{ title : 'Californian wildlife returning home' } ,
] ;
// basic usage
search ( articles , 'berkeley cali' , a => a . title ) ;
// => [{ title: 'Weather in Berkeley, California' }]
search ( articles , 'california' , a => a . title ) ;
// => [
// { title: 'Weather in Berkeley, California' },
// { title: 'Californian wildlife returning home' },
// ]
// mode: 'word' only returns whole-word matches
search ( articles , 'california' , a => a . title , { mode : 'word' } ) ;
// => [{ title: 'Weather in Berkeley, California' }]
// case sensitivity
search ( articles , 'W' , a => a . title , { caseSensitive : true } ) ;
// => [{ title: 'Weather in Berkeley, California' }]
// empty query returns the full list, unmodified
search ( articles , '' , a => a . title ) ;
// => [{...}, {...}, {...}, {...}]
보다 공식적으로 libsearch는 search
기능이라는 단일 API를 노출합니다. 이 함수는 두 개의 필수 인수와 두 개의 선택적 인수를 사용합니다.
function search < T > (
items : T [ ] ,
query : string ,
by ?: ( it : T ) => string ,
options ?: {
caseSensitive : boolean ,
mode : 'word' | 'prefix' | 'autocomplete' ,
} ,
) : T [ ]
items
검색할 항목의 목록입니다. 일반적으로 items
문자열 배열이거나 일부 문자열 속성이 있는 객체 배열입니다.query
항목 목록을 검색하는 데 사용되는 문자열 쿼리입니다.by
( 선택 사항 )는 items
에서 항목을 가져와 해당 항목을 검색하는 데 사용할 문자열 값을 반환하는 조건자 함수입니다. 예를 들어 items
{ name: 'Linus' }
와 같은 객체 목록인 경우 by
x => x.name
함수여야 합니다. 이는 기본적으로 x => String(x)
값을 가지며, 이는 string[]
유형의 items
에 대해 작동합니다.options
( 선택사항 )는 옵션의 사전입니다.caseSensitive
검색 시 대소문자를 구분합니다. 기본적으로는 false
입니다.mode
불완전한 쿼리 단어가 일치하는 방식을 제어합니다.mode: 'word'
는 모든 검색어가 단어의 일부가 아닌 완전하고 정확한 단어와만 일치해야 합니다. 예를 들어, "California"라는 검색어는 "University of California "와 일치하지만 " California n University"와는 일치하지 않습니다.mode: 'prefix'
모든 검색어가 일치하는 단어의 불완전한 "접두사"일 수 있음을 의미합니다. "Uni Cali"는 " Uni versity of Cali fornia" 및 " Cali fornian Uni versity"와 모두 일치합니다. 이 모드에서도 모든 쿼리 단어는 어딘가에서 일치해야 합니다. " Cali fornia"는 쿼리와 일치하지 않기 때문에 일치하지 않습니다. "유니"라는 단어.mode: 'autocomplete'
검색 결과가 반환될 때 사용자가 계속해서 쿼리를 입력하는 자동 완성 스타일 검색에 사용할 때 유용한 다른 두 가지 모드를 혼합한 것입니다. 이 모드는 mode: 'word'
와 동일합니다. 단, 마지막 쿼리 단어가 mode: 'prefix'
처럼 불완전할 수 있다는 점만 다릅니다. 이는 "University of Cali"가 " University of Cali fornia"와 일치한다는 의미입니다. 이는 사용자가 전체 쿼리를 입력하기 전에 일치하는 항목을 찾을 수 있기 때문에 유용합니다.단위 테스트에서 이러한 옵션이 어떻게 결합되는지에 대한 더 많은 예를 찾을 수 있습니다.
<script>
사용하여이것을 HTML에 추가하세요:
< script src =" https://unpkg.com/libsearch/dist/browser.js " > </ script >
그러면 search
기능이 window.libsearch.search
로 노출됩니다.
npm install libsearch
# or
yarn add libsearch
그리고 코드에서 사용하세요:
import { search } from 'libsearch' ;
// search(...);
libsearch는 소스 파일에서 생성된 TypeScript 유형 정의와 함께 제공됩니다. NPM에서 libsearch를 사용하면 TypeScript 컴파일러가 해당 항목을 선택해야 합니다.
libsearch를 사용하면 사전 구축된 검색 색인 없이도 JavaScript 개체 목록에 대해 기본 전체 텍스트 검색을 신속하게 수행할 수 있으며 결과에 대해 합리적으로 우수한 TF-IDF 순위를 제공할 수 있습니다. FlexSearch 및 lunr.js와 같은 라이브러리와 함께 제공되는 다양한 기능을 제공하지는 않지만 text.indexOf(query) > -1
보다 큰 발전을 이루었으며 온라인에서 수천 개의 문서를 검색하는 데 사용할 수 있을 만큼 충분히 빠릅니다. 내 경험의 모든 키 입력.
libsearch가 이를 제공하는 방법에는 두 가지 핵심 아이디어가 있습니다.
최신 JavaScript 엔진은 고도로 최적화된 정규식 엔진과 함께 제공되며, libsearch는 검색 시 쿼리 문자열을 정규식 필터로 변환하여 색인 없는 텍스트 검색을 빠르게 수행하는 데 이를 활용합니다.
대부분의 전체 텍스트 검색 라이브러리는 먼저 개발자에게 검색어가 나타나는 문서에 매핑되는 "인덱스" 데이터 구조를 구축하도록 요구함으로써 작동합니다. 이는 "검색"의 일부 계산 작업을 미리 수행하여 검색 자체를 빠르고 정확하게 유지할 수 있기 때문에 일반적으로 좋은 절충안입니다. 또한 검색 속도를 저하시키지 않으면서 인덱싱된 데이터에 대한 표제어 추출과 같은 고급 변환 및 데이터 정리가 가능합니다. 그러나 프로토타입과 간단한 웹 앱을 구축할 때 "충분히 좋은" 검색 솔루션을 얻기 위해 별도의 "인덱싱" 단계를 거치는 복잡성을 초래하고 싶지 않은 경우가 많았습니다. 인덱스는 어딘가에 저장되어야 하며 기본 데이터 세트가 변경되고 증가함에 따라 지속적으로 유지 관리되어야 합니다.
검색 색인의 주요 작업은 데이터 세트에 나타나는 "토큰" 또는 키워드를 해당 문서에 매핑하여 "어떤 문서에 X라는 단어가 포함되어 있습니까?"라는 질문을 하는 것입니다. 검색 시 응답이 빠릅니다( O(1)
). 색인이 없으면 모든 문서에서 키워드를 검색해야 하므로 이는 O(n)
질문으로 변합니다. 그러나 클라이언트 측 웹 앱에서 일반적으로 사용되는 충분히 작은 데이터 세트(몇 MB)의 경우 최신 하드웨어에서는 n
이 키 입력마다 O(n)
눈에 띄지 않을 만큼 작습니다.
libsearch는 "Uni of California"와 같은 쿼리를 정규식 필터 목록 (^|W)Uni($|W)
, (^|W)of($|W)
, (^|W)California
으로 변환합니다. (^|W)California
. 그런 다음 각 정규식을 통해 말뭉치를 필터링하여 색인 없이 "검색"합니다.
기존 TF-IDF 측정항목은 각 단어에 대해 다음과 같이 계산됩니다.
( # matches ) / ( # words in the doc ) * log ( # total docs / # docs that matched )
문서의 단어 수를 얻으려면 문서를 토큰화하거나 적어도 문서를 공백으로 분할해야 하는데, 이는 계산 비용이 많이 듭니다. 따라서 libsearch는 대신 문서의 길이(문자 수)를 사용하여 이를 대략적으로 계산합니다.
위에서 설명한 정규식 쿼리를 사용하면 libsearch의 TF-IDF 공식은 다음과 같습니다.
( # RegExp matches ) / ( doc . length ) * log ( # docs / # docs that matched RegExp )
이는 검색이 수행될 때 각 단어에 대해 계산된 다음 정렬을 위해 마지막에 집계됩니다.
libsearch의 소스 코드는 TypeScript로 작성되었습니다. TypeScript, 바닐라 Node.js 및 웹에서 라이브러리를 사용할 수 있도록 다음 두 가지 빌드를 컴파일합니다.
search.ts
유형 검사를 거쳐 유형이 제거된 ES 모듈 빌드입니다 . Node.js에서 libsearch
가져올 때 가져오는 코드입니다.search
기능을 window.libsearch
전역으로 내보내는 브라우저 빌드 ES 모듈 빌드는 TypeScript 컴파일러인 tsc
로 생성되며, 축소된 브라우저 빌드는 Webpack으로 추가로 생성됩니다.
NPM/원사 명령:
lint
및 fmt
- 저장소에서 소스 코드를 lint하고 자동으로 형식 지정test
라이브러리의 최신 빌드에서 단위 테스트를 실행합니다. test
실행하기 전에 build:tsc
실행해야 합니다.build:*
명령은 다양한 유형의 라이브러리 빌드 생성을 조정합니다.build:tsc
ES 모듈 빌드를 빌드합니다.build:w
모든 파일 쓰기에서 build:tsc
실행합니다.build:cjs
ES 모듈 빌드에서 브라우저 빌드를 빌드합니다.build:all
두 빌드를 순서대로 빌드합니다.clean
dist/
에서 생성/빌드 파일을 모두 제거합니다.docs
sephist.github.io/libsearch에 있는 Litterate 기반 문서를 구축합니다.메인이나 퍼블리싱으로 푸시하기 전에 저는 보통 다음을 실행합니다.
yarn fmt && yarn build:all && yarn test && yarn docs
내가 잊어버린 게 없는지 확인하기 위해서요.