programing

유사한 결과를 찾고 유사도를 기준으로 정렬하는 방법은 무엇입니까?

projobs 2022. 10. 21. 22:05
반응형

유사한 결과를 찾고 유사도를 기준으로 정렬하는 방법은 무엇입니까?

유사성에 따라 정렬된 레코드를 조회하려면 어떻게 해야 합니까?

예: "Stock Overflow"를 검색하면 반환됩니다.

  1. 스택 오버플로
  2. SharePoint 오버플로우
  3. 산술 오버플로
  4. 정치 오버플로
  5. VFX 오버플로

예: "LO"를 검색하면 다음과 같이 반환됩니다.

  1. 파블로 피카소
  2. 미켈란지LO
  3. 잭슨 폴잠그다

도움이 필요한 것:

  1. 검색 엔진을 사용한 MySQL 테이블 색인 및 검색으로 더 나은 결과를 얻을 수 있습니다.

    • PHP와 함께 Sphinx 검색 엔진 사용

    • PHP와 함께 Lucene 엔진 사용

  2. 전체 텍스트 색인을 사용하여 유사한/포함 문자열 찾기


무엇이 잘 작동하지 않는가

  • Levenshtein 거리는 매우 불규칙합니다. (UDF, Query)
    "개"를 검색하면 다음과 같은 이점이 있습니다.
    1. 수렁
    2. 전에
    3. 큰.
    4. 메아리치다
  • LIKE는 더 나은 결과를 반환하지만 유사한 문자열이 존재하지만 긴 쿼리에 대해서는 아무것도 반환하지 않습니다.
    1. 도그리드
    2. 도가랄
    3. 도그마

Levenshtein 거리는 다른 풀스트링에 대해 풀스트링을 검색하는 경우 양호하지만 문자열 내에서 키워드를 검색할 경우 이 메서드는 원하는 결과를 반환하지 않을 수 있습니다(경우에 따라서는).또한 SOUNDEX 기능은 영어 이외의 언어에는 적합하지 않기 때문에 상당히 제한적입니다.LIKE로 빠져나갈 수는 있지만, 실제로는 기본적인 검색을 위한 것입니다.원하는 검색 방법을 찾아보는 것이 좋습니다.예를 들어 다음과 같습니다.

프로젝트 검색 기반으로 Lucene을 사용할 수 있습니다.대부분의 주요 프로그래밍 언어로 구현되어 있어 매우 빠르고 다재다능합니다.이 방법은 서브스트링뿐만 아니라 문자 이동, 접두사 및 서픽스(모두 조합)도 검색하기 때문에 아마도 가장 적합합니다.단, 별도의 인덱스를 보관해야 합니다(CRON을 사용하여 독립 스크립트에서 업데이트해도 가끔 작동합니다).

또한 MySQL 솔루션을 원하는 경우 전체 텍스트 기능이 매우 우수하며 저장 프로시저보다 더 빠릅니다.테이블이 MyISAM이 아닌 경우 임시 테이블을 생성하여 풀텍스트 검색을 수행할 수 있습니다.

CREATE TABLE IF NOT EXISTS `tests`.`data_table` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `title` varchar(2000) CHARACTER SET latin1 NOT NULL,
  `description` text CHARACTER SET latin1 NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB  DEFAULT CHARSET=utf8 COLLATE=utf8_bin AUTO_INCREMENT=1 ;

직접 생성하지 않으려면 데이터 생성기를 사용하여 랜덤 데이터를 생성하십시오.

** 메모 ** : 컬럼 타입은 다음과 같습니다.latin1_binlatin1은 " " "를 합니다.utf8_bin하여 '''를 합니다.utf8_general_ci이치노

DROP TABLE IF EXISTS `tests`.`data_table_temp`;
CREATE TEMPORARY TABLE `tests`.`data_table_temp`
   SELECT * FROM `tests`.`data_table`;

ALTER TABLE `tests`.`data_table_temp`  ENGINE = MYISAM;

ALTER TABLE `tests`.`data_table_temp` ADD FULLTEXT `FTK_title_description` (
  `title` ,
  `description`
);

SELECT *,
       MATCH (`title`,`description`)
       AGAINST ('+so* +nullam lorem' IN BOOLEAN MODE) as `score`
  FROM `tests`.`data_table_temp`
 WHERE MATCH (`title`,`description`)
       AGAINST ('+so* +nullam lorem' IN BOOLEAN MODE)
 ORDER BY `score` DESC;

DROP TABLE `tests`.`data_table_temp`;

자세한 내용은 MySQL API 참조 페이지를 참조하십시오.

이것의 단점은 문자 이동이나 "비슷한, 비슷한" 단어들을 찾지 않는다는 것이다.

** 업데이트 **

검색에 Lucene을 사용하여 cron 작업(모든 웹 호스트에는 이 "기능"이 있음)을 생성하기만 하면 됩니다.이 작업에서는 인덱스를 갱신하는 PHP 스크립트(예를 들어 "cd /path/to/script; php searchindexer.php")를 실행할 수 있습니다.수천 개의 "문서"(행, 데이터 등)를 인덱싱하는 데 몇 초, 심지어 몇 분 정도 걸릴 수 있지만, 이는 모든 검색이 가능한 한 빨리 수행되도록 하기 위함입니다.따라서 서버에 의해 실행되는 지연 작업을 생성할 수 있습니다.하룻밤이 될 수도 있고, 한 시간 뒤면 당신에게 달렸어요.PHP 스크립트는 다음과 같습니다.

$indexer = Zend_Search_Lucene::create('/path/to/lucene/data');

Zend_Search_Lucene_Analysis_Analyzer::setDefault(
  // change this option for your need
  new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive()
);

$rowSet = getDataRowSet();  // perform your SQL query to fetch whatever you need to index
foreach ($rowSet as $row) {
   $doc = new Zend_Search_Lucene_Document();
   $doc->addField(Zend_Search_Lucene_Field::text('field1', $row->field1, 'utf-8'))
       ->addField(Zend_Search_Lucene_Field::text('field2', $row->field2, 'utf-8'))
       ->addField(Zend_Search_Lucene_Field::unIndexed('someValue', $someVariable))
       ->addField(Zend_Search_Lucene_Field::unIndexed('someObj', serialize($obj), 'utf-8'))
  ;
  $indexer->addDocument($doc);
}

// ... you can get as many $rowSet as you want and create as many documents
// as you wish... each document doesn't necessarily need the same fields...
// Lucene is pretty flexible on this

$indexer->optimize();  // do this every time you add more data to you indexer...
$indexer->commit();    // finalize the process

다음으로 기본적으로 다음과 같이 검색합니다(기본 검색).

$index = Zend_Search_Lucene::open('/path/to/lucene/data');

// same search options
Zend_Search_Lucene_Analysis_Analyzer::setDefault(
   new Zend_Search_Lucene_Analysis_Analyzer_Common_Utf8Num_CaseInsensitive()
);

Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('utf-8');

$query = 'php +field1:foo';  // search for the word 'php' in any field,
                                 // +search for 'foo' in field 'field1'

$hits = $index->find($query);

$numHits = count($hits);
foreach ($hits as $hit) {
   $score = $hit->score;  // the hit weight
   $field1 = $hit->field1;
   // etc.
}

Java, PHP Lucene에 대한 훌륭한 사이트를 소개합니다.그물

결론적으로 각 검색 방법에는 각각 장단점이 있습니다.

  • 방금 스핑크스 검색을 언급하셨는데, 웹 호스트에서 deamon을 실행할 수 있으면 매우 좋습니다.
  • Zend Lucene을 사용하려면 cron 작업이 필요합니다.사용자에게 매우 투명하지만 새 데이터(또는 삭제된 데이터!)가 항상 데이터베이스의 데이터와 동기화되지 않으므로 사용자 검색에 즉시 표시되지 않습니다.
  • MySQL FULLTEXT 검색은 훌륭하고 빠르지만 처음 두 검색의 성능과 유연성을 모두 제공하지는 않습니다.

잊어버린 것, 놓친 것이 있으면 언제든지 코멘트를 주세요.

1. 유사성

MySQL의 Levenshtein의 경우 www.codejanitor.com/wp/2007/02/10/levenshtein-distance-as-a-mysql-stored-function에서 찾았습니다.

SELECT 
    column, 
    LEVENSHTEIN(column, 'search_string') AS distance 
FROM table 
WHERE 
    LEVENSHTEIN(column, 'search_string') < distance_limit
ORDER BY distance DESC

2. 격납, 대소문자 구분 없음

하다를 사용하세요.LIKE기본적으로 대소문자를 구분하지 않는 MySQL의 스테이트먼트입니다.%에, 「Wildcard」의 수 .search_string.

SELECT 
    *
FROM 
    table
WHERE 
    column_name LIKE "%search_string%"

3. 포함, 대소문자 구분

MySQL 매뉴얼은 다음과 같은 이점을 제공합니다.

기본 문자 세트와 조합은 latin1 및 latin1_swedish_ci이므로 비바이너리 문자열 비교에서는 기본적으로 대소문자가 구분되지 않습니다.즉, col_name LIKE 'a%'로 검색하면 A 또는 a로 시작하는 모든 열 값을 얻을 수 있습니다.이 검색에서 대소문자를 구분하려면 피연산자 중 하나에 대소문자를 구분하거나 바이너리 조회가 있는지 확인하십시오.예를 들어 latin1 문자 세트를 가진 열과 문자열을 비교하는 경우 COLATE 연산자를 사용하여 피연산자 중 하나에 latin1_general_cs 또는 latin1_bin 대조...를 설정할 수 있습니다.

프로그램이 MySQL을 .latin1_general_cs ★★★★★★★★★★★★★★★★★」latin1_bin, collation, collation, collation을 하여 잘 utf8_bin합니다.

SELECT 
    *
FROM 
    table
WHERE 
    column_name LIKE "%search_string%" COLLATE utf8_bin

2. / 3. Levenshtein 거리별로 정렬

SELECT 
    column, 
    LEVENSHTEIN(column, 'search_string') AS distance // for sorting
FROM table 
WHERE 
    column_name LIKE "%search_string%"
    COLLATE utf8_bin // for case sensitivity, just leave out for CI
ORDER BY
    distance
    DESC

유사성에 대한 당신의 정의는 의미적 유사성인 것 같습니다.따라서 이러한 유사함수를 구축하기 위해서는 의미적 유사성 측정을 사용해야 합니다.이 문제에 대한 작업 범위는 몇 시간에서 몇 년까지 다를 수 있으므로 작업을 시작하기 전에 범위를 결정하는 것이 좋습니다.유사성 관계를 구축하기 위해 어떤 데이터를 가지고 있는지 파악하지 못했습니다.문서 데이터 집합과 쿼리 데이터 집합에 액세스할 수 있을 것입니다.단어의 동시 발생부터 시작할 수 있습니다(예: 조건부 확률).당신은 단지 그들이 매우 인기가 있기 때문에 당신이 대부분의 단어와 관련된 스톱워드 목록을 얻는다는 것을 금방 알게 될 것이다.조건부 확률의 리프트를 사용하면 정지어는 처리되지만 적은 수(대부분의 경우)의 관계에서 오류가 발생하기 쉽습니다.Jacard를 시도해 볼 수도 있지만 대칭이기 때문에 찾을 수 없는 많은 관계가 있을 것입니다.그런 다음 기본 단어에서 짧은 거리에만 나타나는 관계를 고려할 수 있습니다.일반적인 말뭉치(Wikipedia 등)와 사용자별(예: 이메일)을 기반으로 관계를 고려할 수 있습니다(또한 고려해야 합니다).

모든 측정값이 양호하고 다른 측정값보다 어느 정도 유리할 때 유사성 측정값이 많이 나올 것입니다.

그러한 대책을 조합하기 위해서, 나는 그 문제를 분류의 문제로 좁히고 싶다.

단어 집합의 데이터 집합을 작성하고 "관련됨"으로 레이블을 지정해야 합니다.라벨이 붙은 대규모 데이터 세트를 작성하려면 다음 작업을 수행합니다.

  • 긍정의 경우 알려진 관련 단어 출처(예: 이전 Wikipedia 범주)를 사용합니다.
  • 관련되지 않은 단어들은 대부분 관련이 없다.

그런 다음 가지고 있는 모든 측도를 쌍의 피쳐로 사용합니다.이제 당신은 분류 문제의 영역에 있습니다.데이터 세트에 분류기를 작성하고 필요에 따라 평가하여 필요에 맞는 유사성 측정값을 얻을 수 있습니다.

언급URL : https://stackoverflow.com/questions/3338889/how-to-find-similar-results-and-sort-by-similarity

반응형