Tutoriel pour comprendre les agrégations avec ElasticSearch

| Netapsys | David Boujot |

Ce tutoriel vous apprend à comprendre les agrégations avec ElasticSearch.

Pour réagir au contenu de cet article, un espace de dialogue vous est proposé sur le forum Commentez .

- Titre : Tutoriel pour comprendre les agrégations avec ElasticSearch

- Auteurs : Netapsys, David Boujot

- Parution : 18 mai 2020

- Licence : Copyright ® 2020 Netapsys. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.

Sommaire

- I. Introduction

- II. Avant de commencer

- II-A. Installation

- II-B. Indexation des données

- III. Agrégeons

- III-A. Agrégation « bucket »

- III-A-1. Agrégation simple

- III-A-2. Agrégation type « histogram »

- III-A-3. Agrégation par tranche

- III-B. Agrégations « metric »

- III-C. Les sous-agrégations

- IV. Conclusion

- V. Remerciements

I - Introduction

Tout d'abord quelques mots sur Elasticsearch pour la culture. Elasticsearch est basé sur la bibliothèque Apache Lucene. Il fournit tout le nécessaire à l'indexation de documents et à l'exécution des requêtes de recherche.

Elasticsearch facilite l'utilisation de Lucene en intégrant la bibliothèque dans une application Java modulaire, facilement configurable et capable de fonctionner en cluster.

En plus d'offrir des fonctionnalités avancées de recherche, Elasticsearch permet également de faire des agrégations sur vos données, c'est à dire d’effectuer des calculs sur vos données indexées pour par exemple faire un top 5 par catégorie avec le total correspondant, faire une répartition par date, faire une répartition par volume... Nous allons immédiatement voir tout ceci d'un peu plus près.

II - Avant de commencer

II-A - Installation

On va tout d'abord installer tout ce dont nous avons besoin, pour cela assurez-vous d'avoir un JDK 7 minimum installé et la variable JAVA_HOME configurée.

Ensuite, vous devez télécharger les binaires pour Elasticsearch et Logstash, ce dernier nous permettra d'envoyer nos données dans Elasticsearch pour l'indexation. Pour Logstash si vous n'avez pas JRuby installé sur votre poste, il vous le faudra également.

Les installations d'Elasticsearch et Logstash sont très simples, car il vous suffit de décompresser les archives. Pour faciliter la saisie des requêtes Elasticsearch, je vous conseille d'installer une extension pour Chrome qui s'appelle Sense.

II-B - Indexation des données

Le jeu de données qui sera utilisé dans cet article provient des données en libre utilisation de la ville de New York sur les informations concernant les accidents de circulation. Téléchargez le fichier à cette adresse : https://data.cityofnewyork.us/api/views/h9gi-nx95/rows.csv?accessType=DOWNLOAD. Il s'agit d'un fichier CSV qui fait la bagatelle de 135Mo.

Une fois le fichier CSV téléchargé, déplacez ce dernier dans le répertoire « bin » de votre installation Logstash et faites de même avec ce fichier : nyc_collision_logstash. Ouvrez une fenêtre de commande à l'emplacement du dossier « bin » puis exécutez la commande suivante :

cat nyc_collision_data.csv | logstash -f nyc_collision_logstash.conf

Et maintenant, vous pouvez prendre un café, car il faut compter une dizaine de minutes pour indexer l'intégralité du fichier CSV. Quand l'indexation est terminée, la commande s'arrête d'elle-même. Pour vérifier que tout s'est bien passé, vous pouvez, dans Sense, lancer cette requête  :

GET /nyc_visionzero/_count

Vous devriez avoir en résultat quelque chose comme cela :

1. {

2. "count": 735858,

3. "_shards": {

4. "total": 5,

5. "successful": 5,

6. "failed": 0

7. }

8. }

qui nous indique que notre index nyc_visionzero contient 735858 documents.

III - Agrégeons

III-A - Agrégation « bucket »

Les agrégations de type « bucket » sont des agrégations renvoyant plusieurs résultats, lesquels sont répartis dans un tableau de valeurs en fonction des critères définis dans la requête.

III-A-1 - Agrégation simple

Le premier exemple d'agrégation va nous retourner le nombre d'incidents répertoriés par arrondissement :

1. GET /nyc_visionzero/_search?search_type=count

2. {

3. "aggs" : {

4. "all_boroughs": {

5. "terms": {

6. "field": "borough"

7. }

8. }

9. }

10. }

On voit que notre agrégation est une recherche, _search, que nous typons pour obtenir uniquement les résultats de l'agrégation, search_type=count. Le corps de notre requête commence par le mot-clef aggs, c'est ce qui définit le début de notre agrégation. Le terme all_boroughs correspond au nom que nous donnons à notre agrégation. Et enfin, nous indiquons que notre agrégation est faite par rapport au champ borough.

Ce qui donne comme résultat :

1. {

2. "took": 109,

3. "timed_out": false,

4. "_shards": {

5. "total": 5,

6. "successful": 5,

7. "failed": 0

8. },

9. "hits": {

10. "total": 735858,

11. "max_score": 0,

12. "hits": []

13. },

14. "aggregations": {

15. "all_boroughs": {

16. "doc_count_error_upper_bound": 0,

17. "sum_other_doc_count": 0,

18. "buckets": [

19. {

20. "key": "BROOKLYN",

21. "doc_count": 170884

22. },

23. {

24. "key": "MANHATTAN",

25. "doc_count": 147516

26. },

27. {

28. "key": "QUEENS",

29. "doc_count": 143586

30. },

31. {

32. "key": "BRONX",

33. "doc_count": 71048

34. },

35. {

36. "key": "STATEN ISLAND",

37. "doc_count": 26401

38. }

39. ]

40. }

41. }

42. }

On retrouve le nom de notre agrégation all_boroughs et la répartition des résultats par arrondissement est restituée dans le tableau buckets, dans lequel on voit, pour chaque arrondissement, le nombre d'incidents enregistrés.

III-A-2 - Agrégation type « histogram »

Parmi la multitude d'agrégations disponibles, il y a des agrégations permettant d'avoir les résultats répartis suivant un intervalle donné, par exemple une répartition par mois ou une répartition par tranche de valeurs.

Exemple d'une répartition par mois :

1. GET nyc_visionzero/_search?search_type=count

2. {

3. "aggs" : {

4. "months": {

5. "date_histogram": {

6. "field": "@timestamp",

7. "interval": "month"

8. }

9. }

10. }

11. }

Dans ce cas, tous les incidents de circulation vont être répartis par mois, ce qui donne comme résultat :

1. {

2. "took": 30,

3. "timed_out": false,

4. "_shards": {

5. "total": 5,

6. "successful": 5,

7. "failed": 0

8. },

9. "hits": {

10. "total": 735858,

11. "max_score": 0,

12. "hits": []

13. },

14. "aggregations": {

15. "months": {

16. "buckets": [

17. {

18. "key_as_string": "2012-06-01T00:00:00.000Z",

19. "key": 1338508800000,

20. "doc_count": 34

21. },

22. {

23. "key_as_string": "2012-07-01T00:00:00.000Z",

24. "key": 1341100800000,

25. "doc_count": 16968

26. },

27. {

28. "key_as_string": "2012-08-01T00:00:00.000Z",

29. "key": 1343779200000,

30. "doc_count": 17171

31. },

32. {

33. "key_as_string": "2012-09-01T00:00:00.000Z",

34. "key": 1346457600000,

35. "doc_count": 16498

36. }

37. ...

38. ]

39. }

40. }

41. }

On voit bien dans notre tableau buckets que chaque entrée correspond à un mois dont la valeur est retournée à la fois sous forme de chaîne de caractères, mais aussi de timestamp.

Exemple de répartition par valeur :

Ici nous recherchons les incidents en fonction du nombre de personnes blessées et nous demandons à répartir par tranche de 10.

1. GET nyc_visionzero/_search?search_type=count

2. {

3. "aggs" : {

4. "injuries": {

5. "histogram": {

6. "field": "number_of_persons_injured",

7. "interval": 10

8. }

9. }

10. }

11. }

Le résultat :

1. {

2. "took": 14,

3. "timed_out": false,

4. "_shards": {

5. "total": 5,

6. "successful": 5,

7. "failed": 0

8. },

9. "hits": {

10. "total": 735858,

11. "max_score": 0,

12. "hits": []

13. },

14. "aggregations": {

15. "injuries": {

16. "buckets": [

17. {

18. "key": 0,

19. "doc_count": 735766

20. },

21. {

22. "key": 10,

23. "doc_count": 83

24. },

25. {

26. "key": 20,

27. "doc_count": 5

28. },

29. {

30. "key": 30,

31. "doc_count": 1

32. },

33. {

34. "key": 40,

35. "doc_count": 1

36. }

37. ]

38. }

39. }

40. }

Comme vous pouvez le voir, ce type d'agrégation se prête parfaitement à l'alimentation d'un graphique où l'on reçoit directement les données à afficher.

III-A-3 - Agrégation par tranche

L'agrégation par tranche, range, permet d'obtenir le nombre d'incidents en bornant la recherche avec une valeur minimale et/ou une valeur maximale.

Exemple de recherche avec un range de type numérique :

1. GET nyc_visionzero/_search?search_type=count

2. {

3. "aggs" : {

4. "hour_ranges" : {

5. "range" : {

6. "field" : "hour_of_day",

7. "ranges" : [

8. { "to" : 9 },

9. { "from" : 0, "to" : 23 },

10. { "from" : 11, "to" : 14 },

11. { "from" : 20 }

12. ]

13. }

14. }

15. }

16. }

On recherche ici les incidents par tranche horaire :

- jusqu'à 9 heures du matin ;

- de 0 à 23h ;

- de 11 à 14h ;

- après 20h.

Le résultat :

1. {

2. "took": 34,

3. "timed_out": false,

4. "_shards": {

5. "total": 5,

6. "successful": 5,

7. "failed": 0

8. },

9. "hits": {

10. "total": 735858,

11. "max_score": 0,

12. "hits": []

13. },

14. "aggregations": {

15. "hour_ranges": {

16. "buckets": [

17. {

18. "key": "*-9.0",

19. "to": 9,

20. "to_as_string": "9.0",

21. "doc_count": 137521

22. },

23. {

24. "key": "0.0-23.0",

25. "from": 0,

26. "from_as_string": "0.0",

27. "to": 23,

28. "to_as_string": "23.0",

29. "doc_count": 716631

30. },

31. {

32. "key": "11.0-14.0",

33. "from": 11,

34. "from_as_string": "11.0",

35. "to": 14,

36. "to_as_string": "14.0",

37. "doc_count": 125284

38. },

39. {

40. "key": "20.0-*",

41. "from": 20,

42. "from_as_string": "20.0",

43. "doc_count": 101916

44. }

45. ]

46. }

47. }

48. }

Exemple de recherche avec un range de type date.

De la même façon que nous avons fait une agréation par tranche de valeurs numériques, on peut également faire cela avec des dates :

1. GET nyc_visionzero/_search?search_type=count

2. {

3. "aggs" : {

4. "date_ranges" : {

5. "range" : {

6. "field" : "@timestamp",

7. "format": "dd-MM-yyy",

8. "ranges": [

9. {

10. "from": "now-1d"

11. },

12. {

13. "from" : "01-01-2016"

14. },

15. {

16. "from" : "01-12-2015",

17. "to" : "31-12-2015"

18. }

19. ]

20. }

21. }

22. }

23. }

Ici on demande à avoir les incidents qui sont survenus :

- la veille ;

- depuis le 1er janvier 2016 jusqu'à maintenant ;

- du 01/12/2015 au 31/12/2015.

Le résultat :

1. {

2. "took": 28,

3. "timed_out": false,

4. "_shards": {

5. "total": 5,

6. "successful": 5,

7. "failed": 0

8. },

9. "hits": {

10. "total": 735858,

11. "max_score": 0,

12. "hits": []

13. },

14. "aggregations": {

15. "date_ranges": {

16. "buckets": [

17. {

18. "key": "01-12-2015-31-12-2015",

19. "from": 1448928000000,

20. "from_as_string": "01-12-2015",

21. "to": 1451520000000,

22. "to_as_string": "31-12-2015",

23. "doc_count": 18328

24. },

25. {

26. "key": "01-01-2016-*",

27. "from": 1451606400000,

28. "from_as_string": "01-01-2016",

29. "doc_count": 8263

30. },

31. {

32. "key": "20-01-2016-*",

33. "from": 1453288050362,

34. "from_as_string": "20-01-2016",

35. "doc_count": 42

36. }

37. ]

38. }

39. }

40. }

Je ne vais pas passer en revue toutes les agrégations de type bucket, mais vous pourrez en trouver la liste exhaustive dans la documentation du site d'Elasticsearch.

III-B - Agrégations « metric »

Jusqu'à présent, nos recherches nous retournent toujours le nombre total d'éléments trouvés en fonction des critères déclarés dans nos requêtes d'agrégations, parfait, mais on peut également demander à appliquer des calculs mathématiques tels que des moyennes, la valeur minimale, les n premiers... dans la documentation d'Elasticsearch vous aurez la liste complète des opérations possibles.

Exemple de recherche donnant le centroïde :

1. GET nyc_visionzero/_search?search_type=count

2. {

3. "query" : {

4. "match" : { "vehicle_type" : "TAXI" }

5. },

6. "aggs" : {

7. "centroid" : {

8. "geo_centroid" : { "field" : "coords" }

9. }

10. }

11. }

Cette requête se base sur les coordonnées de géolocalisation des incidents impliquant des taxis et nous donne le centroïde correspondant :

Le résultat :

1. {

2. "took": 12,

3. "timed_out": false,

4. "_shards": {

5. "total": 5,

6. "successful": 5,

7. "failed": 0

8. },

9. "hits": {

10. "total": 51843,

11. "max_score": 0,

12. "hits": []

13. },

14. "aggregations": {

15. "centroid": {

16. "location": {

17. "lat": 40.751348950388326,

18. "lon": -73.96526638571353

19. }

20. }

21. }

22. }

Exemple de recherche avec un résultat restituant les trois premiers :

1. GET nyc_visionzero/_search?search_type=count

2. {

3. "aggs": {

4. "top-tags": {

5. "terms": {

6. "field": "cross_street_name",

7. "size": 3

8. }

9. }

10. }

11. }

Nous demandons les noms des trois carrefours ayant le plus grand nombre d'incidents enregistrés.

Le résultat :

1. {

2. "took": 39,

3. "timed_out": false,

4. "_shards": {

5. "total": 5,

6. "successful": 5,

7. "failed": 0

8. },

9. "hits": {

10. "total": 735858,

11. "max_score": 0,

12. "hits": []

13. },

14. "aggregations": {

15. "top-tags": {

16. "doc_count_error_upper_bound": 2559,

17. "sum_other_doc_count": 600344,

18. "buckets": [

19. {

20. "key": "3 AVENUE",

21. "doc_count": 9428

22. },

23. {

24. "key": "BROADWAY",

25. "doc_count": 9296

26. },

27. {

28. "key": "2 AVENUE",

29. "doc_count": 8100

30. }

31. ]

32. }

33. }

34. }

III-C - Les sous-agrégations

Il est également possible d'imbriquer les agrégations et ainsi d'avoir des agrégations de type bucket au sein d'une agrégation bucket, idem pour les metric, mais aussi de mixer les deux types d'agrégations.

Exemple de sous-agrégation de type bucket :

1. GET /nyc_visionzero/_search?search_type=count

2. {

3. "aggs" : {

4. "all_boroughs": {

5. "terms": {

6. "field": "borough"

7. },

8. "aggs": {

9. "cause": {

10. "terms": {

11. "field": "contributing_factor_vehicle",

12. "size": 3

13. },

14. "aggs": {

15. "incidents_per_month": {

16. "date_histogram": {

17. "field": "@timestamp",

18. "interval": "month"

19. }

20. }

21. }

22. }

23. }

24. }

25. }

26. }

Cette requête va nous renvoyer, par arrondissement et par type, les incidents survenus pour chaque mois.

Le résultat :

1. {

2. "took": 538,

3. "timed_out": false,

4. "_shards": {

5. "total": 5,

6. "successful": 5,

7. "failed": 0

8. },

9. "hits": {

10. "total": 735858,

11. "max_score": 0,

12. "hits": []

13. },

14. "aggregations": {

15. "all_boroughs": {

16. "doc_count_error_upper_bound": 0,

17. "sum_other_doc_count": 0,

18. "buckets": [

19. {

20. "key": "BROOKLYN",

21. "doc_count": 170884,

22. "cause": {

23. "doc_count_error_upper_bound": 1518,

24. "sum_other_doc_count": 48917,

25. "buckets": [

26. {

27. "key": "Unspecified",

28. "doc_count": 102650,

29. "incidents_per_month": {

30. "buckets": [

31. {

32. "key_as_string": "2012-06-01T00:00:00.000Z",

33. "key": 1338508800000,

34. "doc_count": 5

35. },

36. {

37. "key_as_string": "2012-07-01T00:00:00.000Z",

38. "key": 1341100800000,

39. "doc_count": 2656

40. },

41. {

42. "key_as_string": "2012-08-01T00:00:00.000Z",

43. "key": 1343779200000,

44. "doc_count": 2663

45. },

46. {

47. "key_as_string": "2012-09-01T00:00:00.000Z",

48. "key": 1346457600000,

49. "doc_count": 2494

50. },

51. {

52. "key_as_string": "2012-10-01T00:00:00.000Z",

53. "key": 1349049600000,

54. "doc_count": 2607

55. },

56. {

57. "key_as_string": "2012-11-01T00:00:00.000Z",

58. "key": 1351728000000,

59. "doc_count": 2405

60. },

61. {

62. "key_as_string": "2012-12-01T00:00:00.000Z",

63. "key": 1354320000000,

64. "doc_count": 2653

65. },

66. {

67. "key_as_string": "2013-01-01T00:00:00.000Z",

68. "key": 1356998400000,

69. "doc_count": 2436

70. },

71. {

72. "key_as_string": "2013-02-01T00:00:00.000Z",

73. "key": 1359676800000,

74. "doc_count": 2353

75. },

76. {

77. "key_as_string": "2013-03-01T00:00:00.000Z",

78. "key": 1362096000000,

79. "doc_count": 2553

80. },

81. {

82. "key_as_string": "2013-04-01T00:00:00.000Z",

83. "key": 1364774400000,

84. "doc_count": 2643

85. },

86. {

87. "key_as_string": "2013-05-01T00:00:00.000Z",

88. "key": 1367366400000,

89. "doc_count": 2840

90. },

91. {

92. "key_as_string": "2013-06-01T00:00:00.000Z",

93. "key": 1370044800000,

94. "doc_count": 2860

95. },

96. {

97. "key_as_string": "2013-07-01T00:00:00.000Z",

98. "key": 1372636800000,

99. "doc_count": 2682

100. },

101. {

102. "key_as_string": "2013-08-01T00:00:00.000Z",

103. "key": 1375315200000,

104. "doc_count": 2593

105. },

106. {

107. "key_as_string": "2013-09-01T00:00:00.000Z",

108. "key": 1377993600000,

109. "doc_count": 2527

110. },

111. {

112. "key_as_string": "2013-10-01T00:00:00.000Z",

113. "key": 1380585600000,

114. "doc_count": 2674

115. },

116. {

117. "key_as_string": "2013-11-01T00:00:00.000Z",

118. "key": 1383264000000,

119. "doc_count": 2642

120. },

121. {

122. "key_as_string": "2013-12-01T00:00:00.000Z",

123. "key": 1385856000000,

124. "doc_count": 2614

125. },

126. {

127. "key_as_string": "2014-01-01T00:00:00.000Z",

128. "key": 1388534400000,

129. "doc_count": 2444

130. },

131. {

132. "key_as_string": "2014-02-01T00:00:00.000Z",

133. "key": 1391212800000,

134. "doc_count": 2260

135. },

136. {

137. "key_as_string": "2014-03-01T00:00:00.000Z",

138. "key": 1393632000000,

139. "doc_count": 2451

140. },

141. {

142. "key_as_string": "2014-04-01T00:00:00.000Z",

143. "key": 1396310400000,

144. "doc_count": 2386

145. },

146. {

147. "key_as_string": "2014-05-01T00:00:00.000Z",

148. "key": 1398902400000,

149. "doc_count": 2581

150. },

151. {

152. "key_as_string": "2014-06-01T00:00:00.000Z",

153. "key": 1401580800000,

154. "doc_count": 2607

155. },

156. {

157. "key_as_string": "2014-07-01T00:00:00.000Z",

158. "key": 1404172800000,

159. "doc_count": 2455

160. },

161. {

162. "key_as_string": "2014-08-01T00:00:00.000Z",

163. "key": 1406851200000,

164. "doc_count": 2181

165. },

166. {

167. "key_as_string": "2014-09-01T00:00:00.000Z",

168. "key": 1409529600000,

169. "doc_count": 2314

170. },

171. {

172. "key_as_string": "2014-10-01T00:00:00.000Z",

173. "key": 1412121600000,

174. "doc_count": 2260

175. },

176. {

177. "key_as_string": "2014-11-01T00:00:00.000Z",

178. "key": 1414800000000,

179. "doc_count": 2232

180. },

181. {

182. "key_as_string": "2014-12-01T00:00:00.000Z",

183. "key": 1417392000000,

184. "doc_count": 2177

185. },

186. {

187. "key_as_string": "2015-01-01T00:00:00.000Z",

188. "key": 1420070400000,

189. "doc_count": 2025

190. },

191. {

192. "key_as_string": "2015-02-01T00:00:00.000Z",

193. "key": 1422748800000,

194. "doc_count": 1771

195. },

196. {

197. "key_as_string": "2015-03-01T00:00:00.000Z",

198. "key": 1425168000000,

199. "doc_count": 2281

200. },

201. {

202. "key_as_string": "2015-04-01T00:00:00.000Z",

203. "key": 1427846400000,

204. "doc_count": 2054

205. },

206. {

207. "key_as_string": "2015-05-01T00:00:00.000Z",

208. "key": 1430438400000,

209. "doc_count": 2347

210. },

211. {

212. "key_as_string": "2015-06-01T00:00:00.000Z",

213. "key": 1433116800000,

214. "doc_count": 2297

215. },

216. {

217. "key_as_string": "2015-07-01T00:00:00.000Z",

218. "key": 1435708800000,

219. "doc_count": 2217

220. },

221. {

222. "key_as_string": "2015-08-01T00:00:00.000Z",

223. "key": 1438387200000,

224. "doc_count": 2391

225. },

226. {

227. "key_as_string": "2015-09-01T00:00:00.000Z",

228. "key": 1441065600000,

229. "doc_count": 2256

230. },

231. {

232. "key_as_string": "2015-10-01T00:00:00.000Z",

233. "key": 1443657600000,

234. "doc_count": 2341

235. },

236. {

237. "key_as_string": "2015-11-01T00:00:00.000Z",

238. "key": 1446336000000,

239. "doc_count": 2175

240. },

241. {

242. "key_as_string": "2015-12-01T00:00:00.000Z",

243. "key": 1448928000000,

244. "doc_count": 2219

245. },

246. {

247. "key_as_string": "2016-01-01T00:00:00.000Z",

248. "key": 1451606400000,

249. "doc_count": 1028

250. }

251. ]

252. }

253. },

254. {

255. "key": "Driver Inattention/Distraction",

256. "doc_count": 16634,

257. "incidents_per_month": {

258. "buckets": [

259. {

260. "key_as_string": "2012-06-01T00:00:00.000Z",

261. "key": 1338508800000,

262. "doc_count": 1

263. },

264. {

265. "key_as_string": "2012-07-01T00:00:00.000Z",

266. "key": 1341100800000,

267. "doc_count": 320

268. },

269. {

270. "key_as_string": "2012-08-01T00:00:00.000Z",

271. "key": 1343779200000,

272. "doc_count": 297

273. },

274. {

275. "key_as_string": "2012-09-01T00:00:00.000Z",

276. "key": 1346457600000,

277. "doc_count": 312

278. },

279. {

280. "key_as_string": "2012-10-01T00:00:00.000Z",

281. "key": 1349049600000,

282. "doc_count": 289

283. },

284. {

285. "key_as_string": "2012-11-01T00:00:00.000Z",

286. "key": 1351728000000,

287. "doc_count": 333

288. },

289. {

290. "key_as_string": "2012-12-01T00:00:00.000Z",

291. "key": 1354320000000,

292. "doc_count": 346

293. },

294. {

295. "key_as_string": "2013-01-01T00:00:00.000Z",

296. "key": 1356998400000,

297. "doc_count": 261

298. },

299. {

300. "key_as_string": "2013-02-01T00:00:00.000Z",

301. "key": 1359676800000,

302. "doc_count": 246

303. },

304. {

305. "key_as_string": "2013-03-01T00:00:00.000Z",

306. "key": 1362096000000,

307. "doc_count": 303

308. },

309. {

310. "key_as_string": "2013-04-01T00:00:00.000Z",

311. "key": 1364774400000,

312. "doc_count": 303

313. },

314. {

315. "key_as_string": "2013-05-01T00:00:00.000Z",

316. "key": 1367366400000,

317. "doc_count": 356

318. },

319. {

320. "key_as_string": "2013-06-01T00:00:00.000Z",

321. "key": 1370044800000,

322. "doc_count": 336

323. },

324. {

325. "key_as_string": "2013-07-01T00:00:00.000Z",

326. "key": 1372636800000,

327. "doc_count": 318

328. },

329. {

330. "key_as_string": "2013-08-01T00:00:00.000Z",

331. "key": 1375315200000,

332. "doc_count": 289

333. },

334. {

335. "key_as_string": "2013-09-01T00:00:00.000Z",

336. "key": 1377993600000,

337. "doc_count": 328

338. },

339. {

340. "key_as_string": "2013-10-01T00:00:00.000Z",

341. "key": 1380585600000,

342. "doc_count": 343

343. },

344. {

345. "key_as_string": "2013-11-01T00:00:00.000Z",

346. "key": 1383264000000,

347. "doc_count": 351

348. },

349. {

350. "key_as_string": "2013-12-01T00:00:00.000Z",

351. "key": 1385856000000,

352. "doc_count": 291

353. },

354. {

355. "key_as_string": "2014-01-01T00:00:00.000Z",

356. "key": 1388534400000,

357. "doc_count": 311

358. },

359. {

360. "key_as_string": "2014-02-01T00:00:00.000Z",

361. "key": 1391212800000,

362. "doc_count": 280

363. },

364. {

365. "key_as_string": "2014-03-01T00:00:00.000Z",

366. "key": 1393632000000,

367. "doc_count": 281

368. },

369. {

370. "key_as_string": "2014-04-01T00:00:00.000Z",

371. "key": 1396310400000,

372. "doc_count": 329

373. },

374. {

375. "key_as_string": "2014-05-01T00:00:00.000Z",

376. "key": 1398902400000,

377. "doc_count": 395

378. },

379. {

380. "key_as_string": "2014-06-01T00:00:00.000Z",

381. "key": 1401580800000,

382. "doc_count": 430

383. },

384. {

385. "key_as_string": "2014-07-01T00:00:00.000Z",

386. "key": 1404172800000,

387. "doc_count": 410

388. },

389. {

390. "key_as_string": "2014-08-01T00:00:00.000Z",

391. "key": 1406851200000,

392. "doc_count": 442

393. },

394. {

395. "key_as_string": "2014-09-01T00:00:00.000Z",

396. "key": 1409529600000,

397. "doc_count": 450

398. },

399. {

400. "key_as_string": "2014-10-01T00:00:00.000Z",

401. "key": 1412121600000,

402. "doc_count": 475

403. },

404. {

405. "key_as_string": "2014-11-01T00:00:00.000Z",

406. "key": 1414800000000,

407. "doc_count": 404

408. },

409. {

410. "key_as_string": "2014-12-01T00:00:00.000Z",

411. "key": 1417392000000,

412. "doc_count": 469

413. },

414. {

415. "key_as_string": "2015-01-01T00:00:00.000Z",

416. "key": 1420070400000,

417. "doc_count": 412

418. },

419. {

420. "key_as_string": "2015-02-01T00:00:00.000Z",

421. "key": 1422748800000,

422. "doc_count": 372

423. },

424. {

425. "key_as_string": "2015-03-01T00:00:00.000Z",

426. "key": 1425168000000,

427. "doc_count": 457

428. },

429. {

430. "key_as_string": "2015-04-01T00:00:00.000Z",

431. "key": 1427846400000,

432. "doc_count": 435

433. },

434. {

435. "key_as_string": "2015-05-01T00:00:00.000Z",

436. "key": 1430438400000,

437. "doc_count": 557

438. },

439. {

440. "key_as_string": "2015-06-01T00:00:00.000Z",

441. "key": 1433116800000,

442. "doc_count": 543

443. },

444. {

445. "key_as_string": "2015-07-01T00:00:00.000Z",

446. "key": 1435708800000,

447. "doc_count": 560

448. },

449. {

450. "key_as_string": "2015-08-01T00:00:00.000Z",

451. "key": 1438387200000,

452. "doc_count": 500

453. },

454. {

455. "key_as_string": "2015-09-01T00:00:00.000Z",

456. "key": 1441065600000,

457. "doc_count": 591

458. },

459. {

460. "key_as_string": "2015-10-01T00:00:00.000Z",

461. "key": 1443657600000,

462. "doc_count": 617

463. },

464. {

465. "key_as_string": "2015-11-01T00:00:00.000Z",

466. "key": 1446336000000,

467. "doc_count": 528

468. },

469. {

470. "key_as_string": "2015-12-01T00:00:00.000Z",

471. "key": 1448928000000,

472. "doc_count": 521

473. },

474. {

475. "key_as_string": "2016-01-01T00:00:00.000Z",

476. "key": 1451606400000,

477. "doc_count": 242

478. }

479. ]

480. }

481. },

482. {

483. "key": "Failure to Yield Right-of-Way",

484. "doc_count": 9373,

485. "incidents_per_month": {

486. "buckets": [

487. {

488. "key_as_string": "2012-07-01T00:00:00.000Z",

489. "key": 1341100800000,

490. "doc_count": 154

491. },

492. {

493. "key_as_string": "2012-08-01T00:00:00.000Z",

494. "key": 1343779200000,

495. "doc_count": 182

496. },

497. {

498. "key_as_string": "2012-09-01T00:00:00.000Z",

499. "key": 1346457600000,

500. "doc_count": 185

501. },

502. {

503. "key_as_string": "2012-10-01T00:00:00.000Z",

504. "key": 1349049600000,

505. "doc_count": 208

506. },

507. {

508. "key_as_string": "2012-11-01T00:00:00.000Z",

509. "key": 1351728000000,

510. "doc_count": 192

511. },

512. {

513. "key_as_string": "2012-12-01T00:00:00.000Z",

514. "key": 1354320000000,

515. "doc_count": 203

516. },

517. {

518. "key_as_string": "2013-01-01T00:00:00.000Z",

519. "key": 1356998400000,

520. "doc_count": 190

521. },

522. {

523. "key_as_string": "2013-02-01T00:00:00.000Z",

524. "key": 1359676800000,

525. "doc_count": 138

526. },

527. {

528. "key_as_string": "2013-03-01T00:00:00.000Z",

529. "key": 1362096000000,

530. "doc_count": 149

531. },

532. {

533. "key_as_string": "2013-04-01T00:00:00.000Z",

534. "key": 1364774400000,

535. "doc_count": 162

536. },

537. {

538. "key_as_string": "2013-05-01T00:00:00.000Z",

539. "key": 1367366400000,

540. "doc_count": 223

541. },

542. {

543. "key_as_string": "2013-06-01T00:00:00.000Z",

544. "key": 1370044800000,

545. "doc_count": 188

546. },

547. {

548. "key_as_string": "2013-07-01T00:00:00.000Z",

549. "key": 1372636800000,

550. "doc_count": 191

551. },

552. {

553. "key_as_string": "2013-08-01T00:00:00.000Z",

554. "key": 1375315200000,

555. "doc_count": 168

556. },

557. {

558. "key_as_string": "2013-09-01T00:00:00.000Z",

559. "key": 1377993600000,

560. "doc_count": 196

561. },

562. {

563. "key_as_string": "2013-10-01T00:00:00.000Z",

564. "key": 1380585600000,

565. "doc_count": 200

566. },

567. {

568. "key_as_string": "2013-11-01T00:00:00.000Z",

569. "key": 1383264000000,

570. "doc_count": 221

571. },

572. {

573. "key_as_string": "2013-12-01T00:00:00.000Z",

574. "key": 1385856000000,

575. "doc_count": 219

576. },

577. {

578. "key_as_string": "2014-01-01T00:00:00.000Z",

579. "key": 1388534400000,

580. "doc_count": 207

581. },

582. {

583. "key_as_string": "2014-02-01T00:00:00.000Z",

584. "key": 1391212800000,

585. "doc_count": 178

586. },

587. {

588. "key_as_string": "2014-03-01T00:00:00.000Z",

589. "key": 1393632000000,

590. "doc_count": 194

591. },

592. {

593. "key_as_string": "2014-04-01T00:00:00.000Z",

594. "key": 1396310400000,

595. "doc_count": 196

596. },

597. {

598. "key_as_string": "2014-05-01T00:00:00.000Z",

599. "key": 1398902400000,

600. "doc_count": 206

601. },

602. {

603. "key_as_string": "2014-06-01T00:00:00.000Z",

604. "key": 1401580800000,

605. "doc_count": 212

606. },

607. {

608. "key_as_string": "2014-07-01T00:00:00.000Z",

609. "key": 1404172800000,

610. "doc_count": 217

611. },

612. {

613. "key_as_string": "2014-08-01T00:00:00.000Z",

614. "key": 1406851200000,

615. "doc_count": 219

616. },

617. {

618. "key_as_string": "2014-09-01T00:00:00.000Z",

619. "key": 1409529600000,

620. "doc_count": 274

621. },

622. {

623. "key_as_string": "2014-10-01T00:00:00.000Z",

624. "key": 1412121600000,

625. "doc_count": 282

626. },

627. {

628. "key_as_string": "2014-11-01T00:00:00.000Z",

629. "key": 1414800000000,

630. "doc_count": 251

631. },

632. {

633. "key_as_string": "2014-12-01T00:00:00.000Z",

634. "key": 1417392000000,

635. "doc_count": 263

636. },

637. {

638. "key_as_string": "2015-01-01T00:00:00.000Z",

639. "key": 1420070400000,

640. "doc_count": 239

641. },

642. {

643. "key_as_string": "2015-02-01T00:00:00.000Z",

644. "key": 1422748800000,

645. "doc_count": 195

646. },

647. {

648. "key_as_string": "2015-03-01T00:00:00.000Z",

649. "key": 1425168000000,

650. "doc_count": 253

651. },

652. {

653. "key_as_string": "2015-04-01T00:00:00.000Z",

654. "key": 1427846400000,

655. "doc_count": 239

656. },

657. {

658. "key_as_string": "2015-05-01T00:00:00.000Z",

659. "key": 1430438400000,

660. "doc_count": 280

661. },

662. {

663. "key_as_string": "2015-06-01T00:00:00.000Z",

664. "key": 1433116800000,

665. "doc_count": 249

666. },

667. {

668. "key_as_string": "2015-07-01T00:00:00.000Z",

669. "key": 1435708800000,

670. "doc_count": 280

671. },

672. {

673. "key_as_string": "2015-08-01T00:00:00.000Z",

674. "key": 1438387200000,

675. "doc_count": 230

676. },

677. {

678. "key_as_string": "2015-09-01T00:00:00.000Z",

679. "key": 1441065600000,

680. "doc_count": 284

681. },

682. {

683. "key_as_string": "2015-10-01T00:00:00.000Z",

684. "key": 1443657600000,

685. "doc_count": 289

686. },

687. {

688. "key_as_string": "2015-11-01T00:00:00.000Z",

689. "key": 1446336000000,

690. "doc_count": 272

691. },

692. {

693. "key_as_string": "2015-12-01T00:00:00.000Z",

694. "key": 1448928000000,

695. "doc_count": 335

696. },

697. {

698. "key_as_string": "2016-01-01T00:00:00.000Z",

699. "key": 1451606400000,

700. "doc_count": 160

701. }

702. ]

703. }

704. }

705. ]

706. }

707. },

708. {

709. "key": "MANHATTAN",

710. "doc_count": 147516,

711. "cause": {

712. "doc_count_error_upper_bound": 1403,

713. "sum_other_doc_count": 58714,

714. "buckets": [

715. {

716. "key": "Unspecified",

717. "doc_count": 65289,

718. "incidents_per_month": {

719. "buckets": [

720. {

721. "key_as_string": "2012-06-01T00:00:00.000Z",

722. "key": 1338508800000,

723. "doc_count": 1

724. },

725. {

726. "key_as_string": "2012-07-01T00:00:00.000Z",

727. "key": 1341100800000,

728. "doc_count": 1963

729. },

730. {

731. "key_as_string": "2012-08-01T00:00:00.000Z",

732. "key": 1343779200000,

733. "doc_count": 1954

734. },

735. {

736. "key_as_string": "2012-09-01T00:00:00.000Z",

737. "key": 1346457600000,

738. "doc_count": 1869

739. },

740. {

741. "key_as_string": "2012-10-01T00:00:00.000Z",

742. "key": 1349049600000,

743. "doc_count": 1843

744. },

745. {

746. "key_as_string": "2012-11-01T00:00:00.000Z",

747. "key": 1351728000000,

748. "doc_count": 1702

749. },

750. {

751. "key_as_string": "2012-12-01T00:00:00.000Z",

752. "key": 1354320000000,

753. "doc_count": 1839

754. },

755. ...

756. ]

757. }

758. }

759. ]

760. }

761. }

762. ]

763. }

764. }

765. }

Exemple de sous-agrégation de type metric :

1. GET nyc_visionzero/_search?search_type=count

2. {

3. "aggs": {

4. "top-tags": {

5. "terms": {

6. "field": "cross_street_name",

7. "size": 3

8. },

9. "aggs": {

10. "top_tag_hits": {

11. "top_hits": {

12. "sort": [

13. {

14. "borough": {

15. "order": "asc"

16. }

17. }

18. ],

19. "_source": {

20. "include": [

21. "borough",

22. "contributing_factor_vehicle"

23. ]

24. },

25. "size" : 3

26. }

27. }

28. }

29. }

30. }

31. }

J'ai repris la requête sur les trois croisements ayant le plus grand nombre d'incidents dans laquelle je veux connaître le nom de l'arrondissement et le « top 3 » des incidents par croisement.

Le résultat :

1. {

2. "took": 112,

3. "timed_out": false,

4. "_shards": {

5. "total": 5,

6. "successful": 5,

7. "failed": 0

8. },

9. "hits": {

10. "total": 735858,

11. "max_score": 0,

12. "hits": []

13. },

14. "aggregations": {

15. "top-tags": {

16. "doc_count_error_upper_bound": 2559,

17. "sum_other_doc_count": 600344,

18. "buckets": [

19. {

20. "key": "3 AVENUE",

21. "doc_count": 9428,

22. "top_tag_hits": {

23. "hits": {

24. "total": 9428,

25. "max_score": null,

26. "hits": [

27. {

28. "_index": "nyc_visionzero",

29. "_type": "logs",

30. "_id": "AVJexYCEDCAjHU4IlkJd",

31. "_score": null,

32. "_source": {

33. "borough": "BRONX",

34. "contributing_factor_vehicle": "Unspecified"

35. },

36. "sort": [

37. "BRONX"

38. ]

39. },

40. {

41. "_index": "nyc_visionzero",

42. "_type": "logs",

43. "_id": "AVJexYecDCAjHU4Ilk-i",

44. "_score": null,

45. "_source": {

46. "borough": "BRONX",

47. "contributing_factor_vehicle": "Driver Inattention/Distraction"

48. },

49. "sort": [

50. "BRONX"

51. ]

52. },

53. {

54. "_index": "nyc_visionzero",

55. "_type": "logs",

56. "_id": "AVJexZx1DCAjHU4IlnJB",

57. "_score": null,

58. "_source": {

59. "borough": "BRONX",

60. "contributing_factor_vehicle": [

61. "Driver Inattention/Distraction",

62. "Prescription Medication"

63. ]

64. },

65. "sort": [

66. "BRONX"

67. ]

68. }

69. ]

70. }

71. }

72. },

73. {

74. "key": "BROADWAY",

75. "doc_count": 9296,

76. "top_tag_hits": {

77. "hits": {

78. "total": 9296,

79. "max_score": null,

80. "hits": [

81. {

82. "_index": "nyc_visionzero",

83. "_type": "logs",

84. "_id": "AVJexZ-sDCAjHU4Ilngz",

85. "_score": null,

86. "_source": {

87. "borough": "BRONX",

88. "contributing_factor_vehicle": "Driver Inattention/Distraction"

89. },

90. "sort": [

91. "BRONX"

92. ]

93. },

94. {

95. "_index": "nyc_visionzero",

96. "_type": "logs",

97. "_id": "AVJexiA2DCAjHU4IlzrA",

98. "_score": null,

99. "_source": {

100. "borough": "BRONX",

101. "contributing_factor_vehicle": "Unspecified"

102. },

103. "sort": [

104. "BRONX"

105. ]

106. },

107. {

108. "_index": "nyc_visionzero",

109. "_type": "logs",

110. "_id": "AVJexhHtDCAjHU4IlyN5",

111. "_score": null,

112. "_source": {

113. "borough": "BRONX",

114. "contributing_factor_vehicle": "Unspecified"

115. },

116. "sort": [

117. "BRONX"

118. ]

119. }

120. ]

121. }

122. }

123. },

124. {

125. "key": "2 AVENUE",

126. "doc_count": 8100,

127. "top_tag_hits": {

128. "hits": {

129. "total": 8100,

130. "max_score": null,

131. "hits": [

132. {

133. "_index": "nyc_visionzero",

134. "_type": "logs",

135. "_id": "AVJexXs0DCAjHU4Iljrp",

136. "_score": null,

137. "_source": {

138. "borough": "BROOKLYN",

139. "contributing_factor_vehicle": "Unspecified"

140. },

141. "sort": [

142. "BROOKLYN"

143. ]

144. },

145. {

146. "_index": "nyc_visionzero",

147. "_type": "logs",

148. "_id": "AVJexYecDCAjHU4Ilk3-",

149. "_score": null,

150. "_source": {

151. "borough": "BROOKLYN",

152. "contributing_factor_vehicle": "Unspecified"

153. },

154. "sort": [

155. "BROOKLYN"

156. ]

157. },

158. {

159. "_index": "nyc_visionzero",

160. "_type": "logs",

161. "_id": "AVJexYecDCAjHU4Ilk4o",

162. "_score": null,

163. "_source": {

164. "borough": "BROOKLYN",

165. "contributing_factor_vehicle": "Driver Inattention/Distraction"

166. },

167. "sort": [

168. "BROOKLYN"

169. ]

170. }

171. ]

172. }

173. }

174. }

175. ]

176. }

177. }

178. }

Exemple de sous-agrégation mixée :

1. GET /nyc_visionzero/_search?search_type=count

2. {

3. "aggs" : {

4. "all_boroughs": {

5. "terms": {

6. "field": "borough"

7. },

8. "aggs": {

9. "total_cyclists_injured": {

10. "sum" : {

11. "field": "number_of_cyclist_injured"

12. }

13. }

14. }

15. }

16. }

17. }

Nous obtiendrons le nombre de cyclistes blessés par arrondissement.

Le résultat :

1. {

2. "took": 37,

3. "timed_out": false,

4. "_shards": {

5. "total": 5,

6. "successful": 5,

7. "failed": 0

8. },

9. "hits": {

10. "total": 735858,

11. "max_score": 0,

12. "hits": []

13. },

14. "aggregations": {

15. "all_boroughs": {

16. "doc_count_error_upper_bound": 0,

17. "sum_other_doc_count": 0,

18. "buckets": [

19. {

20. "key": "BROOKLYN",

21. "doc_count": 170884,

22. "total_cyclists_injured": {

23. "value": 5371

24. }

25. },

26. {

27. "key": "MANHATTAN",

28. "doc_count": 147516,

29. "total_cyclists_injured": {

30. "value": 4054

31. }

32. },

33. {

34. "key": "QUEENS",

35. "doc_count": 143586,

36. "total_cyclists_injured": {

37. "value": 2616

38. }

39. },

40. {

41. "key": "BRONX",

42. "doc_count": 71048,

43. "total_cyclists_injured": {

44. "value": 1172

45. }

46. },

47. {

48. "key": "STATEN ISLAND",

49. "doc_count": 26401,

50. "total_cyclists_injured": {

51. "value": 131

52. }

53. }

54. ]

55. }

56. }

57. }

IV - Conclusion

Nous arrivons à la fin de cette « petite » présentation des agrégations d'Elasticsearch. J’espère qu’elle vous aura donné envie d'utiliser ces fonctionnalités qui permettent d'exploiter différemment vos données indexées, pour par exemple remplir des graphiques ou alimenter des modules de reporting.

Je vous laisse le soin de parcourir la documentation pour trouver votre bonheur dans la liste des agrégations disponibles, notez que vous pouvez même créer des agrégations bucket et metric avec vos propres règles métier par script !

V - Remerciements

Cet article a été publié avec l'aimable autorisation de Netapsys.

Nous tenons à remercier escartefigue pour la relecture orthographique attentive de cet article et Mickael Baron pour la mise au gabarit.