[RESOLU] JQuery Autocomplete source oracle

Eléphanteau du PHP | 49 Messages

16 févr. 2011, 15:15

Bonjour,

Je mets en place une liste de comptes clients que l'on peut rechercher avec le plugin autocomplete de Jquery. Jusque là tout va bien, l'autocompletion fonctionne nickel.
Sauf que durant mes tests préliminaires, j'utilisais une base de données forte de 300 entrées max. Maintenant que je fais le test à grande échelle avec une base de données de 65000 entrées, la page met 40-60 secondes à se charger rien que pour l'autocompletion (j'ai essayé de charger la page sans le plugin et c'est presque instantané). Je cherche donc à optimiser l'appel d'autocomplete.

Du coup voici ma méthode actuelle qui rame:
Je suis sous zend, j'appelle dans le controller l'autocomplete et la fonction qui va lui donner sa base de données.

Code : Tout sélectionner

$liste_noclients = Business_Pilotage_Service::factory($this->_adpater)->listerNoclient(); $this->view->autocompleteElement = new ZendX_JQuery_Form_Element_AutoComplete('autoComplete'); $this->view->autocompleteElement->setJQueryParam('data', $liste_noclients) ->setJQueryParams( array("change" => new Zend_Json_Expr("function() { $('#form_autocomplete').submit(); }") ) ) ->setAttrib('size',10,10,10) ->setAttrib('limit',10) ->setJQueryParam('minLength',5);
Et donc la fonction en question:

Code : Tout sélectionner

public function listerNoclient() { // Connexion $dbConn = new PDO($this->_dsn, $this->_username, $this->_password); // Preparation de le requete $pdoStmt = $dbConn->prepare('SELECT NO_CLI_VPC FROM VCCLICGP WHERE DT_SUP IS NULL ORDER BY NO_CLI_VPC'); // Execution de la requete $pdoStmt->execute(); $res = $pdoStmt->fetchAll(); $resnoclient = array(); $i = 0; foreach($res as $liste){ $resnoclient[$i] = $liste['NO_CLI_VPC']; $i++; } // Fermeture connexion $dbConn = null; return $resnoclient; }
Comme vous pouvez le voir, je fais une requête oracle basique, elle me sort un tableau.

Code : Tout sélectionner

Array ( [0] => Array ( [NO_CLI_VPC] => V000001 [0] => V000001 ) [1] => Array ( [NO_CLI_VPC] => V000002 [0] => V000002 ) [2] => Array ( [NO_CLI_VPC] => V000003 [0] => V000003 ) ...
J'affine ce tableau pour n'avoir que ce qu'autocomplete a besoin:

Code : Tout sélectionner

Array ( [0] => V000001 [1] => V000002 [2] => V000003 [3] => V000004 [4] => V000005 [5] => V000006 [6] => V000007 [7] => V000008 [8] => V000009 [9] => V000010 [10] => V000011 [11] => V000012 ...
Et donc j'envoie ce tableau en paramètre source du plugin. Sauf que c'est très lourd et plutôt lent (environ une minute pour 65k entrées).
Si ce n'était que le premier chargement, mais l'utilisateur se tape un chargement à la validation du coup la recherche est plutôt lente (disons qu'on doit rechercher 3 clients pour les modifier ça nous prend 3 minutes à poireauter pour 5 secondes d'action).

Est-il possible de passer la base directement en paramètre d'autocomplete?
J'entends par là, à partir de 5 caractères entrés, autocomplete va faire un "SELECT V0000* FROM..." et n'avoir en mémoire que les 100 valeurs possibles plutôt que les 65000 qui ne servent à rien.

J'ai bien vu la doc qui me parle de 'remote source' et de bases de données avec des millions d'entrées mais je ne trouve rien d'autre à ce sujet, pas même un exemple. Du coup si vous avez une solution / un lien vers une explication, je serais fort joie de vous voir participer.

En vous remerciant,

--Simon
Modifié en dernier par diday le 17 févr. 2011, 12:04, modifié 1 fois.

ViPHP
ViPHP | 3607 Messages

16 févr. 2011, 15:31

Bonjour,

Effectivement il va falloir utiliser le paramètre "source" afin que le requêtes ne soit faites que sur de petits volumes...
Un peu d'aide ici visiblement : http://framework.zend.com/manual/en/zen ... tionhelper

Mais encore une fois le problème semble venir de l'utilisation de Zend et non pas de jqueryui :/

Eléphanteau du PHP | 49 Messages

16 févr. 2011, 15:36

Je ne suis pas sûr que le problème vienne de Zend (cette fois ^^), je peux très bien passer un paramètre source avec la variable setJQueryParam('data', $liste_noclients), la question est: que dois-je mettre à la place de $liste_noclients?

J'entends par là que la donc me dit de mettre un search.php, mais me voilà bien avancé. De quoi est constitué ce fichier? Que retourne-t-il?

ViPHP
ViPHP | 3607 Messages

16 févr. 2011, 15:39

Je ne sais pas trop, mais à priori rien!

Puisqu'en fait vu qu'il va y avoir de l'ajax, il va falloir créer une autre vue...
Qui renverra uniquement un objet json d'entrée de la base de donnée...

Eléphanteau du PHP | 49 Messages

16 févr. 2011, 15:44

Dans ce cas prenons le problème autrement.

Pourquoi le fait de faire un tableau de 65k entrées est-il si lent? Est-ce normal?

ViPHP
xTG
ViPHP | 7331 Messages

16 févr. 2011, 15:49

65 000 entrées... Euh à priori oui c'est normal que cela soit lent, cela fait beaucoup d'itération machine pour le construire. :)
Exécution requête, traitement et envoie des données. Cela commence à faire pour un si gros volume.
Il va à mon avis nécessairement appliquer une limite de retour.
Si tu as 20000 retours les affiches-tu tous ? A mon avis non car cela exploserai l'écran (bien que suivant la méthode d'affichage...).
Donc il faudrait limiter le nombre de retours de la requête.

Eléphanteau du PHP | 49 Messages

16 févr. 2011, 15:51

Je n'affiche que 100 retours maximum. Le problème c'est que autocomplete doit savoir ce qui existe et donc parcourir la table entièrement, non?

ViPHP
ViPHP | 3607 Messages

16 févr. 2011, 15:52

A priori oui c'est normal, il ne te viendrai pas à l'idée d'afficher 65k entrées dans un tableau html?
C'est quasiment la même chose là sauf que c'est dans un objet javascript...

D'une part la requête SQL est longue, ensuite la construction de l'objet json l'est aussi, enfin le chargement de la page est lourd, et pour finir le js va ramer pour parcourir ton gigantesque tableau!

Essaye de faire un truc du genre
<?php

$this->view->autocompleteElement = new ZendX_JQuery_Form_Element_AutoComplete('autoComplete');
        $this->view->autocompleteElement->setJQueryParams( array("source" => 'url_de_la_nouvelle_vue.php') )
                                        ->setJQueryParams( array("change" => new Zend_Json_Expr("function() { $('#form_autocomplete').submit(); }") ) )
                                        ->setAttrib('size',10,10,10)
                                        ->setAttrib('limit',10)
                                        ->setJQueryParam('minLength',5);
ensuite dans ta nouvelle vue, tu faits un select restreint par les premières lettres tapées et tu ajoutes une limite correspondant à ta limite javascript...
et tu renvoies les résultats comme ça: (extrait d'un autocomplete sur une liste de ville)
 while($entry = mysql_fetch_row($results)){
    $proposal[]=array('value'=>$entry[0],'label'=>$entry[0].' ('.$entry[1].')');
  }

  echo json_encode($proposal);
Note le paramètre label est facultatif.

Eléphanteau du PHP | 49 Messages

16 févr. 2011, 16:11

Zend ne reconnait pas l'url de la source. Même avec le chemin en dur. :/

De toute façon je viens de voir que j'ai d'autres problèmes plus graves que le temps de chargement. Une fois un champ supprimé, l'autocomplete ne veut plus fonctionner (même après refresh forcé). Du coup je me dis qu'il y a une couille quelque part dans mon code que je ne vois pas. On attendra que je corrige tout avant de revenir là dessus.

Je sais pas si zend est sensé être vraiment mieux mais personnellement je perds plus de temps à l'apprivoiser et à contourner les bugs que je créé en ne maitrisant pas la bête qu'à réellement coder. ^^

ViPHP
ViPHP | 3607 Messages

16 févr. 2011, 16:13

A mon avis commence par essayer ton autocomplete ajax à part sans zend.

Et après débrouille toi pour le transposer...

Qu'il ne trouve pas l'url doit être normal, as-tu créé une vue pour le script ajax?

Eléphanteau du PHP | 49 Messages

16 févr. 2011, 16:18

Oui oui j'ai créé la vue. :)
Je vais essayer de me dépatouiller dans mon coin, là je suis trop dans l'inconnu pour être autre chose qu'un boulet sur cette question. Une fois que je maitriserai mieux le sujet je reviendrai poser les interrogations au besoin.

Eléphanteau du PHP | 49 Messages

16 févr. 2011, 17:24

Hop hop j'hijack mon propre thread vu que je suis dans le sujet mais plus vraiment dans le même problème.

J'ai un peu nettoyé mon code, optimisé les appel base (supprimé quelques un redondants) et déjà la vitesse est plus potable (15-20 secondes pour traiter 65k entrées). Sauf que j'ai un problème rigolo:
-> l'autocomplete, quand il n'a que 300 entrées, marche nickel. Pas de soucis j'ai tout qui apparait comme il faut etc. Quand il a 65k entrées, il marche aussi, un peu plus lent mais c'est vivable. Seulement, ma requête SQL est ainsi faite:
SELECT NO_CLI_VPC FROM VCCLICGP WHERE DT_SUP IS NULL ORDER BY NO_CLI_VPC
DT_SUP, c'est un champ de suppression logique (i.e. quand il n'est pas vide, on considère que l'entrée a été supprimée et on ne l'affiche pas dans les possibilités de l'autocomplete). Tant que tous les 65k champs de la table ont leur DT_SUP vide, ça va. Il suffit d'une entrée avec DT_SUP non nul pour que l'autocomplete ne fonctionne plus.

SAUF QUE. Quand j'ai 300 champs il n'y a pas de problème, je peux supprimer logiquement à tout va sans états-d'âme.
Et même quand j'en ai 65k, puisque la requête fonctionne parfaitement et retourne les bons champs. Le problème est donc chez autocomplete.
Un petit coup de log avant/après sur ma base aux 65k entrées.
Avant, autocompletion marche au poil:

Code : Tout sélectionner

Array ( [0] => V000001 [1] => V000002 [2] => V000003 [3] => V000004 [4] => V000005 [5] => V000006 [6] => V000007 [7] => V000008 [8] => V000009 [9] => V000010 [10] => V000011 [11] => V000012 [12] => V000013 ...
Après, rien de va plus:

Code : Tout sélectionner

Array ( [0] => V000002 [1] => V000003 [2] => V000004 [3] => V000005 [4] => V000006 [5] => V000007 [6] => V000008 [7] => V000009 [8] => V000010 [9] => V000011 [10] => V000012 ...
Vous noterez la disparition de V000001.

C'est quand même curieux.

Quand il y en a 300 ça va, c'est quand il y en a 65 000 que ça pose des problèmes...

ViPHP
xTG
ViPHP | 7331 Messages

16 févr. 2011, 17:36

Cet affichage de l'array tu le fais à la réception ou bien après le traitement de la requête ?

Eléphanteau du PHP | 49 Messages

16 févr. 2011, 17:38

C'est ce que retourne la fonction-requête.
public function listerNoclient()
    {
        // Connexion
        $dbConn = new PDO($this->_dsn, $this->_username, $this->_password);
        // Preparation de le requete
        $pdoStmt = $dbConn->prepare('SELECT NO_CLI_VPC FROM VCCLICGP WHERE DT_SUP IS NULL ORDER BY NO_CLI_VPC');
        // Execution de la requete
        $pdoStmt->execute();
        $res = $pdoStmt->fetchAll(PDO::FETCH_COLUMN);
        // Fermeture connexion
        $dbConn = null;
        return $res;
    }
Je fais un log sur cette variable, dans mon controller.

Code : Tout sélectionner

$liste_noclients = Business_Pilotage_Service::factory($this->_adpater)->listerNoclient();

Eléphanteau du PHP | 49 Messages

16 févr. 2011, 17:40

Rebondissement!

65535 entrées ça passe pas, mais 65515 (j'en ai supprimé -réellement- 19 puis une seule logiquement) et ça marche. 65536 ça marchait aussi. :/

--edit: De 65530 à 65535 ça ne marche pas. Oo
Encore 65535 tout seul j'aurai pu me dire pourquoi pas, mais là 65536 et 65529 fonctionnent, quoi. :/