[Οδηγός] Script Αναζήτησης string με βαθμολογία!

Σε αυτή την περιοχή μπορείτε να βρείτε ή να αναζητήσετε πληροφορίες σχετικές με την PHP

Συντονιστές: WebDev Moderators, Super-Moderators, PHP Moderators

Απάντηση
Άβαταρ μέλους
giannis17
Honorary Member
Δημοσιεύσεις: 1215
Εγγραφή: 06 Ιαν 2005 19:50
Τοποθεσία: Παγκράτι - Αθήνα
Επικοινωνία:

[Οδηγός] Script Αναζήτησης string με βαθμολογία!

Δημοσίευση από giannis17 » 31 Δεκ 2014 20:56

Επειδή κάποια στιγμή θα μου χρειαστεί και καθώς βλέπω δεν υπάρχει κάποια έτοιμη παρόμοια λύση σας δίνω ένα script για να κάνετε αναζητήσεις σε δεδομένα μέσω php βαθμολογώντας κάθε λήμμα/λέξη ξεχωριστά. Δουλεύει σε όλες τις γλώσσες εκτός από Αραβικά/Ασιατικά/Εβραϊκά, δηλαδή σε όσες βασίζονται στο λατινικό αλφάβητο + τα ελληνικά.

Ποια είναι η λογική και σε τι μας βοηθάει;

Η λογική είναι πως η κάθε λέξη είτε του search string είτε των δεδομένων μας αποτελεί ξεχωριστή οντότητα και πρέπει να λαμβάνονται υπ' όψιν όλες οι περιπτώσεις ταυτοποίησης αλλά επίσης να δίνονται και εναλλακτικές (μήπως εννοούσατε...).

Μας βοηθάει πάρα πολύ κατ' αρχάς στην περίπτωση που παίρνουμε τα δεδομένα από κάποιο API και δεν έχουμε πρόσβαση στην βάση για να τρέξουμε ερωτήματα. Επίσης βοηθάει πολύ τις ανορθόγραφες αναζητήσεις (ακόμα και με greeklish) αλλά και τις αναζητήσεις που τα λήμματα δεν είναι γραμμένα στην σωστή σειρά ή ακόμα δεν είναι γραμμένα από την αρχή (πχ εμένα vs μένα πιάνεται σχεδόν ίδιο κάτι που σε full text compare δεν γίνεται).

Σίγουρα μπορεί να υλοποιηθεί καλύτερα αλλά είναι μια πολύ καλή βάση για να το βελτιώσετε και να το χρησιμοποιήσετε στις PHP εφαρμογές σας.

Κώδικας: Επιλογή όλων

<?php
header&#40; 'Content-Type&#58; text/html; charset=utf-8' &#41;;
if&#40;!empty&#40;$_POST&#91;'search'&#93;&#41;&#41;&#123;
$data = &#40;array&#41;"Ό,τι κι αν πεις σε εμένα θα γυρνάς!";
//τα $data θα μπορούσε να έρχονται από κάποιο ερώτημα σε βάση δεδομένων ή σε API αλλά για να τρέξει το script πρέπει να έχει μορφή μονοδιάστατου πίνακα. Συνήθως τα δεδομένα μας έχουν πολλά πεδία, σε αυτή την περίπτωση κρατάμε το text field σε ένα καινούριο array απλά διατηρώντας τα ίδια index
/*
δεν χρειάζεται σε αυτό το παράδειγμα αλλά καλό είναι να περιορίζουμε τους χαρακτήρες των data string
π.χ. Ας βάλουμε όριο 500 χαρακτήρες
	function limitdata&#40;$data&#41;&#123;
		foreach&#40;$data as $string&#41;&#123;
			if&#40;strlen&#40;$string&#41; > 500&#41; $string = mb_substr&#40;$string, 0, 500&#41;;
		&#125;
	&#125;
*/
$searchwords = $_POST&#91;'search'&#93;;

function special2latin&#40;$string&#41; &#123; //αντικαθιστούμε τους ειδικούς λατινικούς χαρακτήρες
   $table = array&#40;
        '&#192;'=>'A', '&#193;'=>'A', '&#194;'=>'A', '&#195;'=>'A', '&#196;'=>'A', '&#197;'=>'A', '&#258;'=>'A', '&#256;'=>'A', '&#260;'=>'A', '&#198;'=>'A', '&#508;'=>'A',
        '&#224;'=>'a', '&#225;'=>'a', '&#226;'=>'a', '&#227;'=>'a', '&#228;'=>'a', '&#229;'=>'a', '&#259;'=>'a', '&#257;'=>'a', '&#261;'=>'a', '&#230;'=>'a', '&#509;'=>'a',
        '&#222;'=>'B', '&#254;'=>'b', '&#223;'=>'Ss',
        '&#199;'=>'C', '&#268;'=>'C', '&#262;'=>'C', '&#264;'=>'C', '&#266;'=>'C',
        '&#231;'=>'c', '&#269;'=>'c', '&#263;'=>'c', '&#265;'=>'c', '&#267;'=>'c',
        '&#272;'=>'Dj', '&#270;'=>'D', '&#272;'=>'D',
        '&#273;'=>'dj', '&#271;'=>'d',
        '&#200;'=>'E', '&#201;'=>'E', '&#202;'=>'E', '&#203;'=>'E', '&#276;'=>'E', '&#274;'=>'E', '&#280;'=>'E', '&#278;'=>'E',
        '&#232;'=>'e', '&#233;'=>'e', '&#234;'=>'e', '&#235;'=>'e', '&#277;'=>'e', '&#275;'=>'e', '&#281;'=>'e', '&#279;'=>'e',
        '&#284;'=>'G', '&#286;'=>'G', '&#288;'=>'G', '&#290;'=>'G',
        '&#285;'=>'g', '&#287;'=>'g', '&#289;'=>'g', '&#291;'=>'g',
        '&#292;'=>'H', '&#294;'=>'H',
        '&#293;'=>'h', '&#295;'=>'h',
        '&#204;'=>'I', '&#205;'=>'I', '&#206;'=>'I', '&#207;'=>'I', '&#304;'=>'I', '&#296;'=>'I', '&#298;'=>'I', '&#300;'=>'I', '&#302;'=>'I',
        '&#236;'=>'i', '&#237;'=>'i', '&#238;'=>'i', '&#239;'=>'i', '&#303;'=>'i', '&#297;'=>'i', '&#299;'=>'i', '&#301;'=>'i', '&#305;'=>'i',
        '&#308;'=>'J',
        '&#309;'=>'j',
        '&#310;'=>'K',
        '&#311;'=>'k', '&#312;'=>'k',
        '&#313;'=>'L', '&#315;'=>'L', '&#317;'=>'L', '&#319;'=>'L', '&#321;'=>'L',
        '&#314;'=>'l', '&#316;'=>'l', '&#318;'=>'l', '&#320;'=>'l', '&#322;'=>'l',
        '&#209;'=>'N', '&#323;'=>'N', '&#327;'=>'N', '&#325;'=>'N', '&#330;'=>'N',
        '&#241;'=>'n', '&#324;'=>'n', '&#328;'=>'n', '&#326;'=>'n', '&#331;'=>'n', '&#329;'=>'n',
        '&#210;'=>'O', '&#211;'=>'O', '&#212;'=>'O', '&#213;'=>'O', '&#214;'=>'O', '&#216;'=>'O', '&#332;'=>'O', '&#334;'=>'O', '&#336;'=>'O', '&#338;'=>'O',
        '&#242;'=>'o', '&#243;'=>'o', '&#244;'=>'o', '&#245;'=>'o', '&#246;'=>'o', '&#248;'=>'o', '&#333;'=>'o', '&#335;'=>'o', '&#337;'=>'o', '&#339;'=>'o', '&#240;'=>'o',
        '&#340;'=>'R', '&#344;'=>'R',
        '&#341;'=>'r', '&#345;'=>'r', '&#343;'=>'r',
        '&#352;'=>'S', '&#348;'=>'S', '&#346;'=>'S', '&#350;'=>'S',
        '&#353;'=>'s', '&#349;'=>'s', '&#347;'=>'s', '&#351;'=>'s',
        '&#358;'=>'T', '&#354;'=>'T', '&#356;'=>'T',
        '&#359;'=>'t', '&#355;'=>'t', '&#357;'=>'t',
        '&#217;'=>'U', '&#218;'=>'U', '&#219;'=>'U', '&#220;'=>'U', '&#360;'=>'U', '&#362;'=>'U', '&#364;'=>'U', '&#366;'=>'U', '&#368;'=>'U', '&#370;'=>'U',
        '&#249;'=>'u', '&#250;'=>'u', '&#251;'=>'u', '&#252;'=>'u', '&#361;'=>'u', '&#363;'=>'u', '&#365;'=>'u', '&#367;'=>'u', '&#369;'=>'u', '&#371;'=>'u',
        '&#372;'=>'W', '&#7808;'=>'W', '&#7810;'=>'W', '&#7812;'=>'W',
        '&#373;'=>'w', '&#7809;'=>'w', '&#7811;'=>'w', '&#7813;'=>'w',
        '&#221;'=>'Y', '&#376;'=>'Y', '&#374;'=>'Y',
        '&#253;'=>'y', '&#255;'=>'y', '&#375;'=>'y',
        '&#381;'=>'Z', '&#377;'=>'Z', '&#379;'=>'Z', '&#381;'=>'Z',
        '&#382;'=>'z', '&#378;'=>'z', '&#380;'=>'z', '&#382;'=>'z',
        '"'=>'"', '"'=>'"', '‘'=>"'", '’'=>"'", '&#8226;'=>' ', '&#8230;'=>' ', '&#8212;'=>' ', '&#8211;'=>' ', '&#191;'=>' ', '&#161;'=>' ', '.' => ' ', ',' => ' ', '!' => ' ', '?' => ' ', ';' => ' '
	&#41;;
    $string = strtr&#40; $string, $table &#41;;
    return $string;
&#125;

function greek2latin&#40;$string&#41; &#123; //μετατρέπουμε τα ελληνικά σε greeklish
	$table = array&#40;'ΐ'=>'i','Α'=>'A','Β'=>'B','Γ'=>'G','Δ'=>'D','Ε'=>'E','Ζ'=>'Z','Η'=>'I','Θ'=>'TH','Ι'=>'I','Κ'=>'K','Λ'=>'L','Μ'=>'M','Ν'=>'N','Ξ'=>'X','Ο'=>'O','Π'=>'P','Ρ'=>'R','Σ'=>'S','Τ'=>'T','Υ'=>'Y','Φ'=>'F','Χ'=>'CH','Ψ'=>'PS','Ω'=>'O','Ϊ'=>'I','Ϋ'=>'Y','ά'=>'a','έ'=>'e','ή'=>'i','ί'=>'i','ΰ'=>'y','α'=>'a','β'=>'b','γ'=>'g','δ'=>'d','ε'=>'e','ζ'=>'z','η'=>'i','θ'=>'th','ι'=>'i','κ'=>'k','λ'=>'l','μ'=>'m','ν'=>'n','ξ'=>'x','ο'=>'o','π'=>'p','ρ'=>'r','ς'=>'s','σ'=>'s','τ'=>'t','υ'=>'y','φ'=>'f','χ'=>'ch','ψ'=>'ps','ω'=>'o','ϊ'=>'i','ϋ'=>'y','ό'=>'o','ύ'=>'y','ώ'=>'o', 'ει'=>'i', 'εί'=>'i', 'υι' =>'i', 'οι'=>'i', 'οί'=>'i', 'αι'=>'e', 'αί'=>'e'&#41;;
	$string = strtr&#40;$string, $table&#41;;
	return $string;
&#125;

function stringrank&#40;$searchwords, $words&#41;&#123; //εδώ γίνεται η σύγκριση
	$rank = 0;
	$meter = 0;
	foreach&#40;$searchwords as $search&#41;&#123; //συγκρίνουμε κάθε λέξη του λήμματος
		foreach&#40;$words as $word&#41;&#123; //με κάθε λέξη του data object
			similar_text&#40;$search, $word, $match&#41;; //παίρνουμε το ποσοστό ομοιότητας
			if&#40;$match > 40&#41;&#123; //Άμα η λέξη είναι λιγότερο από 40% όμοια θεωρούμε πως βρίσκεται σε λάθος σειρά και την προσπερνάμε, εδώ θα μπορούσε να υλοποιηθεί καλύτερα
				$rank += $match;
				$meter++;
			&#125;
		&#125;
	&#125;
	return $rank;
&#125;

function wordsplit&#40;$string&#41;&#123; //Χωρίζουμε τα string σε πίνακες λέξεων και αφαιρούμε τις κοινές ελληνικές και αγγλικές λέξεις &#40;προσοχή έχουν ήδη μετατραπεί σε greeklish&#41;
	$array = explode&#40;" ", $string&#41;;
	$common = array&#40;'se', 'ston', 'stin', 'sto', 'stis', 'sti', 'gia', 'tha', 'to', 'ti', 'ton', 'tin', 'ta', 'tou', 'tis', 'ke', 'ki', 'an', 'on', 'in', 'at', 'for', 'and', 'if'&#41;;
	$array = array_diff&#40;$array, $common&#41;;
	return $array;
&#125;

//τελειώσαμε λοιπόν με τις συναρτήσεις και πάμε στην εκτέλεση

//έλεγχος αν η αναζήτηση πρέπει να κάνει διαχωρισμό μικρών-κεφαλαίων αλλιώς τα κάνουμε όλα μικρά
if&#40;empty&#40;$_POST&#91;'case_sensitive'&#93;&#41;&#41;&#123; 
	$searchwords = strtolower&#40;$searchwords&#41;;
	foreach&#40;$data as $cand&#41;&#123; //words may have multiple rows
		$cand = strtolower&#40;$cand&#41;;
	&#125;
&#125;

//τώρα κάνουμε τη μετατροπή των ειδικών χαρακτήρων και ελληνικών ενώ ταυτόχρονα εκτελούμε και το split
$searchwords = special2latin&#40;$searchwords&#41;;
$searchwords = greek2latin&#40;$searchwords&#41;;
$searcharray = wordsplit&#40;$searchwords&#41;;
$candidates = array&#40;&#41;;
foreach&#40;$data as $cand&#41;&#123;
	$cand = special2latin&#40;$cand&#41;;
	$cand = greek2latin&#40;$cand&#41;;
	$candidates&#91;&#93; = wordsplit&#40;$cand&#41;;
&#125;

//ώρα να κάνουμε την σύγκριση
$result1 = Array&#40;&#41;; // θα κρατάει τα αποτελέσματα με δείκτη >80%
$result2 = Array&#40;&#41;; // θα κρατάει τα αποτελέσματα με δείκτη >40%
foreach&#40;$candidates as $key => $cand&#41;&#123; //χρησιμοποιούμε το $key γιατί δεν μας ενδιαφέρει το string που έχει μετατραπεί σε greeklish αλλά το αρχικό οπότε έχουμε το $key σαν δείκτη
	if&#40;sizeof&#40;$searcharray&#41; != 0 &#41;&#123; //εδώ θέλουμε να πάρουμε σωστό ποσοστό ανάλογα με τις λέξεις που αναζητήθηκαν οπότε αν είναι πολλές παίρνουμε το μέσο όρο
		$match = stringrank&#40;$searcharray, $cand&#41; / sizeof&#40;$searcharray&#41;;
	&#125; else &#123;
		$match = stringrank&#40;$searcharray, $cand&#41;;
	&#125;
	//print_r&#40;$match&#41;; αν θέλουμε να δούμε τι ποσοστό είχε η συγκεκριμένη αναζήτηση
	switch&#40;true&#41;&#123;
		case &#40;$match >= 80&#41;&#58;
			$result1&#91;&#93; = $key;
			break;
		case &#40;$match >= 40&#41;&#58;
			$result2&#91;&#93; = $key;
			break;
		default&#58;
			break;
	&#125;
&#125;
&#125;
?>
<html>
	<head>
		<title>PHP Search Script</title>
	</head>
	<body>
		<form method="POST">
			<input name="search" type="text" value="" placeholder="Αναζήτηση..." maxlength="100">
			<input type="checkbox" name="case_sensitive" value="on" title="Case Sensitive">
			<input type="submit" value="Ψάξε τώρα">
		</form>
		<div id="results">
		<?php
		if&#40;!empty&#40;$_POST&#91;'search'&#93;&#41;&#41;&#123;
			echo'<h1>Αποτελέσματα για&#58; <i>'.$_POST&#91;'search'&#93;.'</i></h1>';
			if&#40;sizeof&#40;$result1 > 0&#41;&#41; &#123;
				foreach&#40;$result1 as $sure&#41;&#123;
					echo '<span class="result">'.$data&#91;$sure&#93;.'</span>';
				&#125;
			&#125; else echo '<span class="result">Δεν βρέθηκαν αποτελέσματα!</span>';
			if&#40;sizeof&#40;$result2 > 0&#41;&#41; &#123;
				echo'<h3>Μήπως εννοούσατε...</h3>';
				foreach&#40;$result2 as $maybe&#41;&#123;
					echo '<span class="result">'.$data&#91;$maybe&#93;.'</span>';
				&#125;
			&#125;
		&#125;
		?>
		</div>
	</body>
</html>
Για όσους δεν το πρόσεξαν σαν $data δίνω μόνο ένα string "Ό,τι κι αν πεις σε εμένα θα γυρνάς!" για να δοκιμάσετε πως όντως τις κοινές λέξεις (αν, κι, σε) δεν τις μετράει καθόλου και πως οι μεγαλύτερες λέξεις έχουν πιο μεγάλη αξία από τις μικρές.

Σχόλια στα ελληνικά υπάρχουν μέσα στον κώδικα.

Ελπίζω να φανεί χρήσιμο. Καλή χρονιά σε όλους!

Υ.Γ. sorry που κάνει scroll δεξιά τα πιο πολλά σχόλια είναι //1-line και το έκανα επικόλληση από το php αρχείο. Αν μπορεί κάποιος moderator ας το διορθώσει.
"There is only one problem with common sense; it’s not very common."
&#8211; Milt Bryce

Άβαταρ μέλους
korgr
Honorary Member
Δημοσιεύσεις: 5067
Εγγραφή: 07 Οκτ 2008 18:30
Τοποθεσία: Corinth
Επικοινωνία:

[Οδηγός] Script Αναζήτησης string με βαθμολογία!

Δημοσίευση από korgr » 01 Ιαν 2015 11:35

Ευχαριστούμε και καλή χρονιά εύχομαι! :)

alou
Script Master
Δημοσιεύσεις: 1374
Εγγραφή: 24 Αύγ 2007 19:52
Επικοινωνία:

[Οδηγός] Script Αναζήτησης string με βαθμολογία!

Δημοσίευση από alou » 02 Ιαν 2015 01:03

Καλή χρονιά και ευχαριστούμε Γιάννη :D

Ρίξε και μια ματιά στην levenshtein, είναι πολύ χρήσιμη για αυτό που θες να πετύχεις και αφήνει πολλά περιθώρια χειρισμού.

Απάντηση

Επιστροφή στο “PHP Προγραμματισμός”

Μέλη σε σύνδεση

Μέλη σε αυτήν τη Δ. Συζήτηση: Δεν υπάρχουν εγγεγραμμένα μέλη και 3 επισκέπτες