MySQL Plugin in C - Fehler
-
Hallo,
aus Performance-Gründen habe ich für eine Suche ein MySQL Plugin geschrieben. Es funktioniert grundsätzlich, doch leider tretten unter Last auf dem DB Server eigenartige Fehler aus. Wenn man die Error-Logs ansieht, scheint es sich um einen Speicherüberlauf zu handeln.
Eines vorweg - ich bin Web-Developer und hab mit C eingentlich nicht viel am Hut - ergo wird eines sicher nicht ideal gelöst sein. Leider gibt es für mich keine andere Möglichkeit diese Berechnung schnell zu halten.
Zur Funktion des Plugins:
Es werden der Suchbegriff, der Titel des Inserats und Keywords übergeben. Anhand von gewissen Kriterien wird die Relevanz des jeweiligen Inserats berechnet und dieser Wert zurückgegeben. Somit ist es möglich die Inserate auf der Webseite nach Relevanz auszugeben.Fällt einem C-Profi hier ein offensichtlicher Fehler auf?
Danke im Voraus, LG
#ifdef STANDARD /* STANDARD is defined, don't use any mysql functions */ #include <string.h> #ifdef __WIN__ typedef unsigned __int64 ulonglong; /* Microsofts 64 bit types */ typedef __int64 longlong; #else typedef unsigned long long ulonglong; typedef long long longlong; #endif /*__WIN__*/ #else #include <my_global.h> #include <my_sys.h> #if defined(MYSQL_SERVER) #include <m_string.h> #else /* when compiled as standalone */ #include <string.h> #endif #endif #include <mysql.h> #include <ctype.h> #ifdef HAVE_DLOPEN my_bool relevance_init(UDF_INIT *initid, UDF_ARGS *args, char *message); void relevance_deinit(UDF_INIT *initid); longlong relevance(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error); longlong levenshtein_k_base(UDF_INIT *initid, char *str1, char *str2, int distance, char *is_null, char *error); typedef char** stringArray; stringArray MallocStringArray(size_t SizeOfOneString, size_t StringCount) { char** t=malloc(StringCount*sizeof(char*)); size_t i; for(i=0;i<StringCount;++i) t[i]=malloc(SizeOfOneString); return t; } void FreeStringArray(stringArray StringArray, size_t StringCount) { size_t i; for(i=0;i<StringCount;++i) free(StringArray[i]); free(StringArray); } // Ersetzt ein Zeichen um ein Anderes! Wichtig: Zeichenlänge muss gleich sein! void replace_character(char* string, char from, char to) { unsigned result = 0; if (!string) return; while (*string != '\0') { if (*string == from) { *string = to; result++; } string++; } } void cleanMemory(stringArray a_suchbegriffe, stringArray a_keywords, stringArray a_keywords_phrasen) { FreeStringArray(a_suchbegriffe, 30); FreeStringArray(a_keywords, 50); FreeStringArray(a_keywords_phrasen, 50); } void relevance_deinit(UDF_INIT *initid) { if (initid->ptr != NULL) { free(initid->ptr); } } my_bool relevance_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { if ((args->arg_count != 4) || (args->arg_type[0] != STRING_RESULT || args->arg_type[1] != STRING_RESULT || args->arg_type[2] != STRING_RESULT || args->arg_type[3] != INT_RESULT)) { strcpy(message, "Function requires 4 arguments, (string, string, string, int)"); return 1; } initid->maybe_null = 0; //doesn't return null return 0; } longlong relevance(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { if (args->args[0] == NULL || args->args[1] == NULL || args->args[2] == NULL || args->args[3] == NULL) return 0; int lv_distance = *(int *) args->args[3]; int lv_distance_tmp = 1; char suchbegriff[250]; *suchbegriff = 0; char titel[250]; *titel = 0; char keywords[1000]; *keywords = 0; if (args->lengths[0] < 1) return 0; strncat(suchbegriff,args->args[0],(args->lengths[0] > 250) ? 250 : args->lengths[0]); strncat(titel,args->args[1],(args->lengths[1] > 250) ? 250 : args->lengths[1]); strncat(keywords,args->args[2],(args->lengths[2] > 1000) ? 1000 : args->lengths[2]); char suchbegriff_gesamt[250] = ""; char keywords_phrasen[1000] = ""; strncpy(keywords_phrasen, keywords, 1000); // Keywords-Phrasen: Wörter durch Leerzeichen & Phrasen durch | getrennt replace_character(keywords, '|', ' '); // Keywords: Alle Wörter Leerzeichen getrennt float relevance_val = 0.0; int treffer_titel = 0; int treffer_keywords = 0; int treffer_titel_like = 0; int treffer_keywords_like = 0; stringArray a_suchbegriffe; a_suchbegriffe = MallocStringArray(250,30); stringArray a_keywords; a_keywords = MallocStringArray(250,50); stringArray a_keywords_phrasen; a_keywords_phrasen = MallocStringArray(250,50); // Suchbegriff spliten und in string Array speichern char delims[] = " "; char *result = NULL; int i = 0; int j = 0; int z = 0; int ignoreKeyword = 0; int countKeywords = 0; int max_index = -1; char suchbegriff_tmp[250]; strncpy(suchbegriff_tmp, suchbegriff, 250); result = strtok( suchbegriff_tmp, delims ); while( result != NULL ) { max_index++; //strcpy(a_suchbegriffe[i],result); strncpy(a_suchbegriffe[i],result, 250); strncat(suchbegriff_gesamt,result, strlen(result)); i++; if (i >= 30) break; result = strtok( NULL, delims ); } int treffer_titel_alt = 0; int phrase = 0; if (max_index == 0) { // Nur ein Suchbegriff phrase = 1; } // Titel durchsuchen -- max_index = Anzahl der Suchbegriffe j = 0; if (max_index >= 0) { i = 0; result = strtok( titel, delims ); while( result != NULL ) { for(i=0; i<=max_index; i++) { if (strlen(a_suchbegriffe[i]) <=3) { // Wenn Suchbegriff kurz direkter Vergleich (zB IT) sonst Levenshtein if (strcmp(a_suchbegriffe[i], result) == 0) { treffer_titel++; if (treffer_titel == (max_index+1)) { if (phrase == 1) { cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 100; } else { cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 75; } } } } else { if (strlen(a_suchbegriffe[i]) > 8) lv_distance_tmp = lv_distance+1; else lv_distance_tmp = lv_distance; if (levenshtein_k_base(initid, a_suchbegriffe[i], result, lv_distance_tmp, is_null, error) <= lv_distance_tmp) { treffer_titel++; if (treffer_titel == (max_index+1)) { if (phrase == 1) { cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 100; } else { cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 75; } } } else { //Like Suche if (strstr(result, a_suchbegriffe[i])) treffer_titel_like = 1; } } } // Logik ob die Phrase in einem durch gefunden wurde if (max_index > 0) { // Mehr als ein Suchbegriff if (treffer_titel > 0) { if ((treffer_titel+treffer_titel_like) > treffer_titel_alt) { phrase = 1; treffer_titel_alt++; } else { phrase = 0; treffer_titel_alt = 1000; } } } // Probieren ob der zusammengeschriebene Suchbegriff (ohne Leerzeichen) auf den Titel passt if (levenshtein_k_base(initid, suchbegriff_gesamt, result, lv_distance, is_null, error) <= lv_distance) { cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 100; } else { //Like Suche if (strstr(result, a_suchbegriffe[i])) { cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 75; } } result = strtok( NULL, delims ); if (j >= 30) break; j++; } } if ((max_index+1) <= (treffer_titel+treffer_titel_like)) { cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 75; } // Keywords durchsuchen - Phrasen char delims_phrases[] = "|"; result = NULL; j = 0; if (max_index >= 0) { result = strtok( keywords_phrasen, delims_phrases); while( result != NULL ) { if (strlen(suchbegriff) <=3) { if (strcmp(suchbegriff, result) == 0) { cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 75; } } else { if (levenshtein_k_base(initid, suchbegriff, result, lv_distance, is_null, error) <= lv_distance) { cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 75; } else if (strcmp(suchbegriff_gesamt, result) == 0) { cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 75; } } result = strtok( NULL, delims_phrases ); if (j >= 30) break; j++; } } int indexKeywords = 0; result = NULL; result = strtok( keywords, delims ); while( result != NULL ) { strncpy(a_keywords[indexKeywords],result, 249); if (indexKeywords >= 49) break; indexKeywords++; result = strtok( NULL, delims ); } //Keywords durchsuchen if (max_index >= 0) { for(i=0; i<=max_index; i++) { if (strlen(a_suchbegriffe[i]) > 8) lv_distance_tmp = lv_distance+1; else lv_distance_tmp = lv_distance; j = 0; for (j=0; j<=indexKeywords; j++) { if (strlen(a_suchbegriffe[i]) <=3) { // Wenn Suchbegriff kurz direkter Vergleich (zB IT) sonst Levenshtein if (strcmp(a_suchbegriffe[i], a_keywords[j]) == 0) { treffer_keywords++; if (treffer_keywords == (max_index+1)) { cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 50; } else break; } } else { if (levenshtein_k_base(initid, a_suchbegriffe[i], a_keywords[j], lv_distance_tmp, is_null, error) <= lv_distance_tmp) { treffer_keywords++; if (treffer_keywords == (max_index+1)) { cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 50; } else break; } } if (j >= 50) break; } } } cleanMemory(a_suchbegriffe, a_keywords, a_keywords_phrasen); return 1; } longlong levenshtein_k_base(UDF_INIT *initid, char *str1, char *str2, int distance, char *is_null, char *error) { // Code nicht relevant... } #endif /* HAVE_DLOPEN */
-
...
-
Swordfish schrieb:
Bist du sicher, daß das Array aus 116 im
strncat
in Zeile 157 immer groß genug ist!?Jap. Der Suchbegriff (= Variable "suchbegriff") kommt als Übergabgeparameter in die Funktion und wird auf 250 Zeichen gekürzt (sollte er länger sein).
Bei 157 wird dann einfach dieser Suchbegriff ohne Leerzeichen auf "suchbegriff_gesamt" geschrieben. "suchbegriff_gesamt" ist also entweder gleich groß oder kleiner als "suchbegriff".
Aber danke für den Hinweis.
Weiß jemand wie MySQL den Speicher wieder freigibt den die Funktion benötigt? Im schlimmsten Fall arbeitet die Funktion ja 1000 Datensätze ab und wenn jetzt 20 User zugleich eine Suche abschicken... Kann es damit zu tun haben?
Wenn ich das Programm lokal teste, also als ausführbare Datei (mit main Funktion), und den 3 Übergabeparametern (suchbegriff, titel, keywords) Testdaten zuweise, funktioniert nämlich alles. Ich weiß nicht mehr weiter
-
- Abbruchzeile rausfinden
- strncpy raus, dafür strncat oder sprintf %.*s
- strtok raus, dafür strsep
- for statt while
- assert einbauen
- symbolische Konstanten für Arraygrößen verwendenfor(i=0; i<=max_index; i++) { for (j=0; j<=indexKeywords; j++)
möchte höchstwahrscheinlich
for(i=0; i<max_index; i++) { for (j=0; j<indexKeywords; j++)
heißen.
if ((max_index+1) <= (treffer_titel+treffer_titel_like)) {
sieht sehr fragil aus.
-
rappit schrieb:
Swordfish schrieb:
Bist du sicher, daß das Array aus 116 im
strncat
in Zeile 157 immer groß genug ist!?Jap. Der Suchbegriff (= Variable "suchbegriff") kommt als Übergabgeparameter in die Funktion und wird auf 250 Zeichen gekürzt (sollte er länger sein).
Bei 157 wird dann einfach dieser Suchbegriff ohne Leerzeichen auf "suchbegriff_gesamt" geschrieben. "suchbegriff_gesamt" ist also entweder gleich groß oder kleiner als "suchbegriff".
Aber ein Klopfer ist die Zeile trotzdem noch. Das n von
strncat()
muss von der Restgröße des reservierten Ziel-Puffers abhängen, nicht von der Länge des kopierten strings.
-
Wutz schrieb:
- Abbruchzeile rausfinden
- strncpy raus, dafür strncat oder sprintf %.*s
- strtok raus, dafür strsep
- for statt while
- assert einbauen
- symbolische Konstanten für Arraygrößen verwendenfor(i=0; i<=max_index; i++) { for (j=0; j<=indexKeywords; j++)
möchte höchstwahrscheinlich
for(i=0; i<max_index; i++) { for (j=0; j<indexKeywords; j++)
heißen.
if ((max_index+1) <= (treffer_titel+treffer_titel_like)) {
sieht sehr fragil aus.
Danke erstmal für deine Antwort.
Das < Zeichen bei der Schleife passt schon - das ist der max. Index vom Array und nicht die Größe.Abbruchzeile rausfinden ist sowas. Wie gesagt - die Funktion funktioniert eigentlich richtig und gibt beim Ausführen auch die richtigen Werte zurück.
Der Fehler tritt nach dem 1000 Aufruf ca auf.
zB diese Testdaten:
Suchbegriff: msysql datenbank administrator
Titel des Inserats: datenbank administrator
Kewwords: mysql|sql|oracle|datenbank administrator|datenbankadministrator|db administrator|datenbank administration|datenbankadministration|plsql|db administration|datenbankverwaltungFunktioniert einwandfrei im Test. Beim x-Durchlauf sehe ich dann im sql-log (wenn ich mit printf die Daten mitschaue) bei der letzten Schleife (Keywords durchsuchen) dass er in den Variablen eigenartige Daten hat.
zB
Vergleich der Keywords mit dem Suchbegriff
mysql vs. mysql
mysql vs. sql
mysql vs. oracle
....
datenbank vs. mysql
...
datenbank vs. datenbankadministration
datenbank vs. datenbankadplsql (===> FEHLER: eigentlich plsql)Sowas passiert nach dem 1000 Aufruf ca. Auf einmal steht auf dem String noch der alte Wert und er überschreibt ihn. Aber nur wenn die DB unter starker Last steht...
Lg
-
Inzwischen kenn ich das Problem. Wenn zwei User gleichzeitig die Funktion aufrufen beginnt sich der Speicherbereich zu überschreiben und die Daten werden fehlerhaft.
Laut MySQL sollte man keinen lokalen Variablen verwenden. In der init-Funktion soll der benötigte Speicherbereich auf initd->ptr zugewiesen werden. Dieser erwartet sich (char
als Datentyp.
Leider sind meine C-Kenntnisse zu beschränkt damit ich weiß wie ich diese drei String-Arrays
stringArray a_suchbegriffe; a_suchbegriffe = MallocStringArray(250,30); stringArray a_keywords; a_keywords = MallocStringArray(250,50); stringArray a_keywords_phrasen; a_keywords_phrasen = MallocStringArray(250,50);
auf eine Ebene bringe und diese dann zu (char
umwandle damit ich sie in der Hauptfunktion mit Daten befüllen kann.
Kann mir jemand bitte helfen?
Danke LG
-
rappit schrieb:
Inzwischen kenn ich das Problem. Wenn zwei User gleichzeitig die Funktion aufrufen beginnt sich der Speicherbereich zu überschreiben und die Daten werden fehlerhaft.
Genau deswegen
Wutz schrieb:
- strtok raus, dafür strsep
weil strtok (in den allermeisten Implementierungen) prozessübergreifenden (statischen) Speicher verwendet.
strtok ist also per se nicht reentrant, es ist also völlig klar, dass dieses Ignorieren der Nichtreentranz irgendwann zu Fehlern führt.rappit schrieb:
Laut MySQL sollte man keinen lokalen Variablen verwenden.
Ja ja, ist klar. Die sehen sowas nicht gern. Ist aber IMHO in deinem Fall nicht vorrangig.
Gib mal die Definition von UDF_INIT/UDF_ARGS und ein paar charakteristische übergebene Testdaten an, ich schreibe dir dann mal auf, wie ich mir die Sache denke.
-
Wutz schrieb:
rappit schrieb:
Inzwischen kenn ich das Problem. Wenn zwei User gleichzeitig die Funktion aufrufen beginnt sich der Speicherbereich zu überschreiben und die Daten werden fehlerhaft.
Genau deswegen
Wutz schrieb:
- strtok raus, dafür strsep
weil strtok (in den allermeisten Implementierungen) prozessübergreifenden (statischen) Speicher verwendet.
strtok ist also per se nicht reentrant, es ist also völlig klar, dass dieses Ignorieren der Nichtreentranz irgendwann zu Fehlern führt.rappit schrieb:
Laut MySQL sollte man keinen lokalen Variablen verwenden.
Ja ja, ist klar. Die sehen sowas nicht gern. Ist aber IMHO in deinem Fall nicht vorrangig.
Gib mal die Definition von UDF_INIT/UDF_ARGS und ein paar charakteristische übergebene Testdaten an, ich schreibe dir dann mal auf, wie ich mir die Sache denke.
Danke mit der Begründung warum man strsep verwenden soll, machts jetzt Sinn
Vielen Dank!!
Zur Funktion
Aufruf über Mysql: SELECT *, relevance('C Entwickler', 'Software Developer', 'Software Entwickler|C Entwickler|Programmierer', 1) FROM inserat...
1. Parameter: Suchbegriff
2. Parameter: Inserattitel
3. Parameter: Keywords zum Inserat
4. Parameter: Max. Levenshtein DistanzDer gesamte Programmcode steht schon im Thread-Beginn.
Hier mal die Aufrufübersicht
void relevance_deinit(UDF_INIT *initid) { if (initid->ptr != NULL) { free(initid->ptr); } } my_bool relevance_init(UDF_INIT *initid, UDF_ARGS *args, char *message) { if ((args->arg_count != 4) || (args->arg_type[0] != STRING_RESULT || args->arg_type[1] != STRING_RESULT || args->arg_type[2] != STRING_RESULT || args->arg_type[3] != INT_RESULT)) { strcpy(message, "Function requires 4 arguments, (string, string, string, int)"); // Hier sollte der Speicher für initd->ptr reserviert werden den die Hauptfunktion dann weiter unten verwendet. Laut der MySQL Doku sollten die ganzen String-Arrays die bei mir in der Hauptfunktion stehen hier auf diesem Pointer reserviert werden return 1; } initid->maybe_null = 0; //doesn't return null return 0; } longlong relevance(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { .... }
Aufrufsquenz MySQL:
init();
Hauptfunktion()
deinit();Brauchst du sonst noch Details die du nicht dem Quellcode am Anfang entnehmen kannst?
Danke, LG
-
Ich habe nicht verstanden, was die Funktion überhaupt zurückliefern soll.
Momentan liefert sie doch gar nicht irgendwelche lev.algo. Werte zurück, sondern nur 0,1, oder irgendwelche Fehlercodes.
Und was soll bei strlen<=3 rückgegeben werden?
Du zählst zwar die Treffer, aber was passiert denn anschließend damit?
Ich könnte mir vorstellen, dass der min/max lev.algo.-Wert über alle Treffer ermittelt und dann rückgegeben werden soll oder zumindest die Anzahl der Treffer?!
"treffer_titel_alt" habe ich auch nicht verstanden.Auf jeden Fall lässt sich der Code auf 20-10% kürzen und dafür aber seeeehr deutlich beschleunigen.
Welchen Compiler verwendest du denn und welche Plattform?
-
Mittlerweile habe ich eine ungefähre Vorstellung, was du erreichen willst (an deinem Code leider nicht einfach abzulesen), hier mal mein Vorschlag:
longlong abweichung(UDF_INIT *initid,char *suche,char *in,const char *e,int distance,char *is_null,char *error) { #define MINDESTLAENGE 4 longlong r=LONG_MAX; /* "schlechteste" Übereinstimmung annehmen */ while( in!=e ) /* im zu durchsuchenden char-Array alle "Worte" durchsuchen, also z.B. "abcd" in "xyz\0qwer\0usw\0" */ { assert( (printf("%s ? %s\n",suche,in),1) ); if( strlen(suche)>=MINDESTLAENGE && strlen(in)>=MINDESTLAENGE ) /* nur ab MINDESTLAENGE Zeichen die Suche durchführen */ { if( !strcoll(in,suche) ) return 0; /* falls identisch (inkl. locale), dann "beste" Übereinstimmung liefern + Ende */ if( strstr(in,suche) ) return 0; /* falls enthalten, dann "beste" Übereinstimmung liefern + Ende */ { longlong tmp=levenshtein_k_base(initid,suche,in,distance,is_null,error); assert( tmp>=0 ); /* Code geht davon aus, dass keine neg. Werte geliefert werden */ if( 0==tmp ) return 0; /* falls <lv> "beste" Übereinstimmung liefert => Ende */ if( tmp<r ) r=tmp; /* sonst den besten Wert merken */ } } in+=strlen(in)+1; } return r; } longlong relevance(UDF_INIT *initid, UDF_ARGS *args, char *is_null, char *error) { longlong r=LONG_MAX; /* "schlechteste" Übereinstimmung annehmen */ /* Such-Array bilden z.B. "C Entwickler" => "c\0entwickler\0centwickler\0" */ char *s0=alloca(2+2*(args->lengths[0])),*c=s0?s0:(abort(),NULL),*e=c; memcpy(c,args->args[0],args->lengths[0]);c[args->lengths[0]]=0; while(*e) *e=tolower((unsigned char)*e),e++; strcpy(++e,s0); s0[strlen(s0)]=' '; while(*e) {if(*e==' ') memmove(e,e+1,strlen(e)); ++e;} ++e; c=s0; while(*c) {if(*c==' ') *c=0; ++c;} /***************************************************************************/ /* Inserattitel-Wörter durchsuchen und Ende, falls "beste" Übereinstimmung hier schon gefunden */ c=s0; { char *s1=alloca(1+args->lengths[1]),*x=s1?s1:(abort(),NULL); memcpy(x,args->args[1],args->lengths[1]);x[args->lengths[1]]=0; while(*x) *x=tolower((unsigned char)*x),x++; x=s1; while(*x) {if(*x==' ') *x=0; ++x;} ++x; while( c!=e ) /* alle Such-Wörter durchlaufen */ { longlong tmp=abweichung(initid,c,s1,x,*(int *) args->args[3],is_null,error); if( 0==tmp ) return 0; if( tmp<r ) r=tmp; c+=strlen(c)+1; } } /* Inserattext-Wörter durchsuchen und Ende, falls "beste" Übereinstimmung hier gefunden */ c=s0; { char *s1=alloca(1+args->lengths[2]),*x=s1?s1:(abort(),NULL); memcpy(x,args->args[2],args->lengths[2]);x[args->lengths[2]]=0; while(*x) *x=tolower((unsigned char)*x),x++; x=s1; while(*x) {if(*x==' '||*x=='|') *x=0; ++x;} ++x; while( c!=e ) { longlong tmp=abweichung(initid,c,s1,x,*(int *) args->args[3],is_null,error); if( 0==tmp ) return 0; if( tmp<r ) r=tmp; c+=strlen(c)+1; } } return r; /* "besten" d.h. minimalen Wert vom lev.algo. rückliefern */ }
Der Code ist bis auf alloca strikt C89 (läuft also auf jedem C89-Compiler aufwärts).
Noch zu tun:
- locale-Berücksichtigung instrstr
undlevenshtein_k_base
- auch andere Whitespaces außer ' ' können vorkommen
- die Aufnahme von vielfältigen Freitexten in ein SQL-Statement ist gelinde gesagt suboptimal (von jedem Datenbankspezi kriegst du dafür auf die Finger), weil Statement-Cache und Hashtable deiner DB unnötig strapaziert werden und damit nicht nur deine unmittelbar in diesem Zusammenhang stehenden Statements verlangsamt werden, sondern auch alle anderen in der aktuellen DB-Session.
Das führt mit der Zeit dazu, dass viele deiner (Webseiten)Dateninhalte im Statement-Cache rumliegen, wo sie definitiv nicht hingehören.
Sinnvollerweise setzt man hier Parameter/Bindvariablen ein, also
stattSELECT *, relevance('C Entwickler', 'Software Developer', 'Software Entwickler|C Entwickler|Programmierer', 1) SELECT *, relevance('kdfhgkdfhg', 'ksjad jksdjkf', 'jsadgh jsdgh üqop ksdjf', 1) ... viele weitere SELECT *, relevance('pqownfdkg', 'opqw oiqiw', 'qerew podgfo', 1)
viel besser
SELECT *, relevance(:1,:2,:3,:4)
Sowas sollte selbst mysql beherrschen, siehe dazu die API.
-
Wutz schrieb:
Ich habe nicht verstanden, was die Funktion überhaupt zurückliefern soll.
Momentan liefert sie doch gar nicht irgendwelche lev.algo. Werte zurück, sondern nur 0,1, oder irgendwelche Fehlercodes.
Und was soll bei strlen<=3 rückgegeben werden?
Du zählst zwar die Treffer, aber was passiert denn anschließend damit?
Ich könnte mir vorstellen, dass der min/max lev.algo.-Wert über alle Treffer ermittelt und dann rückgegeben werden soll oder zumindest die Anzahl der Treffer?!
"treffer_titel_alt" habe ich auch nicht verstanden.Auf jeden Fall lässt sich der Code auf 20-10% kürzen und dafür aber seeeehr deutlich beschleunigen.
Welchen Compiler verwendest du denn und welche Plattform?Das sind keine Fehlercodes. Das ist die Relevanz. 100 Punkte Relevanz bedeutet der Suchbegriff ist genau im Inseratstitel vorhanden. 75 bedeutet der Suchbegriff steht als Phrase in den Keywords und 50 bedeutet die Suchbegriffe stehen irgendwo in den Keywords.
Zum strlen < 3: Normalerweise wird der Wortvergleich mit Levenshtein gelöst um Tippfehler vom User auszuschließen. Bei Wörtern mit einer Länge von <= 3 wird ein direkter Vergleich (Stringvergleich) durchgeführt da sonst falsche Wörter gefunden werden. Bei "IT" sollte man beispielsweise keinen Fehler machen sonst bedeut das Wort ja was anderes. Bei längern Wörtern ist die Chance auf einen Tipp/Rechtschreibfehler allerdings höher (zB "Techniker" vs. "Technicker").
Kompiliert wird das mit GCC (Linux):
gcc -o relevance.so -shared relevance.c -I /usr/include/mysql/Das Programm ist standalone leider bei mir auch nicht lauffähig sondern nur wenn ich es über MySQL ausführe. Das liegt aber wohl auch an meinen beschränkten C-Kenntnissen.
Zu deinem Code-Vorschlag: Vielen Dank ich werd mir das jetzt mal ganz genau durchschauen - ich hoffe es kommen nicht zuviel Fragen auf!
-
Hallo Wutz,
danke nochmal für deine Mühe - der Code klappt wunderbar. Musste die Logik allerdings umbauen da die Berechnung anders gedacht war. Hier mal meine Version davon. Wenn du Zeit hast ich hab noch ein paar Fragen in den Code geschrieben da ich gewisse Dinge von dir nicht verstehe.
Mein größtes Problem ist dass ich die Anzahl der Suchbegriffe nicht zählen kann (Zeile 197).
Meine Idee wäre mit "strpbrk" oder ähnlichen einfach die Anzahl der Leerzeichen (+1) zu zählen. Gibts ne bessere Alternative?Lg rappit
Edit: Sorry für die dumme Frage mit der Anzahl, bin inzwischen schon draufgekommen:
Zeile 176 einfach sowhile(*c) {if(*c==' '){ *c=0; anz_suchbegriffe++; } ++c; }
Den restlichen oberen Teil versteh ich trotzdem nicht
Das einzige, letzte logische Problem ist das in Zeile 226.
-
Du hast das Prinzip meines Codevorschlages nicht verstanden.
Stringlisten werden hier nicht wie häufig einzeln angesprochen sondern nur innerhalb eines Streams, d.h. du brauchst überhaupt keine 'anzahlderstrings', das macht alles die while-Schleife.
Die Fragen lese ich mir bei Gelegenheit mal näher durch, trotzdem war dein bisheriger Code hinsichtlich des <lv> ignorant, d.h. die mühsam aufgerufene <lv> Funktion liefert einen mühsam ermittelten Wert für ein Wichtung der Übereinstimmung, den du aber komplett ignorierst, da du ihn nicht an dein SQL durchreichst.
-
Wutz schrieb:
Du hast das Prinzip meines Codevorschlages nicht verstanden.
Stringlisten werden hier nicht wie häufig einzeln angesprochen sondern nur innerhalb eines Streams, d.h. du brauchst überhaupt keine 'anzahlderstrings', das macht alles die while-Schleife.
Die Fragen lese ich mir bei Gelegenheit mal näher durch, trotzdem war dein bisheriger Code hinsichtlich des <lv> ignorant, d.h. die mühsam aufgerufene <lv> Funktion liefert einen mühsam ermittelten Wert für ein Wichtung der Übereinstimmung, den du aber komplett ignorierst, da du ihn nicht an dein SQL durchreichst.Verstehe, danke
Dennoch hast du damit mein Speicherproblem gelöst und ich bekomme keine Fehler mehr. Vielen Dank!