ElasticSearch 102 est un workshop permettant de découvrir le driver Node.js pour ElasticSearch.
elasticsearch-102 par Benjamin CAVY et Sébastien PRUNIER est distribué sous les termes de la licence Creative Commons - Attribution - NonCommercial - ShareAlike.
Nous considérons que vous avez déjà réalisé les workshops suivants :
Vous allez également avoir besoin de Node.js. Si ce n'est pas déjà fait, installez node
et npm
sur votre machine.
Vérifiez les versions installées de node
(minimum v10.x
) et npm
(minimum v6.x
) :
node -v
v10.16.0
npm -v
6.9.0
Le jeu de données utilisé pour le workshop est un ensemble d'actrices et d'acteurs, issus de la base IMDb.
Plus précisément, deux fichiers nous servent de source de données :
Top_1000_Actors_and_Actresses.csv
est un fichier CSV contenant le Top 1000 des actrices et acteurs, depuis lequel nous pourrons extraire le nom de l'actrice ou de l'acteur, sa date de naissance et son identifiant IMDb.Top_1000_Actors_and_Actresses.json
est un fichier contenant une fiche détaillée au format JSON de chacun des 1000 actrices et acteur. Nous pourrons extraire de ce fichier une description, un lien vers une photos et une liste de métiers (acteur, réalisateur, producteur, etc...)
Ces deux fichiers sont disponibles dans le dossier src/data
.
Les exemples de code du workshop se basent sur le driver natif ElasticSearch pour Node.js. La version utilisée est la version 7.11.0.
L'avantage d'utiliser Node.js et le driver natif est que la syntaxe des requêtes du driver est quasiment identique à celles effectuées dans le shell.
La dépendance au driver elasticsearch est déjà présente dans le fichier package.json
, ainsi que la dépendance au module csv-parser
nécessaire pour
la suite :
"dependencies": {
"@elastic/elasticsearch": "^7.5.0",
"csv-parser": "^2.3.2"
}
L'objectif de cette première partie est d'alimenter un index imdb
à partir du
fichier CSV Top_1000_Actors_and_Actresses.csv
.
Pour cela nous nous appuyons sur le module csv-parser
pour lire le fichier CSV
et sur
l'api bulk d'elasticsearch,
qui va permettre l'insertion de tous les documents en un seul appel :
const csv = require('csv-parser');
const fs = require('fs');
const { Client } = require('@elastic/elasticsearch');
const client = new Client({ node: 'http://localhost:9200' });
// Création de l'indice
client.indices.create({ index: 'imdb' }, (err, resp) => {
if (err) console.trace(err.message);
});
let actors = [];
fs
.createReadStream('./data/Top_1000_Actors_and_Actresses.csv')
.pipe(csv())
// Pour chaque ligne on créé un document JSON pour l'acteur correspondant
.on('data', data => {
actors.push({
imdb_id: data.imdb_id,
name: data.name,
birth_date: data.birth_date
});
})
// A la fin on créé l'ensemble des acteurs dans ElasticSearch
.on('end', () => {
client.bulk(createBulkInsertQuery(actors), (err, resp) => {
if (err) console.trace(err.message);
else console.log(`Inserted ${resp.body.items.length} actors`);
client.close();
});
});
// Fonction utilitaire permettant de formatter les données pour l'insertion "bulk" dans elastic
function createBulkInsertQuery(actors) {
const body = actors.reduce((acc, actor) => {
const { name, birth_date } = actor;
acc.push({ index: { _index: 'imdb', _type: '_doc', _id: actor.imdb_id } })
acc.push({ name, birth_date })
return acc
}, []);
return { body };
}
Ce code est disponible dans le fichier src/insert_actors.js
. Vous pouvez
l'exécuter afin d'alimenter une première fois la base :
cd src
# A ne lancer qu'une seule fois pour récupérer les dépendances
npm install
node insert_actors.js
L'objectif de cette seconde partie est de compléter chaque document de la
collection actors
à partir des données du fichier
Top_1000_Actors_and_Actresses.json
.
Pour cela, nous nous appuyons à nouveau sur l'api bulk.
const fs = require('fs');
const { Client } = require('@elastic/elasticsearch');
const client = new Client({ node: 'http://localhost:9200' });
fs.readFile(
'./data/Top_1000_Actors_and_Actresses.json',
'utf8',
(err, data) => {
const actorsToUpdate = data
.split('\n')
// Chaque ligne correspond à un document JSON décrivant un acteur en détail
.map(line => JSON.parse(line))
// On transforme chaque ligne en requête de mise à jour qui sera utilisée dans un 'bulkWrite()'
.map(actor => actor.data);
client.bulk(createBulkUpdateQuery(actorsToUpdate), (err, resp) => {
if (err) console.trace(err.message);
else console.log(`Updated ${resp.body.items.length} actors`);
client.close();
});
}
);
function createBulkUpdateQuery(actors) {
const body = actors.reduce((acc, actor) => {
const {
description = 'No description provided',
image,
occupation
} = actor;
acc.push({ update: { _index: 'imdb', _type: '_doc', _id: actor.id } })
acc.push({
doc: {
description: description.replace(
' See full bio »',
''
),
image,
occupation
}
})
return acc
}, []);
return { body };
}
Ce code est disponible dans le fichier src/update_actors.js
. Vous pouvez
l'exécuter :
cd src
# A ne lancer qu'une seule fois pour récupérer les dépendances
npm install
node update_actors.js
A vous de jouer pour exécuter quelques requêtes intéressantes sur les données !
Par exemple pour récupérer l'acteur le plus vieux du Top 1000 :
client
.search({
index: 'imdb',
body: {
size: 1,
sort: [{ birth_date: 'asc' }]
}
})
.then(resp => console.log(resp.body.hits.hits[0]._source.name));
Autre exemple pour compter le nombre d'acteurs qui sont aussi des producteurs :
client
.count({
index: 'imdb',
body: {
query: {
term: {
occupation: {
value: 'producer'
}
}
}
}
})
.then(resp => {
console.log(resp.body.count);
});