Zend_Search_Lucene-BestPractice.xml 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 13831 -->
  3. <!-- Reviewed: no -->
  4. <sect1 id="zend.search.lucene.best-practice">
  5. <title>Bonnes pratiques</title>
  6. <sect2 id="zend.search.lucene.best-practice.field-names">
  7. <title>Nommage des champs</title>
  8. <para>Il n'y a pas de limitation pour les noms de champs dans
  9. <classname>Zend_Search_Lucene</classname>.</para>
  10. <para>Cependant, il est préférable de ne pas utiliser les noms '<emphasis>id</emphasis>' et
  11. '<emphasis>score</emphasis>' afin d'éviter toute ambiguïté dans les propriétés de
  12. <code>QueryHit</code>.</para>
  13. <para>Les propriétés <code>id</code> et <code>score</code> de
  14. <classname>Zend_Search_Lucene_Search_QueryHit</classname> font toujours référence à l'identifiant
  15. interne du document Lucene et au <link
  16. linkend="zend.search.lucene.searching.results-scoring">score</link> de hit. Si le document
  17. indexé possède les mêmes champs stockés, vous devrez utiliser la méthode
  18. <code>getDocument()</code> pour y accéder :<programlisting role="php"><![CDATA[
  19. $hits = $index->find($query);
  20. foreach ($hits as $hit) {
  21. // Récupérer le champ de document 'title'
  22. $title = $hit->title;
  23. // Récupérer le champ de document 'contents'
  24. $contents = $hit->contents;
  25. // Récupérer l'id interne du document Lucene
  26. $id = $hit->id;
  27. // Récupérer le score de hit
  28. $score = $hit->score;
  29. // Récupérer le champ de document 'id'
  30. $docId = $hit->getDocument()->id;
  31. // Récupérer le champ de document 'score'
  32. $docId = $hit->getDocument()->score;
  33. // Un autre moyen de récupérer le champ 'title'
  34. $title = $hit->getDocument()->title;
  35. }
  36. ]]></programlisting></para>
  37. </sect2>
  38. <sect2 id="zend.search.lucene.best-practice.indexing-performance">
  39. <title>Performance de l'indexation</title>
  40. <para>La performance de l'indexation est un compromis entre les ressources utilisées, le
  41. temps d'indexation et la qualité de l'index.</para>
  42. <para>La qualité de l'index est complètement déterminée par le nombre de segments de
  43. l'index.</para>
  44. <para>Chaque segment d'index et un portion de données entièrement indépendante. Ainsi plus
  45. un index contient de segments, plus il sera gourmand en mémoire et en temps de calcul lors
  46. de la recherche.</para>
  47. <para>L'optimisation d'index est un processus consistant à fusionner plusieurs segments en
  48. un seul nouveau segment. Un index totalement optimisé ne contient qu'un seul segment.</para>
  49. <para>L'optimisation complète de l'index peut être effectuée avec la méthode
  50. <code>optimize()</code> :<programlisting role="php"><![CDATA[
  51. $index = Zend_Search_Lucene::open($indexPath);
  52. $index->optimize();
  53. ]]></programlisting></para>
  54. <para>L'optimisation d'index fonctionne avec des "data streams" et ne consomme pas pas une
  55. grande quantité de mémoire, mais nécessite des ressources de processeur et du temps.</para>
  56. <para>Par nature, les segments d'index de Lucene ne peuvent pas être mis à jour (l'opération
  57. de mise à jour nécessite une réécriture complète du segment). Ainsi, l'ajout de nouveau(x)
  58. document(s) à un index implique toujours la génération d'un nouveau segment. De fait, cela
  59. dégrade la qualité de l'index.</para>
  60. <para>Une optimisation automatique d'un index est effectuée après chaque génération de
  61. segment et consiste en la fusion des segments partiels.</para>
  62. <para>Il y a trois options pour contrôler le comportement de l'auto-optimisation (voir la
  63. section <link linkend="zend.search.lucene.index-creation.optimization">Optimisation
  64. d'index</link>) :<itemizedlist>
  65. <listitem>
  66. <para><emphasis>MaxBufferedDocs</emphasis> représente le nombre maximum de
  67. documents qui peuvent être mis en mémoire tampon avant qu'un nouveau segment
  68. soit généré et écrit sur le disque dur.</para>
  69. </listitem>
  70. <listitem>
  71. <para><emphasis>MaxMergeDocs</emphasis> représente le nombre maximum de
  72. documents qui seront fusionnés dans un nouveau segment lors du processus
  73. d'auto-optimisation.</para>
  74. </listitem>
  75. <listitem>
  76. <para><emphasis>MergeFactor</emphasis> détermine à quelle fréquence
  77. l'auto-optimisation est effectuée.</para>
  78. </listitem>
  79. </itemizedlist> <note>
  80. <para>Toutes ces options sont des propriétés de la classe
  81. <classname>Zend_Search_Lucene</classname>, pas des propriétés d'index. Elles n'affectent que
  82. les instances de <classname>Zend_Search_Lucene</classname> et peuvent varier selon les
  83. scripts.</para>
  84. </note></para>
  85. <para><emphasis>MaxBufferedDocs</emphasis> n'a aucun effet si vous n'indexez qu'un seul
  86. document par exécution de script. En revanche, il est très important pour les indexations
  87. massives ("batch indexing"). Plus sa valeur est élevée, meilleures seront les performances
  88. d'indexation, mais plus la consommation de mémoire sera importante.</para>
  89. <para>Il n'existe pas de manière simple de calculer la meilleure valeur pour le paramètre
  90. <emphasis>MaxBufferedDocs</emphasis> car cela dépend de la taille moyenne des documents, de
  91. l'analyseur utilisé et de la mémoire disponible.</para>
  92. <para>Une bonne méthode pour trouver une valeur correcte consiste à effectuer plusieurs
  93. tests avec les documents les plus volumineux que vous vous attendez à voir figurer dans
  94. l'index.<footnote>
  95. <para><code>memory_get_usage()</code> et <code>memory_get_peak_usage()</code>
  96. peuvent être utilisées pour contrôler l'utilisation de la mémoire.</para>
  97. </footnote>. Une bonne pratique consiste à ne pas utiliser plus de la moitié de la
  98. mémoire allouée.</para>
  99. <para><emphasis>MaxMergeDocs</emphasis> limite la taille d'un segment (en termes de nombre
  100. de documents). De ce fait, il limite également la durée de l'auto-optimisation en
  101. garantissant que la méthode <code>addDocument()</code> ne sera pas exécutée plus d'un
  102. certain nombre de fois. C'est très important dans le cadre d'applications
  103. interactives.</para>
  104. <para>Diminuer la valeur du paramètre <emphasis>MaxMergeDocs</emphasis> peut aussi améliorer
  105. les performances lors de l'indexation en masse ("batch indexing"). L'auto-optimisation est
  106. un processus itératif et utilise une technique ascendante. Les petits segments sont
  107. fusionnés vers de plus gros segments qui sont eux-mêmes fusionnés vers des segments encore
  108. plus gros, etc. L'optimisation complète est achevée lorsqu'il ne reste qu'un seul gros
  109. segment.</para>
  110. <para>De petits segments détériore généralement la qualité de l'index. Un grand nombre de
  111. petits segments peut aussi déclencher l'erreur "Too many open files" déterminée par les
  112. limitations du système d'exploitation<footnote>
  113. <para><classname>Zend_Search_Lucene</classname> maintient chaque segment ouvert pour améliorer
  114. les performances de recherche.</para>
  115. </footnote>.</para>
  116. <para>En général, l'optimisation d'index en arrière-plan devrait être effectuée pour les
  117. modes d'indexation interactifs et la valeur de <emphasis>MaxMergeDocs</emphasis> ne devrait
  118. pas être trop faible pour les indexations de masse ("batch indexing").</para>
  119. <para><emphasis>MergeFactor</emphasis> affecte la fréquence d'auto-optimisation. De faibles
  120. valeurs augmenteront la qualité des index non-optimisés. Des valeurs plus importantes
  121. amélioreront les performances de l'indexation, mais également le nombre de segments
  122. fusionnés. Ce qui peut également déclencher l'erreur "Too many open files".</para>
  123. <para><emphasis>MergeFactor</emphasis> groupe les segments d'index par leur taille :
  124. <orderedlist>
  125. <listitem>
  126. <para>Pas plus grand que <emphasis>MaxBufferedDocs</emphasis>.</para>
  127. </listitem>
  128. <listitem>
  129. <para>Plus grand que <emphasis>MaxBufferedDocs</emphasis>, mais pas plus grand
  130. que
  131. <emphasis>MaxBufferedDocs</emphasis>*<emphasis>MergeFactor</emphasis>.</para>
  132. </listitem>
  133. <listitem>
  134. <para>Plus grand que
  135. <emphasis>MaxBufferedDocs</emphasis>*<emphasis>MergeFactor</emphasis>, mais pas
  136. plus grand que
  137. <emphasis>MaxBufferedDocs</emphasis>*<emphasis>MergeFactor</emphasis>*<emphasis>MergeFactor</emphasis>.</para>
  138. </listitem>
  139. <listitem>
  140. <para>...</para>
  141. </listitem>
  142. </orderedlist></para>
  143. <para><classname>Zend_Search_Lucene</classname> vérifie à chaque appel de <code>addDocument()</code>
  144. si la fusion de n'importe quel segment pour déplacer le segment nouvellement créé dans le
  145. groupe suivant. Si c'est le cas, la fusion est effectuée.</para>
  146. <para>Ainsi, un index avec N groupes peut contenir <emphasis>MaxBufferedDocs</emphasis> +
  147. (N-1)*<emphasis>MergeFactor</emphasis> segments et contient au moins
  148. <emphasis>MaxBufferedDocs</emphasis>*<emphasis>MergeFactor</emphasis><superscript>(N-1)</superscript>
  149. documents.</para>
  150. <para>La formule ci-dessous donne une bonne approximation du nombre de segments dans un
  151. index :</para>
  152. <para><emphasis>Nombre de segments</emphasis> &lt;= <emphasis>MaxBufferedDocs</emphasis> +
  153. <emphasis>MergeFactor</emphasis>*log <subscript><emphasis>MergeFactor</emphasis></subscript>
  154. (<emphasis>Nombre de documents</emphasis>/<emphasis>MaxBufferedDocs</emphasis>)</para>
  155. <para><emphasis>MaxBufferedDocs</emphasis> est déterminé par la mémoire allouée. Cela permet
  156. pour le facteur de fusion (MergeFactor) approprié d'avoir un nombre raisonnable de
  157. segments.</para>
  158. <para>L'optimisation du paramètre <emphasis>MergeFactor</emphasis> est plus efficace pour
  159. les performances de l'indexation de masse (batch indexing) que
  160. <emphasis>MaxMergeDocs</emphasis>. Mais cette méthode manque un peu de finesse. Le mieux est
  161. d'utiliser l'estimation ci-dessus pour optimiser <emphasis>MergeFactor</emphasis>, puis de
  162. jouer avec <emphasis>MaxMergeDocs</emphasis> pour obtenir les meilleures performances
  163. d'indexation de masse (batch indexing).</para>
  164. </sect2>
  165. <sect2 id="zend.search.lucene.best-practice.shutting-down">
  166. <title>Indexation à l'arrêt du programme</title>
  167. <para>L'instance de <classname>Zend_Search_Lucene</classname> effectue quelques tâches à la sortie du
  168. programme si des documents ont été ajoutés à l'index mais pas écrits dans un nouveau
  169. segment.</para>
  170. <para>Elle peut également déclencher le processus d'auto-optimisation.</para>
  171. <para>L'objet qui représente l'index est automatiquement fermé lorsque lui, ainsi que tous
  172. les objets de résultats de requête qui lui sont associés, sont hors de portée du script
  173. principal.</para>
  174. <para>Si l'objet d'index est stocké dans une variable globale, il ne sera fermé qu'à la fin
  175. de l'exécution du script<footnote>
  176. <para>Cela peut aussi se produire s'il y a une référence à l'index ou à l'un de ses
  177. résultats de recherche dans une structure de données cyclique, car le
  178. ramasse-miettes de PHP ne récupère les objets avec des références cycliques qu'en
  179. fin d'exécution de script</para>
  180. </footnote>.</para>
  181. <para>Le processus d'exception de PHP est également fermé à ce moment.</para>
  182. <para>Cela n'empêche pas la fermeture normale du processus de l'index, mais cela peut
  183. empêcher un diagnostic d'erreur précis si une erreur survient durant la fermeture.</para>
  184. <para>Il y a deux moyens qui peuvent permettre d'éviter ce problème.</para>
  185. <para>Le premier est de forcer l'index à sortir de la portée (du scope) :<programlisting
  186. role="php"><![CDATA[
  187. $index = Zend_Search_Lucene::open($indexPath);
  188. ...
  189. unset($index);
  190. ]]></programlisting></para>
  191. <para>Le second est d'effectuer une opération de commit avant la fin du script exécution
  192. :<programlisting role="php"><![CDATA[
  193. $index = Zend_Search_Lucene::open($indexPath);
  194. $index->commit();
  195. ]]></programlisting> Cette possibilité est également décrite dans la section "<link
  196. linkend="zend.search.lucene.advanced.static">Avancé - Utiliser les propriétés statiques de
  197. l'index</link>".</para>
  198. </sect2>
  199. <sect2 id="zend.search.lucene.best-practice.unique-id">
  200. <title>Récupération de documents par leur id unique</title>
  201. <para>C'est une pratique commune de stocker un identifiant unique de document dans l'index.
  202. Par exemple, une url, un chemin ou un identifiant tiré d'une base de données.</para>
  203. <para><classname>Zend_Search_Lucene</classname> fournit une méthode <code>termDocs()</code> pour
  204. récupérer des documents contenant les termes spécifiés.</para>
  205. <para>C'est plus efficace que d'utiliser la méthode <code>find()</code> :<programlisting
  206. role="php"><![CDATA[
  207. // Récupération de documents avec la méthode find()
  208. // en utilisant une querystring
  209. $query = $idFieldName . ':' . $docId;
  210. $hits = $index->find($query);
  211. foreach ($hits as $hit) {
  212. $title = $hit->title;
  213. $contents = $hit->contents;
  214. ...
  215. }
  216. ...
  217. // Récupération de documents avec la méthode find()
  218. // en utilisant l'API de requête.
  219. $term = new Zend_Search_Lucene_Index_Term($docId, idFieldName);
  220. $query = new Zend_Search_Lucene_Search_Query_Term($term);
  221. $hits = $index->find($query);
  222. foreach ($hits as $hit) {
  223. $title = $hit->title;
  224. $contents = $hit->contents;
  225. ...
  226. }
  227. ...
  228. // Récupération de documents avec la méthode termDocs()
  229. $term = new Zend_Search_Lucene_Index_Term($docId, idFieldName);
  230. $docIds = $index->termDocs($term);
  231. foreach ($docIds as $id) {
  232. $doc = $index->getDocument($id);
  233. $title = $doc->title;
  234. $contents = $doc->contents;
  235. ...
  236. }
  237. ]]></programlisting></para>
  238. </sect2>
  239. <sect2 id="zend.search.lucene.best-practice.memory-usage">
  240. <title>Utilisation de la mémoire</title>
  241. <para><classname>Zend_Search_Lucene</classname> est un module relativement gourmand en mémoire.</para>
  242. <para>Il utilise la mémoire pour mettre en cache certaines informations et optimiser la
  243. recherche, ainsi que les performances de l'indexation.</para>
  244. <para>La mémoire requise diffère selon les modes.</para>
  245. <para>L'index du dictionnaire des termes est chargé durant la recherche. Il s'agit de chaque
  246. 128<superscript>ème</superscript><footnote>
  247. <para>Le format de fichier Lucene permet de configurer ce nombre, mais
  248. <classname>Zend_Search_Lucene</classname> n'expose pas cette possibilité dans l'API. Cependant
  249. vous pouvez toujours configurer ce nombre si l'index est géré par une autre
  250. implémentation de Lucene.</para>
  251. </footnote> terme du dictionnaire complet.</para>
  252. <para>De fait, la consommation de mémoire augmente si vous avez un grand nombre de termes
  253. uniques. Cela peut arriver si vous utilisez des phrases non "tokenizées" comme champ de
  254. recherche ou que vous indexez un large volume d'informations non-textuelles.</para>
  255. <para>Un index non-optimisé consiste en plusieurs segments. Cela augmente également
  256. l'utilisation de mémoire. Les segments étant indépendants, chacun possède son propre
  257. dictionnaire de termes et index de dictionnaire de termes. Si un index consiste en
  258. <emphasis>N</emphasis> segments, il risque, dans le pire des cas, de multiplier par
  259. <emphasis>N</emphasis> la consommation de mémoire. Lancez l'optimisation de l'index en
  260. fusionnant tous les segments afin d'éviter de telles consommations de mémoire.</para>
  261. <para>L'indexation utilise la même quantité de mémoire que la recherche, plus de la mémoire
  262. pour mettre les documents en tampon. La quantité de mémoire peut être gérée par le paramètre
  263. <emphasis>MaxBufferedDocs</emphasis>.</para>
  264. <para>L'optimisation d'index (complète ou partielle) utilise un processus de type flux
  265. ("streaming") et ne requiert pas une grosse quantité de mémoire.</para>
  266. </sect2>
  267. <sect2 id="zend.search.lucene.best-practice.encoding">
  268. <title>Encodage</title>
  269. <para><classname>Zend_Search_Lucene</classname> travaille avec des chaînes en UTF-8 en interne. Ainsi
  270. toutes les chaînes de caractères retournée par <classname>Zend_Search_Lucene</classname> sont encodées
  271. en UTF-8.</para>
  272. <para>Vous ne devriez pas être concernés par l'encodage si vous travaillez avec des chaîne
  273. purement ASCII, mais vous devez être prudent si ce n'est pas le cas.</para>
  274. <para>Un mauvais encodage peut causer des notices (erreur) durant la conversation
  275. d'encodage, voire la perte de données.</para>
  276. <para><classname>Zend_Search_Lucene</classname> offre un large éventail de possibilités d'encodage
  277. pour les documents indexés et les requêtes analysées.</para>
  278. <para>L'encodage peut être explicitement spécifié en passant un paramètre optionnel à la
  279. méthode de création d'un champ :<programlisting role="php"><![CDATA[
  280. $doc = new Zend_Search_Lucene_Document();
  281. $doc->addField(Zend_Search_Lucene_Field::Text('title',
  282. $title,
  283. 'iso-8859-1'));
  284. $doc->addField(Zend_Search_Lucene_Field::UnStored('contents',
  285. $contents,
  286. 'utf-8'));
  287. ]]></programlisting> C'est le meilleur moyen d'éviter toute ambiguïté dans les encodages
  288. utilisés.</para>
  289. <para>Si le paramètre optionnel de l'encodage est omis, la locale courante est utilisée. La
  290. locale courante peut contenir des données d'encodage en plus des spécification de langue
  291. :<programlisting role="php"><![CDATA[
  292. setlocale(LC_ALL, 'fr_FR');
  293. ...
  294. setlocale(LC_ALL, 'de_DE.iso-8859-1');
  295. ...
  296. setlocale(LC_ALL, 'ru_RU.UTF-8');
  297. ...
  298. ]]></programlisting></para>
  299. <para>La même approche est utilisée pour définir l'encodage des chaînes de requête.</para>
  300. <para>Si l'encodage n'est pas spécifié, la locale courante est utilisée pour le
  301. déterminer.</para>
  302. <para>L'encodage peut être passée comme paramètre optionnel si la requête est analysée
  303. explicitement avant la recherche :<programlisting role="php"><![CDATA[
  304. $query =
  305. Zend_Search_Lucene_Search_QueryParser::parse($queryStr, 'iso-8859-5');
  306. $hits = $index->find($query);
  307. ...
  308. ]]></programlisting></para>
  309. <para>L'encodage par défaut peut également être spécifié avec la méthode
  310. <code>setDefaultEncoding()</code> :<programlisting role="php"><![CDATA[
  311. Zend_Search_Lucene_Search_QueryParser::setDefaultEncoding('iso-8859-1');
  312. $hits = $index->find($queryStr);
  313. ...
  314. ]]></programlisting>Une chaîne vide sous-entend "locale courante".</para>
  315. <para>Si l'encodage correct est spécifié, il pourra être correctement interprété par
  316. l'analyseur. Le comportement dépend de quel analyseur est utilisé. Consultez la section sur
  317. les <link linkend="zend.search.lucene.charset">Jeu de caractères</link> pour plus de
  318. détails.</para>
  319. </sect2>
  320. <sect2 id="zend.search.lucene.best-practice.maintenance">
  321. <title>Maintenance de l'index</title>
  322. <para>Il devrait être clair que <classname>Zend_Search_Lucene</classname> comme toute autre
  323. implémentation de Lucene ne comporte pas de "base de données".</para>
  324. <para>Les index ne devrait pas être utilisés pour du stockage de données. Ils ne fournissent
  325. pas de fonctionnalités de backup/restauration partiel, journalisation, logs, transactions et
  326. beaucoup d'autres fonctionnalités assimilées aux systèmes de gestion de bases de
  327. données.</para>
  328. <para>Cependant, <classname>Zend_Search_Lucene</classname> tente de garder ses index dans un état
  329. constant en tout temps.</para>
  330. <para>Le backup et la restauration d'un index devrait être effectué en copiant le contenu du
  331. répertoire de l'index.</para>
  332. <para>Si pour une raison quelconque, un index devait être corrompu, il devrait être restauré
  333. ou complètement reconstruit.</para>
  334. <para>C'est donc une bonne idée de faire des backups des gros index et de stocker les logs
  335. de modifications pour pouvoir effectuer des restaurations manuelles et des opérations de
  336. "roll-forward" si nécessaire. Cette pratique réduit considérablement le temps de
  337. restauration.</para>
  338. </sect2>
  339. </sect1>