PHP / MySQL dizi arama algoritması

3 Cevap php

I'd like to be able to use php search an array (or better yet, a column of a mysql table) for a particular string. However, my goal is for it to return the string it finds and the number of matching characters (in the right order) or some other way to see how reasonable the search results are, so then I can make use of that info to decide if I want to display the top result by default or give the user options of the top few. I know I can do something like

$citysearch = mysql_query("  SELECT city FROM $table WHERE city LIKE '$city' ");

ama ne kadar doğru olduğunu belirlemek için bir yol anlamaya olamaz.

The goal would be:
a) find "Milwaukee" if the search term were "milwakee" or something similar.
b) if the search term were "west", return things like "West Bend" and "Westmont".

Herkes bunu yapmak için iyi bir yol biliyor musun?

3 Cevap

Daha fazla arama yapmak için en iyi yol olduğunu kanıtladı, similar_text sonra Levenshtein mesafe götürdü ve.

similar_text("input string", "match against this", $pct_accuracy);

dizeleri karşılaştırır ve daha sonra bir değişken olarak doğruluğunu kaydeder. Levenshtein mesafe, (örn. bunu değiştirmek için daha fazla mal yapabilirsiniz eklemek, ya da farklı her işlevi ağırlıklandırılarak için bir ödenek ile, diğer bir dize almak için yapmanız gerekir tek bir karakter üzerinde işlevleri yerine kaç silme belirler Bir karakteri silmek için daha bir karakter). Bu görünüşe göre daha hızlı ama daha az doğru similar_text daha bulunuyor. Ben başka bir yerde okudum diğer mesajlar az 10000 karakter dizeleri için, hız fonksiyonel fark olduğunu belirtmiştik.

Ben o iş yapmak için bulunan bir şey değiştirilmiş bir sürümünü kullanarak sona erdi. Bu (tam eşleşme durumu hariç) ilk 3 sonuç tasarruf biter.

$input = $_POST["searchcity"];
$accuracy = 0;
$runner1acc = 0;
$runner2acc = 0;
while ($cityarr = mysql_fetch_row($allcities)) {
  $cityname = $cityarr[1];
  $cityid = $cityarr[0];
  $city = strtolower($cityname);
  $diff = similar_text($input, $city, $tempacc);

  // check for an exact match
  if ($tempacc == '100') {

    // closest word is this one (exact match)
    $closest = $cityname;
    $closestid = $cityid;
    $accuracy = 100;

    break;
  }

  if ($tempacc >= $accuracy) { // more accurate than current leader
    $runner2 = $runner1;
    $runner2id = $runner1id;
    $runner2acc = $runner1acc;
    $runner1 = $closest;
    $runner1id = $closestid;
    $runner1acc = $accuracy;
    $closest  = $cityname;
    $closestid = $cityid;
    $accuracy = $tempacc;
  }
  if (($tempacc < $accuracy)&&($tempacc >= $runner1acc)) { // new 2nd place
    $runner2 = $runner1;
    $runner2id = $runner1id;
    $runner2acc = $runner1acc;
    $runner1 = $cityname;
    $runner1id = $cityid;
    $runner1acc = $tempacc;
  }
  if (($tempacc < $runner1acc)&&($tempacc >= $runner2acc)) { // new 3rd place
    $runner2 = $cityname;
    $runner2id = $cityid;
    $runner2acc = $tempacc;
  }
}

echo "Input word: $input\n<BR>";
if ($accuracy == 100) {
  echo "Exact match found: $closestid $closest\n";
} elseif ($accuracy > 70) { // for high accuracies, assumes that it's correct
  echo "We think you meant $closestid $closest ($accuracy)\n";
} else {
  echo "Did you mean:<BR>";
  echo "$closestid $closest? ($accuracy)<BR>\n";
  echo "$runner1id $runner1 ($runner1acc)<BR>\n";
  echo "$runner2id $runner2 ($runner2acc)<BR>\n";
}

Sen full text searching MySQL kontrol etmelisiniz. Ayrıca Apache Lucene projenin zerdüştlerin noktasını kontrol, Zend_Search_Lucene.

Bu çok karmaşık olabilir, ve ben onlar var eminim, ancak ben herhangi bir iyi 3 parti kütüphaneleri şahsen farkında değilim. Diğerleri olsa da, bazı konserve çözümler önermek mümkün olabilir.

Ben geçmişte sıfırdan benzer bir şey bir kaç kez yazdım. Eğer bu rota aşağı giderseniz, muhtemelen her sorgu tüm kayıtları almak ve onlar üzerinde hesaplamalar gerçekleştirmek içerecektir olarak kendi PHP yapmak istediğimiz bir şey değildir. Neredeyse kesinlikle özelliklerini karşılamak indeks tabloları kümesi oluşturarak içerecektir.

Örneğin, size "Milwaukee" yazıldığından bitebileceğini düşünün nasıl kuralları ile gelmek gerekir "milwakee." Bu benim çözüm (bu aslında arama terimlerini ise emin değil) ünlü sıkıştırma ve çoğaltılması sıkıştırma yapmak oldu. : Yani, Milwaukee olarak endeksli olacaktır

  • Milwaukee
  • m_lw__k__
  • m_lw_k_

Arama sorgusu "Milwaukee" için geldi, ben metin girişi üzerinde aynı işlemi çalıştırın ve sonra için dizin masaya bir arayacaksınız olacaktır:

SELECT cityId,
       COUNT(*)
  FROM myCityIndexTable
 WHERE term IN ('Milwaukee', 'm_lw__k__', 'm_lw_k_')

Arama sorgusu "milwakee" için geldi, ben metin girişi üzerinde aynı işlemi çalıştırın ve sonra için dizin masaya bir arayacaksınız olacaktır:

SELECT cityId,
       COUNT(*)
  FROM myCityIndexTable
 WHERE term IN ('Milwaukee', 'm_lw_k__', 'm_lw_k_')

Milwaukee durumunda (doğru yazıldığından), bu sayımı için "3" dönecekti.

(Sadece ortada bir sesli harf vardı gibi m_lw__k__ desen maç olmaz çünkü) Milwakee (yanlış yazıldığından) durumunda, bu sayımı için "2" dönecekti.

Eğer sayısı dayalı sonuçlarını sıralamak ise, "Milwaukee" daha olası bir eşleşme olarak yüksek Sıralanan sona ereceğini, toplantı senin kurallarından biri sona ereceğini "Milwakee."

Eğer (sorguda $table kullanımınız ima gibi) genel bir şekilde bu sistemi kurmak istiyorsanız o zaman muhtemelen uygun masaya koşullarınızı haritasına orada bir yerde başka bir eşleme tablosu gerekiyordu.

Ben bu iyi (hatta iyi) bu, ben denemek ve bir üçüncü taraf çözüm olmadan bunu yapmak planlıyorsanız sizin için faydalı olabilir geçmişte yaptık sadece bir şey hakkında gitmek için yoludur demiyorum.