PHP: Denna sökfunktion är inte optimal?

Tråden skapades och har fått 14 svar. Det senaste inlägget skrevs .
1
  • Medlem
  • Boden
  • 2011-08-18 14:14

Hej. Har lyckats snickra ihop en sökfunktion, men den är otroligt långsam (20 sek i IE på en äldre PC).

Jag antar att, även om jag fått det att funka, har gjort helt fel.

Man fyller i ett formulär, och utifrån det genereras en URL som man skickas vidare till. Inget konstigt här väl? Detta för att sidan man skickas till ska veta vad man sökt efter.

Sökvariablerna hämtas från URLen:

$sok_marke = $_GET[ 'ma' ]; 
$sok_modell = $_GET[ 'mo' ]; 
$sok_karosseri = $_GET[ 'ka' ]; 
$sok_ar_fran = $_GET[ 'af' ]; 
$sok_ar_till = $_GET[ 'at' ]; 
$sok_pris_fran = $_GET[ 'pf' ]; 
$sok_pris_till = $_GET[ 'pt' ]; 
$sok_mil_fran = $_GET[ 'mf' ]; 
$sok_mil_till = $_GET[ 'mt' ]; 
$sok_drivmedel = $_GET[ 'dr' ]; 
$sok_vaxellada = $_GET[ 'va' ]; 

Databasen är i form av en XML. Jag använder cURL för att ladda XMLen eftersom den är på extern server. Sen använder jag SimpleXML för att göra om XMLen till en array:

foreach($xml->car as $car)
{
        $acar = array(
            'image' => $car->thumbs->image[1], 
            'yearmodel' => $car->yearmodel, 
            'miles' => $car->miles, 
            'price' => $car->{'price-sek'}, 
            'model' => $car->model, 
            'brand' => $car->brand,
            'modeldescription' => $car->modeldescription,
            'info' => $car->{'info'},
            'id'	=> $car->id,
            'bodytype'	=> $car->bodytype,
            'fueltype'	=> $car->fueltype,
            'gearboxtype'	=> $car->gearboxtype,
        );
        $cararray[] = $acar;
}

Nu har jag alltså en Array (cararray). För att plocka fram datan som matchar sökningen har jag en kanske lite för lång foreach loop (tror det är denna som slöar ner).

foreach( $cararray as $thiscar )
{
	if(("$thiscar[brand]" == $sok_marke || $sok_marke == 'Alla') 
	&& ("$thiscar[model]" == $sok_modell || $sok_modell == 'Alla')
	&& ("$thiscar[bodytype]" == $sok_karosseri || $sok_karosseri == 'Alla')
	&& (("$thiscar[yearmodel]" >= $sok_ar_fran || $sok_ar_fran =='null') && ("$thiscar[yearmodel]" <= $sok_ar_till || $sok_ar_till =='null'))
	&& (("$thiscar[price]" >= $sok_pris_fran || $sok_pris_fran =='null') && ("$thiscar[price]" <= $sok_pris_till || $sok_pris_till =='null'))
	&& (("$thiscar[miles]" >= $sok_mil_fran || $sok_mil_fran =='null') && ("$thiscar[price]" <= $sok_mil_till || $sok_mil_till =='null'))
	&& ("$thiscar[fueltype]" == $sok_drivmedel || $sok_drivmedel == 'Alla')
	&& ("$thiscar[gearboxtype]" == $sok_vaxellada || $sok_vaxellada == 'Alla')
	){ 
         echo ("Här är kod för att skriva ut resultatet i form av en DIV, en if loop, om bild inte finns visas en "ingen bild finns-bild" med mera);
}

Detta är väl inte rätta vägen att gå? När man plockar ut alla inlägg i XMLen och lägger i en array, kan man välja att bara plocka ut den data som matchar sökningen? På så sätt slipper man den gigantiska foreach / if loopen som rensar bland resultaten. Skulle det snabba upp sökningen? Hur skulle en sådan snutt kod kunna se ut?

Tack så mycket på föhand!

1. Nu går du ju igenom listan två gånger. Som du säger, bättre att kolla sökkriterierna i den första loopen och returnera när du hittat det du söker eller bara spara undan och fortsätta.

2. Är det stora bilder du laddar ur thumbs/image?

  • Medlem
  • Boden
  • 2011-08-18 15:09

1. Jo jag tänkte också det. Är det bara att flytta upp If loopen (den som plockar ut resultaten efter sökkriterierna), eller finns det ett effektivare sätt att plocka ut resultaten (något som inte loopar genom alla ca 200 "car").

edit: Eller rättare sagt, något som inte plockar ut vilken "car" som ska vara med i arrayen utifrån en if loop med 11 statements? För en if loop med 11 statements (22 om man räknar med "or") segar väl ner ordentligt? (känns som det är rätt mycket för servern att behandla för varje "car").

2. Thumbsen är 128x85px på 4kb.

Senast redigerat 2011-08-18 15:21
Ursprungligen av jensm:

1. Jo jag tänkte också det. Är det bara att flytta upp If loopen (den som plockar ut resultaten efter sökkriterierna), eller finns det ett effektivare sätt att plocka ut resultaten (något som inte loopar genom alla ca 200 "car").

edit: Eller rättare sagt, något som inte plockar ut vilken "car" som ska vara med i arrayen utifrån en if loop med 11 statements? För en if loop med 11 statements (22 om man räknar med "or") segar väl ner ordentligt? (känns som det är rätt mycket för servern att behandla för varje "car").

Du får nog flytta upp den.

Du måste gå igenom alla instanser av Car, och göra lika många jämförelser som du har sökkriterier på varje.

  • Medlem
  • Boden
  • 2011-08-18 18:19

Alltså något i stil med:

foreach($xml->car as $car)
{

if(("$car[brand]" == $sok_marke || $sok_marke == 'Alla') 
	&& ("$car[model]" == $sok_modell || $sok_modell == 'Alla')
	&& ("$car[bodytype]" == $sok_karosseri || $sok_karosseri == 'Alla')
	&& (("$car[yearmodel]" >= $sok_ar_fran || $sok_ar_fran =='null') && ("$car[yearmodel]" <= $sok_ar_till || $sok_ar_till =='null'))
	&& (("$car[price]" >= $sok_pris_fran || $sok_pris_fran =='null') && ("$car[price]" <= $sok_pris_till || $sok_pris_till =='null'))
	&& (("$car[miles]" >= $sok_mil_fran || $sok_mil_fran =='null') && ("$car[price]" <= $sok_mil_till || $sok_mil_till =='null'))
	&& ("$car[fueltype]" == $sok_drivmedel || $sok_drivmedel == 'Alla')
	&& ("$car[gearboxtype]" == $sok_vaxellada || $sok_vaxellada == 'Alla')
	){ 

        $acar = array(
            'image' => $car->thumbs->image[1], 
            'yearmodel' => $car->yearmodel, 
            'miles' => $car->miles, 
            'price' => $car->{'price-sek'}, 
            'model' => $car->model, 
            'brand' => $car->brand,
            'modeldescription' => $car->modeldescription,
            'info' => $car->{'info'},
            'id'	=> $car->id,
            'bodytype'	=> $car->bodytype,
            'fueltype'	=> $car->fueltype,
            'gearboxtype'	=> $car->gearboxtype,
        );
        $cararray[] = $acar;
}
}

Detta funkar dock inte. Tror "$car[något]" i if loopen spökar eftersom det inte är en array än.

Kör $car->miles istället för $car[miles] och så vidare.

Det är nog värt att kika på Xpath i det här fallet. Ett otestat exempel på hur det skulle kunna se ut kommer här:

function is_blank($field) {
	return in_array($field, array('Alla', 'null'));
}

$criterias = array();

if( ! is_blank($sok_marke))
	$criterias[] = "brand=$sok_marke";

if( ! is_blank($sok_modell))
	$criterias[] = "model=$sok_modell";

if( ! is_blank($bodytype))
	$criterias[] = "bodytype=$sok_karosseri";

if( ! is_blank($sok_ar_fran))
	$criterias[] = "yearmodel>$sok_ar_fran";

if( ! is_blank($sok_ar_till))
	$criterias[] = "yearmodel<$sok_ar_till";

$xpath = 'car['.implode($criterias, ' and ').']';

$result = $xml->xpath($xpath);

På så vis slipper PHP instansiera objekt för varje iteration, vilket borde bli vansinnigt mycket effektivare. De bilar som matchar sökkriterierna hamnar i exemplet i $result. Förmodligen så går det att bygga något snyggare för att skapa sökfrågan, men nån måtta får det vara.

  • Medlem
  • Boden
  • 2011-08-18 20:30

Tack för hjälpen, nu funkar det. Ska testa om det går snabbare när jag har tillgång till en lite äldre dator med IE.

Jensm, varför låter du inte med hjälp av SQL-syntax plocka ut rätt bilar istället för PHP? Det går mycket snabbare och kräver mindre avsevärt mindre datorresurser.

Jag brukar låta SQL ta hand om sökningar samt sorteringar och PHP tar hand om hur informationen från SQL sökningarna skall visas på skärmen.

jensm: Det har inget att göra med vare sig webbläsare eller klientdator, det är helt enkelt lösningen som är väldigt ineffektiv.

  • Medlem
  • Boden
  • 2011-08-18 23:20

Vikingen: Helt enkelt eftersom jag inte vet vad SQL-syntax innebär. Om du vill kan du gärna peka mig åt några artiklar/tutorials. Det låter intressant!

irrelogical: jo. PHP är clientside, vilket om jag inte har fel innebär att belastningen hamnar på servern (rätta mig om jag har fel, men vilket innebär att datorn inte borde spela någon roll eftersom servern hanterar alla beräkningar,) men det tar 5 sekunder här hemma på en macbook (100 mb/s bredband) men på företaget jag utvecklar sidan för tar det 20 sek. De har gamla datorer med IE6, har det verkligen inget med laddningen att göra? Jag antar att ett företag (relativt stort företag) har motsvarande de 100mb/s som jag har som privatperson..

Hur är lösningen ineffektiv? Ska leverera sidan om 1 månad, måste snabba upp skiten innan dess

Ursprungligen av jensm:

Vikingen: Helt enkelt eftersom jag inte vet vad SQL-syntax innebär. Om du vill kan du gärna peka mig åt några artiklar/tutorials. Det låter intressant!

Då du inte har mycket tid kvar för leverans är det bättre att du fokuserar dig nu på xpath.

Men för framtida projekt kan du titta närmare på SQL och dess syntax:

SQL Tutorial

Där finns det flera exempel och hur dessa används tillsammans med php och html.

Lycka till!

Ursprungligen av jensm:

irrelogical: jo. PHP är clientside, vilket om jag inte har fel innebär att belastningen hamnar på servern (rätta mig om jag har fel, men vilket innebär att datorn inte borde spela någon roll eftersom servern hanterar alla beräkningar,)

Det är inte databeräkningen som bekymrar mig, det är mängden minne. Jag hade på min förra arbetsplats en kraftfull dator men ändå inte kunde slutföra en stress test pga krash. Det visade sig vara RAM minnet som var problemet. När en användare använder din motor aktiveras en tråd som kör ditt php skript och läser in mycket information och skall sållas bort, så går det åt en viss mängd minne. När det är många användare som använder ditt php skript är det många trådar och därmed en massa minne som tas i anspråk. När php exekveringen är klar frigörs tråden och återlämner tillbaka minnet. Frågan är om minnet som återlämnas tillbaka hinner tillräckligt snabbt tillbaka innan nya användare exekverar ditt php skript. (En användare = 1 tråd)

Jag brukar tänka så här som php programmerare:
Bäckar små blir en stor å…

Mitt råd är när du är klar med din php skript, stress testa på kundens dator och se om datorn har tillräcklig med RAM.

I nästa projekt är det en mycket bättre idé att dela upp arbetet mellan SQL och php. SQL sökningar går mycket fortare, tar mycket mindre resurser (RAM och processorkraft). PHP tar hand om färdig behandlad information från SQL och presenterar det på skärmen, på det vis du vill att den skall göra. Kombinera PHP arbetet gärna med xpath och Xquery. Därmed är också lösningen mycket mer stabil när många söker samtidigt.

Senast redigerat 2011-08-19 11:28

Som irrelogical säger, närhelst man ska söka igenom XML så är det xpath som gäller (eller Xquery, men PHP har inget inbyggt stöd för det). Det är en lätt tusen gånger snabbare (på riktigt) än att loopa igenom det.

När du däremot får ditt sökresultat och ska visa det på sidan kan du loopa på det där sättet, för det handlar bara om rena pyttemängder men om det (mot all förmodan) också skulle kräva optimering kan du använda en XSL för att både extrahera/söka data och omvandla det till HTML.

The Real Viking: Det förutsätter att jensm öht har tillgång till databasen där informationen ligger, och att det är en relationsdatabas, vilket inte framgår.

Ursprungligen av irrelogical:

The Real Viking: Det förutsätter att jensm öht har tillgång till databasen där informationen ligger, och att det är en relationsdatabas, vilket inte framgår.

Irrelogical, du har förståss helt rätt.

1
Bevaka tråden