Carnet Wiki

Version 6 — Février 2017 — LaurentP

L’indexation de la date dans le plugin Indexer se fait au format UNIX_TIMESTAMP, ce qui n’est pas sans causer quelques soucis.

Je documente ci-dessous ce que j’ai fait, mais si quelqu’un a une meilleure idée on peut essayer d’améliorer ça.

1. Le problème

Sur les 4 installations de Sphinx que j’ai, le champ date est stocké au format unix, à savoir le nombre de secondes écoulé depuis EPOCH (1er janvier 1970).

Cette date est calculée via dans lib/Indexer/Storage/Sphinx.php via date = strtotime($doc->date).

Pour les dates antérieures au 1er janv. 1970 PHP donne un nombre négatif (alors que MySQL va donner ) ; donc ici, on a date = nombre négatif.

Mais le champ sphinx est un entier non signé, donc les nombres négatifs sont représentés en ajoutant le bit 2^32.

2. La solution trouvée pour le moment

Tant que sphinx stocke ses dates en 32 bits ([cf forum sphinx-search->http://sphinxsearch.com/forum/view.html?id=15237]), *Si on est en 32 bits * il faut donc retrancher 2^32 = 4294967296 à la valeur que nous renvoie sphinx, pour retrouver notre nombre négatif… On le fait pour les nombres > 2^31 = 2147483648 2992477296 , c’est-à-dire postérieurs au 19 janvier 2038 29 octobre 2064 .

Ce cutoff est choisi de façon un peu arbitraire, et permet d’avoir la plage de dates suivante :
-  de 0 à 2147483648 2992477296 = de 1er janv. 1970 à 19 janvier 2038 29 octobre 2064
-  de 2147483649 2992477297 à 4294967295 = de 13 décembre 1901 22 septembre 1928 au 31 décembre 1969.

Du coup dans le squelette on pourra utiliser la fonction suivante pour trier selon un ordre qui donne une priorité à la date, soit une priorité aboslue (si tri par date), soi une priorité relative (si tri par « pertinence », on veut quand même donner un bonus aux plus récents) :

[(#REM)


Définition de la fonction de score (tri des résultats)


On bidouille la date car le UNIX_TIMESTAMP
        est stupidement impossible avant 1er janvier 1970.


Cf. https://contrib.spip.net/Indexer-date-32bits


]


[(#ENV{tri}|=={date}|ou{[(#RECHERCHE|strlen|=={0})]}?{
        #SET{select,'*, IF (date > 2147483648 2992477296 , date-4294967296, date) as dateu'}
        #SET{tri,dateu}
        #SET{sens_tri,1}
,
        #SET{select,'*, WEIGHT() / (100+SQRT(SQRT(NOW()-(IF (date > 2147483648 2992477296 , date-4294967296, date))))) as poids'}
        #SET{tri,poids}
        #SET{sens_tri,1}
})]

3. Une meilleure solution ?

Si on veut pouvoir encoder une plage de dates plus large, il va falloir soit passer en 64 bits, soit utiliser un format de champ qui permette des nombres négatifs, soit oublier les secondes et diviser la date par 60. À moins d’une autre idée ??

4. Utiliser YEARMONTHDAY(date)

Il semble qu’on puisse utiliser YEARMONTHDAY(date) pour calculer une formule, et cela fonctionne de 1902 à 2038 sans rien changer à la structure de la base sphinx. Donc en attendant 2038, ça a l’air plus convaincant que la formule ci-dessus.

Avant la boucle :

        #SET{select, 'YEARMONTHDAY(date) as odate, *'}
        #SET{tri, odate}

et plus bas, dans la boucle :

{select #GET{select}}
{par #GET{tri}}{inverse}
{par date}{inverse}