[javascript] 의미있는 자바 스크립트 퍼지 검색

배열을 필터링하기 위해 퍼지 검색 JavaScript 라이브러리를 찾고 있습니다. fuzzyset.jsfuse.js 사용을 시도 했지만 결과가 끔찍합니다 (링크 된 페이지에서 시도 할 수있는 데모가 있습니다).

Levenshtein 거리에 대해 읽은 후 사용자가 입력 할 때 찾는 내용에 대한 근사치가 나쁘다는 생각이 듭니다. 모르는 사람들을 위해 시스템 은 두 개의 문자열을 일치시키는 데 필요한 삽입 , 삭제대체 수를 계산합니다 .

Levenshtein-Demerau 모델에서 수정 한 명백한 결점은 양이다 BluB 효소가슴이 똑같이 유사한 고려 전구 (각각 두 개의 치환을 요구하는). 그러나, 분명 전구는 더 유사하다 BluB 효소 보다 가슴이 , 그리고 내가 방금 언급 한 모델을 가능하게하여 해당 인식 트랜스 포지션 .

나는 이것을 텍스트 완성의 맥락에서 사용하고 싶다. 그래서 내가 array를 가지고 ['international', 'splint', 'tinder']있고 내 쿼리가 int 이라면 , 전자가 10 점 (higher = worse)을 가지고 있더라도 internationalsplint 보다 더 높은 순위를 가져야 한다고 생각 한다. 후자의 3.

그래서 내가 찾고있는 것은 (존재하지 않으면 만들 것입니다) 다음을 수행하는 라이브러리입니다.

  • 다양한 텍스트 조작에 가중치 적용
  • 단어에 나타나는 위치에 따라 각 조작에 가중치를 부여합니다 (초기 조작은 늦은 조작보다 비용이 많이 듭니다).
  • 관련성별로 정렬 된 결과 목록을 반환합니다.

이런 걸 본 사람이 있습니까? StackOverflow가 소프트웨어 권장 사항을 요청하는 곳이 아니라는 것을 알고 있지만 위의 암시 적 (더 이상은 아닙니다!)은 다음과 같습니다. 올바른 방식으로 생각하고 있습니까?


편집하다

주제에 대한 좋은 논문 (pdf) 을 찾았습니다 . 일부 참고 및 발췌 :

Affine 편집 거리 기능은 일련의 삽입 또는 삭제에 상대적으로 낮은 비용을 할당합니다.

특정 비용 매개 변수가있는 Smith-Waterman 거리 함수 (Durban et al. 1998)의 유사 변형 인 Monger-Elkan 거리 함수 (Monge & Elkan 1996)

들어 스미스 – 워터맨 거리 (위키) “대신에 전체 시퀀스를 찾고, 스미스 – 워터맨 알고리즘은 모든 가능한 길이의 세그먼트를 비교하여 유사도 측정치를 최적화한다.” 그것은 n-gram 접근 방식입니다.

편집 거리 모델을 기반으로하지 않는 광범위하게 유사한 메트릭은 Jaro 메트릭입니다 (Jaro 1995; 1989; Winkler 1999). 레코드-연결 문헌에서 두 문자열 사이의 공통 문자 수와 순서를 기반으로하는이 방법의 변형을 사용하여 좋은 결과를 얻었습니다.

Winkler (1999)로 인한 변형은 또한 가장 긴 공통 접두사의 길이 P를 사용합니다.

(주로 짧은 문자열을위한 것으로 보임)

텍스트 완성을 위해 Monger-Elkan 및 Jaro-Winkler 접근 방식이 가장 합리적입니다. Jaro 메트릭에 Winkler의 추가는 효과적으로 단어의 시작 부분에 더 많은 가중치를 부여합니다. 그리고 Monger-Elkan의 affine 측면은 단어를 완성해야하는 필요성 (단순히 추가되는 순서)이 너무 많이 싫어하지 않을 것임을 의미합니다.

결론:

TFIDF 순위는 여러 토큰 기반 거리 메트릭 중에서 가장 잘 수행되었으며 Monge와 Elkan이 제안한 조정 된 affine-gap 편집 거리 메트릭은 여러 문자열 편집-거리 메트릭 중에서 가장 잘 수행되었습니다. 놀랍게도 좋은 거리 측정법은 Jaro가 제안하고 나중에 Winkler가 확장 한 빠른 휴리스틱 방식입니다. 이것은 Monge-Elkan 방식과 거의 비슷하지만 훨씬 더 빠릅니다. TFIDF 방법과 Jaro-Winkler를 결합하는 간단한 방법 중 하나는 TFIDF에서 사용되는 정확한 토큰 일치를 Jaro-Winkler 체계를 기반으로하는 대략적인 토큰 일치로 바꾸는 것입니다. 이 조합은 평균적으로 Jaro-Winkler 또는 TFIDF보다 약간 더 나은 성능을 제공하며 때로는 훨씬 더 나은 성능을 제공합니다. 또한이 백서에서 고려한 몇 가지 최고의 메트릭의 학습 된 조합에 가깝습니다.



답변

좋은 질문! 그러나 제 생각에는 Levenshtein-Demerau를 수정하는 것보다 다른 알고리즘을 시도하거나 두 알고리즘의 결과를 결합 / 가중치는 것이 더 나을 수 있습니다.

“시작 접두사”와 정확히 일치하거나 근접한 일치는 Levenshtein-Demerau가 특별한 가중치를주지 않는 것입니다.

나는 “Levenshtein보다 더 나은”을 검색했고, 무엇보다도 이것을 발견했다 :

http://www.joyofdata.de/blog/comparison-of-string-distance-algorithms/

이것은 많은 “문자열 거리”측정을 언급합니다. 귀하의 요구 사항과 특히 관련이있는 세 가지는 다음과 같습니다.

  1. 가장 긴 공통 부분 문자열 거리 : 결과 부분 문자열이 동일 할 때까지 두 문자열에서 제거해야하는 최소 기호 수입니다.

  2. q- 그램 거리 : 두 문자열의 N- 그램 벡터 간의 절대 차이의 합계입니다.

  3. Jaccard 거리 : 1 분 공유 N- 그램과 관찰 된 모든 N- 그램의 몫입니다.

Levenshtein (공통 하위 문자열, 공통 N-gram 또는 Jaccard는 모두 유사한 문자열을 강력하게 선호 함) 함께 이러한 메트릭의 가중치 조합 (또는 최소값)을 사용할 수 있습니다. 아니면 Jaccard를 사용해보십시오.

목록 / 데이터베이스의 크기에 따라 이러한 알고리즘은 다소 비쌀 수 있습니다. 필자가 구현 한 퍼지 검색의 경우 구성 가능한 수의 N- 그램을 DB에서 “검색 키”로 사용한 다음 값 비싼 문자열 거리 측정을 실행하여 선호하는 순서대로 정렬했습니다.

SQL의 퍼지 문자열 검색에 대한 메모를 작성했습니다. 보다:


답변

나는 fuse.js와 같은 기존의 퍼지 라이브러리를 사용해 보았고 또한 그것들이 끔찍하다는 것을 알았 기 때문에 기본적으로 숭고한 검색과 같이 작동하는 라이브러리를 작성했습니다. https://github.com/farzher/fuzzysort

허용되는 유일한 오타는 전치입니다. 그것은 꽤 고체이다 (1K 별, 0 문제) , 매우 빨리 , 쉽게 케이스를 처리합니다

fuzzysort.go('int', ['international', 'splint', 'tinder'])
// [{highlighted: '*int*ernational', score: 10}, {highlighted: 'spl*int*', socre: 3003}]


답변

여기 제가 몇 번 사용한 기술이 있습니다 … 꽤 좋은 결과를 제공합니다. 그래도 요청한 모든 것을 수행하지는 않습니다. 또한 목록이 방대하면 비용이 많이들 수 있습니다.

get_bigrams = (string) ->
    s = string.toLowerCase()
    v = new Array(s.length - 1)
    for i in [0..v.length] by 1
        v[i] = s.slice(i, i + 2)
    return v

string_similarity = (str1, str2) ->
    if str1.length > 0 and str2.length > 0
        pairs1 = get_bigrams(str1)
        pairs2 = get_bigrams(str2)
        union = pairs1.length + pairs2.length
        hit_count = 0
        for x in pairs1
            for y in pairs2
                if x is y
                    hit_count++
        if hit_count > 0
            return ((2.0 * hit_count) / union)
    return 0.0

에 두 개의 문자열 패스 string_similarity사이의 숫자를 반환하는 0그리고 1.0그들이 얼마나 비슷한에 따라. 이 예에서는 Lo-Dash를 사용합니다.

사용 예 ….

query = 'jenny Jackson'
names = ['John Jackson', 'Jack Johnson', 'Jerry Smith', 'Jenny Smith']

results = []
for name in names
    relevance = string_similarity(query, name)
    obj = {name: name, relevance: relevance}
    results.push(obj)

results = _.first(_.sortBy(results, 'relevance').reverse(), 10)

console.log results

또한 .. 바이올린을

본체가 열려 있는지 확인하십시오. 그렇지 않으면 아무것도 표시되지 않습니다. 🙂


답변

이것은 퍼지 매치에 대한 짧고 간결한 기능입니다.

function fuzzyMatch(pattern, str) {
  pattern = '.*' + pattern.split('').join('.*') + '.*';
  const re = new RegExp(pattern);
  return re.test(str);
}


답변

Atom의 https://github.com/atom/fuzzaldrin/lib를 살펴볼 수 있습니다 .

npm에서 사용할 수 있고 간단한 API가 있으며 저에게 적합합니다.

> fuzzaldrin.filter(['international', 'splint', 'tinder'], 'int');
< ["international", "splint"]


답변

2019 년 11 월 업데이트. 나는 퓨즈가 꽤 괜찮은 업그레이드를 발견했습니다. 그러나 bool (예 : OR, AND 등의 연산자)을 사용하거나 API 검색 인터페이스를 사용하여 결과를 필터링 할 수 없습니다.

나는 발견했다 nextapps-de/flexsearch: https://github.com/nextapps-de/flexsearch 그리고 나는 그것이 내가 시도한 다른 많은 자바 스크립트 검색 라이브러리를 훨씬 능가한다고 믿으며 bool, 검색 필터링 및 페이지 매김을 지원합니다.

검색 데이터 (예 : 스토리지)에 대한 자바 스크립트 개체 목록을 입력 할 수 있으며 API는 매우 잘 문서화되어 있습니다. https://github.com/nextapps-de/flexsearch#api-overview

지금까지 10,000 개에 가까운 레코드를 인덱싱했으며 내 검색은 바로 옆에 있습니다. 즉, 각 검색에 대해 눈에 띄지 않는 시간.


답변

여기 @InternalFX에서 제공하는 솔루션이 있지만 JS에서 (나는 공유하기 위해 사용했습니다) :

function get_bigrams(string){
  var s = string.toLowerCase()
  var v = s.split('');
  for(var i=0; i<v.length; i++){ v[i] = s.slice(i, i + 2); }
  return v;
}

function string_similarity(str1, str2){
  if(str1.length>0 && str2.length>0){
    var pairs1 = get_bigrams(str1);
    var pairs2 = get_bigrams(str2);
    var union = pairs1.length + pairs2.length;
    var hits = 0;
    for(var x=0; x<pairs1.length; x++){
      for(var y=0; y<pairs2.length; y++){
        if(pairs1[x]==pairs2[y]) hits++;
    }}
    if(hits>0) return ((2.0 * hits) / union);
  }
  return 0.0
}