it-swarm.com.de

Wie finde ich ähnliche Ergebnisse und sortiere nach Ähnlichkeit?

Wie frage ich nach Datensätzen, die nach Ähnlichkeit sortiert sind?

Z.B. Suche nach "Stock Overflow" würde zurückkehren

  1. Paketüberfluss
  2. SharePoint-Überlauf
  3. Math Overflow
  4. Politischer Überlauf
  5. VFX-Überlauf

Z.B. Suche nach "LO" würde zurückgeben:

  1. pablo Picasso
  2. michelangeLO
  3. jackson polLOck

Was ich brauche Hilfe bei:

  1. Verwenden einer Suchmaschine zum Indizieren und Durchsuchen einer MySQL-Tabelle, um bessere Ergebnisse zu erzielen

    • Verwendung der Sphinx Suchmaschine mit PHP

    • Verwendung der Lucene Engine mit PHP

  2. Verwenden der Volltextindizierung zum Suchen ähnlicher/enthaltender Zeichenfolgen


Was funktioniert nicht gut

  • Levenshtein Abstand ist sehr unberechenbar. ( UDF , Abfrage )
    Die Suche nach "Hund" gibt mir:
    1. hund
    2. moor
    3. vor
    4. groß
    5. echo
  • LIKE gibt bessere Ergebnisse zurück, gibt aber bei langen Abfragen nichts zurück, obwohl ähnliche Zeichenfolgen existieren
    1. hund
    2. dogid
    3. dogaral
    4. dogma
67
Robinicks

Ich habe herausgefunden, dass der Levenshtein-Abstand gut ist, wenn Sie eine vollständige Zeichenfolge mit einer anderen vollständigen Zeichenfolge vergleichen. Wenn Sie jedoch nach Schlüsselwörtern in einer Zeichenfolge suchen, gibt diese Methode (manchmal) nicht die gewünschten Ergebnisse zurück. Darüber hinaus ist die SOUNDEX-Funktion nicht für andere Sprachen als Englisch geeignet, sodass sie nur in sehr begrenztem Umfang zur Verfügung steht. Sie könnten mit LIKE davonkommen, aber es ist wirklich für einfache Suchanfragen. Möglicherweise möchten Sie nach anderen Suchmethoden suchen, um herauszufinden, was Sie erreichen möchten. Beispielsweise:

Sie können Lucene als Suchbasis für Ihre Projekte verwenden. Es ist in den meisten wichtigen Programmiersprachen implementiert und sehr schnell und vielseitig. Diese Methode ist wahrscheinlich die beste, da sie nicht nur nach Teilzeichenfolgen sucht, sondern auch nach Buchstaben, Präfixen und Suffixen (alle kombiniert). Sie müssen jedoch einen separaten Index führen (mit CRON kann dieser jedoch gelegentlich von einem unabhängigen Skript aktualisiert werden).

Wenn Sie eine MySQL-Lösung wünschen, ist die Volltextfunktionalität ziemlich gut und mit Sicherheit schneller als eine gespeicherte Prozedur. Wenn Ihre Tabellen nicht MyISAM sind, können Sie eine temporäre Tabelle erstellen und dann Ihre Volltextsuche durchführen:

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 ;

Verwenden Sie einen Datengenerator , um zufällige Daten zu generieren, wenn Sie diese nicht selbst erstellen möchten ...

** [~ # ~] Anmerkung [~ # ~] **: Der Spaltentyp sollte latin1_bin sein, um Groß- und Kleinschreibung zu berücksichtigen Suche statt Groß-/Kleinschreibung mit latin1. Für Unicode-Zeichenfolgen würde ich utf8_bin Für Suchvorgänge empfehlen, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird, und utf8_general_ci Für Suchvorgänge, bei denen die Groß- und Kleinschreibung nicht berücksichtigt wird.

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`;

Weitere Informationen finden Sie auf der MySQL API-Referenzseite

Der Nachteil dabei ist, dass es nicht nach Buchstabenumsetzung oder "ähnlichen, klingenden" Wörtern aussieht.

** [~ # ~] Update [~ # ~] **

Wenn Sie Lucene für Ihre Suche verwenden, müssen Sie lediglich einen Cron-Job erstellen (alle Web-Hosts haben diese "Funktion"), in dem dieser Job einfach ein PHP-Skript (z. B. "cd/path/to/script") ausführt ; php searchindexer.php "), der die Indizes aktualisiert. Der Grund dafür ist, dass das Indizieren von Tausenden von "Dokumenten" (Zeilen, Daten usw.) einige Sekunden oder sogar Minuten dauern kann. Dies soll jedoch sicherstellen, dass alle Suchvorgänge so schnell wie möglich durchgeführt werden. Daher möchten Sie möglicherweise einen Verzögerungsjob erstellen, der vom Server ausgeführt wird. Es kann über Nacht sein oder in der nächsten Stunde liegt es an Ihnen. Das Skript PHP sollte ungefähr so ​​aussehen:

$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

Dann ist dies im Grunde, wie Sie suchen (einfache Suche):

$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.
}

Hier sind großartige Seiten über Lucene in Java , PHP und . Net .

Abschließend hat jede Suchmethode ihre eigenen Vor- und Nachteile:

  • Sie haben Sphinx-Suche erwähnt und es sieht sehr gut aus, solange Sie den Deamon auf Ihrem Web-Host ausführen können.
  • Zend Lucene benötigt einen Cron-Job, um die Datenbank neu zu indizieren. Dies ist zwar für den Benutzer ziemlich transparent, bedeutet jedoch, dass neue Daten (oder gelöschte Daten!) Nicht immer mit den Daten in Ihrer Datenbank synchronisiert sind und daher bei der Benutzersuche nicht sofort angezeigt werden.
  • Die MySQL FULLTEXT-Suche ist gut und schnell, bietet Ihnen jedoch nicht die Leistungsfähigkeit und Flexibilität der ersten beiden.

Bitte zögern Sie nicht zu kommentieren, wenn ich etwas vergessen/verpasst habe.

83
Yanick Rochon

1. Ähnlichkeit

Für Levenshtein in MySQL fand ich dies von 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. Enthalten, Groß-/Kleinschreibung beachten

Verwenden Sie die LIKE -Anweisung von MySQL, bei der standardmäßig die Groß- und Kleinschreibung nicht berücksichtigt wird. Der % Ist ein Platzhalter, daher kann es vor und nach search_string Eine beliebige Zeichenfolge geben.

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

3. Groß- und Kleinschreibung beachten

Das MySQL Manual hilft:

Der Standardzeichensatz und die Standardkollatierung sind latin1 und latin1_swedish_ci. Daher wird beim Vergleich von nichtbinären Zeichenfolgen standardmäßig die Groß- und Kleinschreibung nicht berücksichtigt. Das heißt, wenn Sie mit col_name LIKE 'a%' suchen, erhalten Sie alle Spaltenwerte, die mit A oder a beginnen. Um diese Suche zwischen Groß- und Kleinschreibung zu unterscheiden, stellen Sie sicher, dass einer der Operanden eine Sortierung zwischen Groß- und Kleinschreibung oder eine binäre Sortierung aufweist. Wenn Sie beispielsweise eine Spalte und eine Zeichenfolge vergleichen, die beide den Zeichensatz latin1 haben, können Sie mit dem Operator COLLATE bewirken, dass entweder der Operand die Sortierung latin1_general_cs oder latin1_bin hat ...

Mein MySQL-Setup unterstützt weder latin1_general_cs Noch latin1_bin, Aber es hat gut funktioniert, wenn ich die Kollatierung utf8_bin Verwende, da bei utf8 die Groß- und Kleinschreibung beachtet wird:

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

2./3. sortiert nach Levenshtein Distanz

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
21
opatut

Es scheint, dass Ihre Definition von Ähnlichkeit semantische Ähnlichkeit ist. Um eine solche Ähnlichkeitsfunktion zu erstellen, sollten Sie semantische Ähnlichkeitsmaße verwenden. Beachten Sie, dass der Umfang der Arbeit an dem Problem von einigen Stunden bis zu Jahren variieren kann. Es wird daher empfohlen, den Umfang festzulegen, bevor Sie mit der Arbeit beginnen. Ich habe nicht herausgefunden, über welche Daten Sie verfügen, um die Ähnlichkeitsrelation zu erstellen. Ich gehe davon aus, dass Sie Zugriff auf einen Datensatz von Dokumenten und einen Datensatz von Abfragen haben. Sie können mit dem gemeinsamen Auftreten der Wörter beginnen (z. B. bedingte Wahrscheinlichkeit). Sie werden schnell feststellen, dass Sie die Liste Stoppwörter erhalten, die die meisten der Wörter enthält, einfach weil sie sehr beliebt sind. Wenn Sie den Lift der bedingten Wahrscheinlichkeit verwenden, werden die Stoppwörter berücksichtigt, die Relation wird jedoch in geringer Anzahl fehleranfällig (in den meisten Fällen). Sie könnten versuchen Jacard aber da es symmetrisch ist, wird es viele Beziehungen geben, die es nicht findet. Dann können Sie Beziehungen berücksichtigen, die nur in geringer Entfernung vom Basiswort erscheinen. Sie können (und sollten) Beziehungen berücksichtigen, die auf allgemeinen Korpussen (z. B. Wikipedia) und benutzerspezifischen (z. B. seinen E-Mails) basieren.

In Kürze werden Sie viele Ähnlichkeitsmaße haben, wenn alle Maße gut sind und einige Vorteile gegenüber den anderen haben.

Um solche Maßnahmen zu kombinieren, möchte ich das Problem in ein Klassifizierungsproblem reduzieren.

Sie sollten einen Datensatz aus Paris von Wörtern erstellen und diese als "verwandt" kennzeichnen. Um einen großen beschrifteten Datensatz zu erstellen, können Sie:

  • Verwenden Sie Quellen bekannter verwandter Wörter (z. B. gute alte Wikipedia-Kategorien) für Positive
  • Die meisten Wörter, die nicht als verwandt bekannt sind, sind nicht verwandt.

Verwenden Sie dann alle Kennzahlen, die Sie als Merkmale der Paare haben. Jetzt befinden Sie sich in der Domäne des Problems der überwachten Klassifizierung. Erstellen Sie einen Klassifikator für den Datensatz, der gemäß Ihren Anforderungen ausgewertet wird, und ermitteln Sie ein Ähnlichkeitsmaß, das Ihren Anforderungen entspricht.

3
DaL