ElasticSearch and the Mystery of Auto-completion

Few months ago, I was looking for information concerning auto-completion with ElasticSearch as a source. I found many solutions, but none of them really fit my needs. Nevertheless, it helped me building my request.

What’s the situation Doc?

I needed to be able to extract values from an indexed field in ElasticSearch according to a group of letters (user input). In fact, my indexed document contains a field named map which is an array of string. The idea is the following: if the user is looking for the value « name » for instance, let’s say he will first type « na ». So here we must be able to suggest searches to the user. Furthermore, with this 2 letters, we must proposed existing fields like « name », « native » or « nature ».

I’m using Elastical to interact with ElasticSearch.
First, I’m building a regex which will be used later in the request.

var search = req.body.searched.toLowerCase(),
firstLetter = search.charAt(0),
rest = req.body.searched.slice(1),
reg = "^["+firstLetter+firstLetter.toUpperCase()+"]"+rest+".*";

The regex is build to match with both an upper case char or a lower case one in first position.

What about the request?

var request = {
  query: {
    query_string: {
      default_field: "map",
      default_operator: "AND",
      query: req.body.searched+"*"
    }
  },
  facets:{
    map:{
      terms:{
        field: "map.exact",
        regex: reg,
        size: 10
      }
    }
  }
}

First, I’m asking ElasticSearch to retrieve all documents which match req.body.searched+ »* » where req.body.searched contains the user input. I’ve change the default operator to « AND » rather than « OR » in order to be able to deal with fields like « Nom de la gare » or « Name of the dog ». By default, ElasticSearch uses the « OR » operator, so it will ask for « name » OR « of » OR « the » OR « dog »; which is not what I wanted.

Then, I’m using facets to retrieve values in the field map of found documents matching the given regex. I’m using map.exact for the same reason I must use the « AND » operator.

This request works great with on the tests I’ve made. Remains to be seen if it can handle big indexes.

I can now ask ElasticSearch with Elastical and build a clean response:

elastical.search(request, function (err, results, full) {
  var terms = [];
  async.forEach(full.facets.map.terms, function(data, callback) {
    terms.push(data.term);
    callback();
  }, function(err) {
    res.send(terms);
  });
});

[PROJET] Cubiq Arcadeum

Comme vous l’avez peut-être déjà lu dans la partie réservée du site, l’actuel onglet « Cubiq Arcadeum ». Je développe depuis un petit moment déjà ce petit jeu. Le développement est bientôt fini. Système de score, musique, langue (Français, Anglais, Japonais, Allemand, Italien, Espagnol). Les six langues des pays dans lesquels sont édités les jeux du PSM !

La suite du programme, soumission du jeu et de son code au SCE. Le comité qui donne son accord ou nom à la publication du jeu sur le PSN.

Prochaine news plus tard !

 

 

[Gnome3] Evince en lecteur pdf par défaut

Pour une raison obscure, mon lecteur par défaut des documents pdf était configuré sur l’application Documents de Gnome, simple application de gestion de documents. Il restait possible de les lire en passant par l’option « Ouvrir avec » ou en passant par la console. Néanmoins, impossible de configurer Evince comme lecteur pdf par défaut, celui n’apparaissant pas dans la liste des programmes disponibles. Impossible de le trouver.

La cause de ce problème est un paramètre de configuration d’Evince nommé NoDisplay est valant par défaut true.Ceci explique que le programme ne soit pas disponible à l’affichage. Voilà donc la procédure pour faire d’Evince votre programme par défaut pour la lecture de document pdf:

  • Ouvrir /usr/share/applications/evince.desktop avec l’éditeur qui vous convient.
  • Trouver la ligne correspondant à NoDisplay=true.
  • Changer true en false et sauvegarder.
  • Faire un clic droit sur un pdf.
  • Propriétés
  • Ouvrir avec
  • Sélectionner Visionneur de documents.

Félicitations, Evince est maintenant votre lecteur de pdf par défaut! Vous pouvez à présent remettre le paramètre NoDisplay à true si vous le souhaitez.

En outre, ce problème a fait l’objet d’un rapport de bug ArchLinux.

 

 

[ArchLinux] Configurer le réseau

Directement tirées du wiki officiel, voici les commandes qui me sont très utiles pour configurer le réseau.

Filaire:

sudo ifconfig eth0 up
sudo ifconfig eth0 netmask 255.255.255.0 broadcast 192.168.255.255 192.168.102.1
sudo route add default gw 192.168.102.254

Wifi:

wifi-menu

NetworkManager:

sudo systemctl enable NetworkManager

Permet de lancer NetworkManager pour qu’il soit présent après le login.

Node-Elastical: Implement stats function

Recently, I had to find the size of an index in Elasticsearch. I could have just built my query using request like this:

var request = require('request');
request.get('http://localhost:9200/' + myIndex + '/_stats'
  , function (err, res, body) {
    console.log(res._all.primaries.store.size_in_bytes);
});

Fast and easy. Since it’s not available yet in node-elastical, I found it better to add this functionality so that other people can use it. Implementing the stats function wasn’t difficult. It just require parsing options to create the right url and then making the request (with request) to Elasticsearch. I used the Indices Stats API documentation to create the function.

In file client.js

stats: function (options, callback) {
  var query = [],
  url = '',
  hasOptions;

  if (typeof options === 'function') {
    callback = options;
    options = {};
  }
  //Create a copy of options so we can modify it.
  options = util.merge(options || {});

  if (options.index) {
    url = '/' + encode(Array.isArray(options.index) ?
      options.index.join(',') : options.index);
    delete options.index;
    //Look for types only if there is an index
    if (options.types) {
      query.push(encode('types') + '=' + encode(
        Array.isArray(options.types) ?
        options.types.join(',') : options.types));
    }
    delete options.types;
  }

  url += '/_stats';

  util.each(options, function (value, name) {
    if (value === true || value === false) {
      value = value ? '1' : '0';
    }

    query.push(encode(name) + '=' + encode(value));
  });

  if (query.length) {
    url += '?' + query.join('&');
  }

  this._request(url, {
    method: 'GET'
  }, function (err, res) {
    if(err) { return callback(err, null, res), undefined; }
    callback(null, res);
  });
},
In file index.js
stats: function (options, callback) {
  if (typeof options === 'function') {
    callback = options;
    options = {};
  }
  this.client.stats(util.merge(options,{index: this.name}), callback);
},