…Poney, Guinness et gif animé.
avec du code !
"(($wpdb->posts.post_title LIKE '{$n}{$term}{$n}')
OR ($wpdb->posts.post_content LIKE '{$n}{$term}{$n}'))";
WordPress has killer search baked in. Every word you write is fully searchable […]
{
"content": "Yolo",
"created_by": {
"name": "Damin0u"
}
}
Nous stockons les documents dans des types, eux-mêmes inclus dans un index.
http://localhost:9200/yolo/test/1
curl -XPOST "http://localhost:9200/yolo/test/1"
{
"post_date": "2013/10/30 13:37:00",
"content": "Elasticsearch is swag.",
"author": {
"name": "Damien",
}
}
curl -XPOST "http://localhost:9200/yolo/test/_search"
{
"query": {
"match_all" : {}
}
}
Chaque prestation d'un artiste est un document :
{
name: "Black Sabbath",
listeners: 2128492,
description: "Black Sabbath is an English heavy...",
festival: "Hellfest 2014",
year: 2014,
date: "2014, Sunday 22 June",
stage: "Main Stage 01",
tags: [
"heavy metal",
"hard rock",
"classic rock",
"metal"
]
}
composer require ruflin/Elastica dev-master
./composer.json has been updated
Loading composer repositories with package information
Updating dependencies (including require-dev)
- Installing ruflin/elastica (dev-master 62f63df)
Cloning 62f63df4d093f325b6848fa0bedae23da6ece3d7
<?php
include("vendor/autoload.php");
// default to localhost:9200, no config!
$elasticaClient = new \Elastica\Client();
/** @var $festivalIndex \Elastica\Index */
$festivalIndex = $elasticaClient->getIndex('festival');
$festivalIndex->exists(); // => false
<?php
$festivalIndex->create(
array(
'number_of_shards' => 1,
'number_of_replicas' => 0,
// Analysis here too
),
true // delete index if it already exists
);
/** @var $hellfestType \Elastica\Type */
$hellfestType = $festivalIndex->getType('hellfest');
$gigs = json_decode(file_get_contents("data-merged.json"), true);
// Slow
foreach ($gigs as $gig) {
$hellfestType->addDocument(
new \Elastica\Document('', $gig)
);
}
Une requête HTTP par Document !
$docs = array();
foreach ($gigs as $gig) {
$docs[] = new \Elastica\Document('', $gig);
}
// Only one request!
$hellfestType->addDocuments($docs);
Créez des bulks de taille raisonnable et abusez-en.
marvel.agent.enabled: false
GET festival/hellfest/_search
{
"query": {
"match": {
"name": "slayer"
}
}
}
GET INDEX/TYPE/_search
{
"query": {
"TYPE DE RECHERCHE": {
"PARAMÉTRES"
}
}
}
$elasticaClient = new \Elastica\Client(); // localhost:9200
/** @var $festivalIndex \Elastica\Index */
$festivalIndex = $elasticaClient->getIndex('festival');
/** @var $hellfestType \Elastica\Type */
$hellfestType = $festivalIndex->getType('hellfest');
À rendre disponible sous forme de service dans votre application.
$matchQuery = new \Elastica\Query\Match();
$matchQuery->setField('name', 'Slayer');
// Not mandatory
$searchQuery = new \Elastica\Query();
$searchQuery->setQuery($matchQuery);
$resultSet = $hellfestType->search($matchQuery);
Traduire de gauche à droite !
GET festival/hellfest/_search // getType()->search() { "query": { // new \Elastica\Query() "match": { // new \Elastica\Query\Match() "name": "slayer" // setField() } } }
Les namespaces respectent la hiérarchie.
{
"took": 1,
"timed_out": false,
"_shards": { ... },
"hits": {
"total": 4,
"max_score": 6.2214365,
"hits": [
{
"_index": "festival",
"_type": "hellfest",
"_id": "NPs8-jFuQUShDVSvQazOzQ",
"_score": 6.2214365,
"_source": {
"tags": [
"thrash metal", ...
],
"listeners": 1226045,
"description": "Slayer is a thrash metal band...",
"name": "Slayer" ...
}
}, ...
]
}
}
class Elastica\ResultSet { protected $_results => array(4) { [0] => class Elastica\Result { protected $_hit => array(5) { '_index' => "festival" '_type' => "hellfest" '_id' => "CwBTEcsnSgeVsvP52Wjurw" '_score' => double(6.2214365) '_source' => array(8) { ... } } }, ... } protected $_response => class Elastica\Response protected $_query => class Elastica\Query // gift! protected $_took => int(3) protected $_timedOut => bool(false) protected $_totalHits => int(4) protected $_maxScore => double(6.2214365) }
echo sprintf("Slayer a fait %d Hellfest :\n",
$resultSet->getTotalHits());
foreach ($resultSet->getResults() as $result)
{
/** @var $result \Elastica\Result */
$data = $result->getData();
echo $data['festival']."\n";
}
Slayer a fait 4 Hellfest :
Hellfest 2008 Open-Air Edition
Hellfest 2007
Hellfest 2010
Hellfest 2014
Et Elastica c'est facile...
$matchQuery->setField('name', 'Motorhead');
$resultSet = $hellfestType->search($matchQuery);
echo sprintf("Motorhead a fait %d Hellfest\n",
$resultSet->getTotalHits());
$matchQuery->setField('name', 'Motörhead');
$resultSet = $hellfestType->search($matchQuery);
echo sprintf("Motörhead a fait %d Hellfest\n",
$resultSet->getTotalHits());
Rien à voir avec Tolkien.
Séparer les contenus en morceaux facile à trouver.
$festivalIndex->create(array(
'number_of_shards' => 1,
'number_of_replicas' => 0,
'analysis' => array(
'analyzer' => array(
'my_awesome_analyzer' => array(
'type' => 'custom',
'tokenizer' => 'standard',
'filter' => array('lowercase', 'asciifolding')
),
)
)
),
true
);
$mapping = new \Elastica\Type\Mapping();
$mapping->setType($hellfestType);
$mapping->setProperties(array(
'name' => array(
'type' => 'string',
'analyzer' => 'my_awesome_analyzer',
'boost' => 3
),
'description' => array('type' => 'string', 'analyzer' => 'my_awesome_analyzer'),
'tags' => array('type' => 'string', 'boost' => 2, 'index' => 'not_analyzed'),
));
$mapping->send();
$matchQuery->setField('name', 'Motorhead');
$resultSet = $hellfestType->search($matchQuery);
echo sprintf("Motorhead a fait %d Hellfest\n",
$resultSet->getTotalHits());
J'aime le progressive metal mais je déteste le grindcore, quels groupes puis-je aller voir en 2014 ?
'tags' => array(
'type' => 'string',
'boost' => 2,
'index' => 'not_analyzed'
),
not_analyzed est parfait pour un tag, le token === le mot.
{
"term": {
"tags": {
"value": "progressive metal"
}
}
}
{
"term": {
"year": {
"value": 2014
}
}
}
{
"bool": {
"must": [
// Queries to match
],
"must_not": [
// Queries to not match
]
}
}
$progressiv = new \Elastica\Query\Term(array(
'tags' => array('value' => 'progressive metal')
));
$year = new \Elastica\Query\Term(array(
'year' => array('value' => 2014)
));
$grindcore = new \Elastica\Query\Term(array(
'tags' => array('value' => 'grindcore')
));
$boolQuery = new \Elastica\Query\Bool();
$boolQuery->addMust($progressiv);
$boolQuery->addMust($year);
$boolQuery->addMustNot($grindcore);
$resultSet = $hellfestMappingType->search($boolQuery);
echo sprintf("Il y a %d groupes que tu aime au Hellfest %d\n", $resultSet->getTotalHits(), 2014);
Elasticsearch est aussi un super moteur de statistiques !
Les facettes ne sont pas officiellement dépréciées, mais utilisez les agrégations !
GET festival/hellfest/_search
{
"size": 0,
"aggs": {
"by_year": {
"terms": {
"field": "year",
"size": 50
}
}
}
}
SELECT COUNT(*) FROM gigs GROUP BY year
$yearsAgg = new \Elastica\Aggregation\Terms("by_year");
$yearsAgg->setSize(50);
$yearsAgg->setField('year');
$search = new \Elastica\Query();
$search->setSize(0);
$search->addAggregation($yearsAgg);
$results = $type->search($search);
$years = $results->getAggregation('by_year');
Sky is the limit.
Pour les gros bourrins du bulk :
$index->setSettings(array(
"refresh_interval" => "-1"
));
$index->setSettings(array(
"refresh_interval" => "1s"
));
$index->optimize();
Faites toujours pointez vos recherches sur un alias !
Vous pouvez demander uniquement quelques champs de votre source
. Voir aucun.
$query = Query::create($myQuery);
$query->setFields(['_id']);
L'admin réseau vous dira merci.
<?php
use Elastica\Util;
echo Util::convertRequestToCurlCommand(
$type->getIndex()->getClient()->getLastRequest()
);
curl -XGET 'http://host:9200/index/type/_search' -d '{"query":{"query_string":{"query":"coucou"}}}'
This is madness!
new \Elastica\Query\Term();
new \Elastica\Filter\Term();
Bonne utilisation des use
.
use Elastica\Query as Query;
use Elastica\Filter as Filter;
// after
new Query\Term();
new Filter\Term();
Match
?$matching = new \Elastica\Query\Bool();
$matchQuery = new \Elastica\Query\Match();
$matchQuery->setField('name', $terms);
$mathing->addShould($matchQuery);
$matchQuery = new \Elastica\Query\Match();
$matchQuery->setField('pays', $terms);
$mathing->addShould($matchQuery);
$matchQuery = new \Elastica\Query\Match();
$matchQuery->setField('bio', $terms);
$mathing->addShould($matchQuery);
$matchQuery = new \Elastica\Query\Match();
$matchQuery->setField('job', $terms);
$mathing->addShould($matchQuery);
MultiMatch
\o/$matching = new Query\MultiMatch();
$matching->setQuery($terms);
$matching->setParam('use_dis_max', false);
$matching->setFields(array(
'name',
'bio',
'pays',
'job',
));
$matching = new Query\MultiMatch();
$matching->setQuery($terms);
$matching->setParam('use_dis_max', false);
$matching->setFields(array(
'name^10',
'bio^4',
'pays',
'job',
));
Bool
new Query\Bool();
Toujours utile pour ajouter des must
ou must_not
à la volée !
Pour copier/coller la doc !
$query = '{"query": {"match_all": {}}}';
$path = $index->getName() . '/' . $type->getName() . '/_search';
$response = $client->request($path, Request::GET, $query);
$responseArray = $response->getData();
Pas de ResultSet en retour ☹
Utilisez FOSElasticaBundle !
Elastica.io ne suffit pas.
https://github.com/ruflin/Elastica/tree/master/test/lib/Elastica/Test
Souvent quelques version de retard
sur Elasticsearch : default[:elasticsearch][:version] = "1.1.1"
env:
global:
- ES_WAIT_ON_MAPPING_CHANGE=true
Non documenté, mais permet de ne lancer les tests que sur un node prêt !
Elasticsearch, ce n'est pas que de la recherche.
Intro / PHP Weapon / Internet unicorn / Slayer / Animated gifs / Animated gifs / Veste a patch / Favicon
Elasticsearch, the definitive guide / Compilation de ressources pour débuter