esql-search-tutorial.asciidoc 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580
  1. // ℹ️ 9.x version of this doc lives in docs-content repo
  2. // https://github.com/elastic/docs-content/blob/main/solutions/search/esql-search-tutorial.md
  3. [[esql-search-tutorial]]
  4. == Tutorial: Search and filter with {esql}
  5. [TIP]
  6. =====
  7. This tutorial presents examples in {esql} syntax. Refer to <<full-text-filter-tutorial,the Query DSL version>> for the equivalent examples in Query DSL syntax.
  8. =====
  9. This is a hands-on introduction to the basics of full-text search and semantic search, using <<esql,{esql}>>.
  10. For an overview of all the search capabilities in {esql}, refer to <<esql-for-search>>.
  11. In this scenario, we're implementing search for a cooking blog. The blog contains recipes with various attributes including textual content, categorical data, and numerical ratings.
  12. [discrete]
  13. [[esql-search-tutorial-requirements]]
  14. === Requirements
  15. You need a running {es} cluster, together with {kib} to use the Dev Tools API Console. Refer to <<elasticsearch-intro-deploy,choose your deployment type>> for deployment options.
  16. Want to get started quickly? Run the following command in your terminal to set up a <<run-elasticsearch-locally,single-node local cluster in Docker>>:
  17. [source,sh]
  18. ----
  19. curl -fsSL https://elastic.co/start-local | sh
  20. ----
  21. // NOTCONSOLE
  22. [discrete]
  23. [[esql-search-tutorial-running-esql-queries]]
  24. === Running {esql} queries
  25. In this tutorial, {esql} examples are displayed in the following format:
  26. [source,esql]
  27. ----
  28. FROM cooking_blog
  29. | WHERE description:"fluffy pancakes"
  30. | LIMIT 1000
  31. ----
  32. If you want to run these queries in the <<esql-kibana-console,Dev Tools Console>>, you need to use the following syntax:
  33. [source,js]
  34. ----
  35. POST /_query?format=txt
  36. {
  37. "query": """
  38. FROM cooking_blog
  39. | WHERE description:"fluffy pancakes"
  40. | LIMIT 1000
  41. """
  42. }
  43. ----
  44. // NOTCONSOLE
  45. If you'd prefer to use your favorite programming language, refer to <<http-clients,Client libraries>> for a list of official and community-supported clients.
  46. [discrete]
  47. [[esql-search-tutorial-step-1-create-an-index]]
  48. === Step 1: Create an index
  49. Create the `cooking_blog` index to get started:
  50. [source,console]
  51. ----
  52. PUT /cooking_blog
  53. ----
  54. // TESTSETUP
  55. Now define the mappings for the index:
  56. [source,console]
  57. ----
  58. PUT /cooking_blog/_mapping
  59. {
  60. "properties": {
  61. "title": {
  62. "type": "text",
  63. "analyzer": "standard", <1>
  64. "fields": { <2>
  65. "keyword": {
  66. "type": "keyword",
  67. "ignore_above": 256 <3>
  68. }
  69. }
  70. },
  71. "description": {
  72. "type": "text",
  73. "fields": {
  74. "keyword": {
  75. "type": "keyword"
  76. }
  77. }
  78. },
  79. "author": {
  80. "type": "text",
  81. "fields": {
  82. "keyword": {
  83. "type": "keyword"
  84. }
  85. }
  86. },
  87. "date": {
  88. "type": "date",
  89. "format": "yyyy-MM-dd"
  90. },
  91. "category": {
  92. "type": "text",
  93. "fields": {
  94. "keyword": {
  95. "type": "keyword"
  96. }
  97. }
  98. },
  99. "tags": {
  100. "type": "text",
  101. "fields": {
  102. "keyword": {
  103. "type": "keyword"
  104. }
  105. }
  106. },
  107. "rating": {
  108. "type": "float"
  109. }
  110. }
  111. }
  112. ----
  113. // TEST
  114. <1> The `standard` analyzer is used by default for `text` fields if an `analyzer` isn't specified. It's included here for demonstration purposes.
  115. <2> <<multi-fields,Multi-fields>> are used here to index `text` fields as both `text` and `keyword` <<mapping-types,data types>>. This enables both full-text search and exact matching/filtering on the same field. Note that if you used <<dynamic-field-mapping,dynamic mapping>>, these multi-fields would be created automatically.
  116. <3> The <<ignore-above,`ignore_above` parameter>> prevents indexing values longer than 256 characters in the `keyword` field. Again this is the default value, but it's included here for demonstration purposes. It helps to save disk space and avoid potential issues with Lucene's term byte-length limit.
  117. [TIP]
  118. =====
  119. Full-text search is powered by <<analysis,text analysis>>. Text analysis normalizes and standardizes text data so it can be efficiently stored in an inverted index and searched in near real-time. Analysis happens at both <<analysis-index-search-time,index and search time>>. This tutorial won't cover analysis in detail, but it's important to understand how text is processed to create effective search queries.
  120. =====
  121. [discrete]
  122. [[esql-search-tutorial-index-data]]
  123. === Step 2: Add sample blog posts to your index
  124. Now you'll need to index some example blog posts using the https://www.elastic.co/docs/api/doc/elasticsearch/v8/operation/operation-indices-get-mapping[Bulk API]. Note that `text` fields are analyzed and multi-fields are generated at index time.
  125. [source,console]
  126. ----
  127. POST /cooking_blog/_bulk?refresh=wait_for
  128. {"index":{"_id":"1"}}
  129. {"title":"Perfect Pancakes: A Fluffy Breakfast Delight","description":"Learn the secrets to making the fluffiest pancakes, so amazing you won't believe your tastebuds. This recipe uses buttermilk and a special folding technique to create light, airy pancakes that are perfect for lazy Sunday mornings.","author":"Maria Rodriguez","date":"2023-05-01","category":"Breakfast","tags":["pancakes","breakfast","easy recipes"],"rating":4.8}
  130. {"index":{"_id":"2"}}
  131. {"title":"Spicy Thai Green Curry: A Vegetarian Adventure","description":"Dive into the flavors of Thailand with this vibrant green curry. Packed with vegetables and aromatic herbs, this dish is both healthy and satisfying. Don't worry about the heat - you can easily adjust the spice level to your liking.","author":"Liam Chen","date":"2023-05-05","category":"Main Course","tags":["thai","vegetarian","curry","spicy"],"rating":4.6}
  132. {"index":{"_id":"3"}}
  133. {"title":"Classic Beef Stroganoff: A Creamy Comfort Food","description":"Indulge in this rich and creamy beef stroganoff. Tender strips of beef in a savory mushroom sauce, served over a bed of egg noodles. It's the ultimate comfort food for chilly evenings.","author":"Emma Watson","date":"2023-05-10","category":"Main Course","tags":["beef","pasta","comfort food"],"rating":4.7}
  134. {"index":{"_id":"4"}}
  135. {"title":"Vegan Chocolate Avocado Mousse","description":"Discover the magic of avocado in this rich, vegan chocolate mousse. Creamy, indulgent, and secretly healthy, it's the perfect guilt-free dessert for chocolate lovers.","author":"Alex Green","date":"2023-05-15","category":"Dessert","tags":["vegan","chocolate","avocado","healthy dessert"],"rating":4.5}
  136. {"index":{"_id":"5"}}
  137. {"title":"Crispy Oven-Fried Chicken","description":"Get that perfect crunch without the deep fryer! This oven-fried chicken recipe delivers crispy, juicy results every time. A healthier take on the classic comfort food.","author":"Maria Rodriguez","date":"2023-05-20","category":"Main Course","tags":["chicken","oven-fried","healthy"],"rating":4.9}
  138. ----
  139. [[step-3-basic-search-operations]]
  140. [discrete]
  141. === Step 3: Basic search operations
  142. Full-text search involves executing text-based queries across one or more document fields. In this section, we start with simple text matching and build up to understanding how search results are ranked.
  143. {esql} provides multiple functions for full-text search, including `MATCH`, `MATCH_PHRASE`, and `QSTR`. For basic text matching, you can use either:
  144. 1. Full <<esql-match,match function>> syntax: `match(field, "search terms")`
  145. 2. Compact syntax using the <<esql-search-operators,match operator `:`>>: `field:"search terms"`
  146. Both are equivalent for basic matching and can be used interchangeably. The compact syntax is more concise, while the function syntax allows for more configuration options. We use the compact syntax in most examples for brevity.
  147. Refer to the <<esql-match,match function>> reference docs for advanced parameters available with the function syntax.
  148. [discrete]
  149. [[esql-search-tutorial-make-first-search]]
  150. ==== Make your first search query
  151. Let's start with the simplest possible search - looking for documents that contain specific words:
  152. [source,esql]
  153. ----
  154. FROM cooking_blog
  155. | WHERE description:"fluffy pancakes"
  156. | LIMIT 1000
  157. ----
  158. This query searches the `description` field for documents containing either "fluffy" OR "pancakes" (or both). By default, {esql} uses OR logic between search terms, so it matches documents that contain any of the specified words.
  159. [discrete]
  160. [[esql-search-tutorial-control-result-fields]]
  161. ==== Control which fields appear in results
  162. You can specify exactly which fields to include in your results using the `KEEP` command:
  163. [source,esql]
  164. ----
  165. FROM cooking_blog
  166. | WHERE description:"fluffy pancakes"
  167. | KEEP title, description, rating
  168. | LIMIT 1000
  169. ----
  170. This helps reduce the amount of data returned and focuses on the information you need.
  171. [discrete]
  172. [[esql-search-tutorial-relevance-scoring-basics]]
  173. ==== Understand relevance scoring
  174. Search results can be ranked by how well they match your query. To calculate and use relevance scores, you need to explicitly request the `_score` metadata:
  175. [source,esql]
  176. ----
  177. FROM cooking_blog METADATA _score
  178. | WHERE description:"fluffy pancakes"
  179. | KEEP title, description, _score
  180. | SORT _score DESC
  181. | LIMIT 1000
  182. ----
  183. Notice two important things:
  184. 1. `METADATA _score` tells {esql} to include relevance scores in the results
  185. 2. `SORT _score DESC` orders results by relevance (highest scores first)
  186. If you don't include `METADATA _score` in your query, you won't see relevance scores in your results. This means you won't be able to sort by relevance or filter based on relevance scores.
  187. Without explicit sorting, results aren't ordered by relevance even when scores are calculated. If you want the most relevant results first, you must sort by `_score`, by explicitly using `SORT _score DESC` or `SORT _score ASC`.
  188. [TIP]
  189. =====
  190. When you include `METADATA _score`, search functions included in `WHERE` conditions contribute to the relevance score. Filtering operations (like range conditions and exact matches) don't affect the score.
  191. =====
  192. [discrete]
  193. [[esql-search-tutorial-exact-matches-basics]]
  194. ==== Find exact matches
  195. Sometimes you need exact matches rather than full-text search. Use the `.keyword` field for case-sensitive exact matching:
  196. [source,esql]
  197. ----
  198. FROM cooking_blog
  199. | WHERE category.keyword == "Breakfast" # Exact match (case-sensitive)
  200. | KEEP title, category, rating
  201. | SORT rating DESC
  202. | LIMIT 1000
  203. ----
  204. This is fundamentally different from full-text search - it's a binary yes/no filter that doesn't affect relevance scoring.
  205. [discrete]
  206. [[step-4-search-precision-control]]
  207. === Step 4: Search precision control
  208. Now that you understand basic searching, let's explore how to control the precision of your text matches.
  209. [discrete]
  210. [[esql-search-tutorial-require-all-terms]]
  211. ==== Require all search terms (AND logic)
  212. By default, searches with match use OR logic between terms. To require ALL terms to match, use the function syntax with the `operator` parameter to specify AND logic:
  213. [source,esql]
  214. ----
  215. FROM cooking_blog
  216. | WHERE match(description, "fluffy pancakes", {"operator": "AND"})
  217. | LIMIT 1000
  218. ----
  219. This stricter search returns *zero hits* on our sample data, as no document contains both "fluffy" and "pancakes" in the description.
  220. [NOTE]
  221. =====
  222. The `MATCH` function with AND logic doesn't require terms to be adjacent or in order. It only requires that all terms appear somewhere in the field. Use `MATCH_PHRASE` to search for exact phrases.
  223. =====
  224. [discrete]
  225. [[esql-search-tutorial-minimum-terms]]
  226. ==== Set a minimum number of terms to match
  227. Sometimes requiring all terms is too strict, but the default OR behavior is too lenient. You can specify a minimum number of terms that must match:
  228. [source,esql]
  229. ----
  230. FROM cooking_blog
  231. | WHERE match(title, "fluffy pancakes breakfast", {"minimum_should_match": 2})
  232. | LIMIT 1000
  233. ----
  234. This query searches the title field to match at least 2 of the 3 terms: "fluffy", "pancakes", or "breakfast".
  235. [discrete]
  236. [[esql-search-tutorial-match-phrase]]
  237. ==== Search for exact phrases
  238. When you need to find documents containing an exact sequence of words, use the `MATCH_PHRASE` function:
  239. [source,esql]
  240. ----
  241. FROM cooking_blog
  242. | WHERE MATCH_PHRASE(description, "rich and creamy")
  243. | KEEP title, description
  244. | LIMIT 1000
  245. ----
  246. This query only matches documents where the words "rich and creamy" appear exactly in that order in the description field.
  247. [discrete]
  248. [[esql-search-tutorial-semantic-search]]
  249. === Step 5: Semantic search and hybrid search
  250. [discrete]
  251. [[esql-search-tutorial-index-semantic-content]]
  252. ==== Index semantic content
  253. {es} allows you to semantically search for documents based on the meaning of the text, rather than just the presence of specific keywords. This is useful when you want to find documents that are conceptually similar to a given query, even if they don't contain the exact search terms.
  254. ES|QL supports semantic search when your mappings include fields of the <<semantic-text,`semantic_text`>> type. This example mapping update adds a new field called `semantic_description` with the type `semantic_text`:
  255. [source,console]
  256. ----
  257. PUT /cooking_blog/_mapping
  258. {
  259. "properties": {
  260. "semantic_description": {
  261. "type": "semantic_text"
  262. }
  263. }
  264. }
  265. ----
  266. Next, index a document with content into the new field:
  267. [source,console]
  268. ----
  269. POST /cooking_blog/_doc
  270. {
  271. "title": "Mediterranean Quinoa Bowl",
  272. "semantic_description": "A protein-rich bowl with quinoa, chickpeas, fresh vegetables, and herbs. This nutritious Mediterranean-inspired dish is easy to prepare and perfect for a quick, healthy dinner.",
  273. "author": "Jamie Oliver",
  274. "date": "2023-06-01",
  275. "category": "Main Course",
  276. "tags": ["vegetarian", "healthy", "mediterranean", "quinoa"],
  277. "rating": 4.7
  278. }
  279. ----
  280. // TEST[skip:uses ML]
  281. [discrete]
  282. [[esql-search-tutorial-perform-semantic-search]]
  283. ==== Perform semantic search
  284. Once the document has been processed by the underlying model running on the inference endpoint, you can perform semantic searches. Here's an example natural language query against the `semantic_description` field:
  285. [source,esql]
  286. ----
  287. FROM cooking_blog
  288. | WHERE semantic_description:"What are some easy to prepare but nutritious plant-based meals?"
  289. | LIMIT 5
  290. ----
  291. [TIP]
  292. =====
  293. Follow this <<semantic-search-semantic-text,tutorial>> if you'd like to test out the semantic search workflow against a large dataset.
  294. =====
  295. [discrete]
  296. [[esql-search-tutorial-perform-hybrid-search]]
  297. ==== Perform hybrid search
  298. You can combine full-text and semantic queries. In this example we combine full-text and semantic search with custom weights:
  299. [source,esql]
  300. ----
  301. FROM cooking_blog METADATA _score
  302. | WHERE match(semantic_description, "easy to prepare vegetarian meals", { "boost": 0.75 })
  303. OR match(tags, "vegetarian", { "boost": 0.25 })
  304. | SORT _score DESC
  305. | LIMIT 5
  306. ----
  307. This query searches the `semantic_description` field for documents that are semantically similar to "easy to prepare vegetarian meals" with a higher weight, while also matching the `tags` field for "vegetarian" with a lower weight. The results are sorted by relevance score.
  308. Learn how to combine these with complex criteria in <<step-8-complex-search-solutions>>.
  309. [discrete]
  310. [[step-6-advanced-search-features]]
  311. === Step 6: Advanced search features
  312. Once you're comfortable with basic search precision, these advanced features give you powerful search capabilities.
  313. [discrete]
  314. [[esql-search-tutorial-query-string]]
  315. ==== Use query string for complex patterns
  316. The `QSTR` function enables powerful search patterns using a compact query language. It's ideal for when you need wildcards, fuzzy matching, and boolean logic in a single expression:
  317. [source,esql]
  318. ----
  319. FROM cooking_blog
  320. | WHERE QSTR(description, "fluffy AND pancak* OR (creamy -vegan)")
  321. | KEEP title, description
  322. | LIMIT 1000
  323. ----
  324. Query string syntax lets you:
  325. - Use boolean operators: `AND`, `OR`, `-` (NOT)
  326. - Apply wildcards: `pancak*` matches "pancake" and "pancakes"
  327. - Enable fuzzy matching: `pancake~1` for typo tolerance
  328. - Group terms: `(thai AND curry) OR pasta`
  329. - Search exact phrases: `"fluffy pancakes"`
  330. - Search across fields: `QSTR("title,description", "pancake OR (creamy AND rich)")`
  331. [discrete]
  332. [[esql-search-tutorial-search-across-fields]]
  333. ==== Search across multiple fields
  334. When users enter a search query, they often don't know (or care) whether their search terms appear in a specific field. You can search across multiple fields simultaneously:
  335. [source,esql]
  336. ----
  337. FROM cooking_blog
  338. | WHERE title:"vegetarian curry" OR description:"vegetarian curry" OR tags:"vegetarian curry"
  339. | LIMIT 1000
  340. ----
  341. This query searches for "vegetarian curry" across the title, description, and tags fields. Each field is treated with equal importance.
  342. [discrete]
  343. [[esql-search-tutorial-weight-fields]]
  344. ==== Weight different fields
  345. In many cases, matches in certain fields (like the title) might be more relevant than others. You can adjust the importance of each field using boost scoring:
  346. [source,esql]
  347. ----
  348. FROM cooking_blog METADATA _score
  349. | WHERE match(title, "vegetarian curry", {"boost": 2.0}) # Title matches are twice as important
  350. OR match(description, "vegetarian curry")
  351. OR match(tags, "vegetarian curry")
  352. | KEEP title, description, tags, _score
  353. | SORT _score DESC
  354. | LIMIT 1000
  355. ----
  356. [discrete]
  357. [[step-7-filtering-exact-matching]]
  358. === Step 7: Filtering and exact matching
  359. Filtering allows you to narrow down your search results based on exact criteria. Unlike full-text searches, filters are binary (yes/no) and do not affect the relevance score. Filters execute faster than queries because excluded results don't need to be scored.
  360. [discrete]
  361. [[esql-search-tutorial-filter-category]]
  362. ==== Basic filtering by category
  363. [source,esql]
  364. ----
  365. FROM cooking_blog
  366. | WHERE category.keyword == "Breakfast" # Exact match using keyword field
  367. | KEEP title, author, rating, tags
  368. | SORT rating DESC
  369. | LIMIT 1000
  370. ----
  371. Note the use of `category.keyword` here. This refers to the <<keyword,`keyword`>> multi-field of the `category` field, ensuring an exact, case-sensitive match.
  372. [discrete]
  373. [[esql-search-tutorial-date-range]]
  374. ==== Date range filtering
  375. Often users want to find content published within a specific time frame:
  376. [source,esql]
  377. ----
  378. FROM cooking_blog
  379. | WHERE date >= "2023-05-01" AND date <= "2023-05-31"
  380. | KEEP title, author, date, rating
  381. | LIMIT 1000
  382. ----
  383. [discrete]
  384. [[esql-search-tutorial-numerical-range]]
  385. ==== Numerical range filtering
  386. Filter by ratings or other numerical values:
  387. [source,esql]
  388. ----
  389. FROM cooking_blog
  390. | WHERE rating >= 4.5 # Only highly-rated recipes
  391. | KEEP title, author, rating, tags
  392. | SORT rating DESC
  393. | LIMIT 1000
  394. ----
  395. [discrete]
  396. [[esql-search-tutorial-exact-author]]
  397. ==== Exact author matching
  398. Find recipes by a specific author:
  399. [source,esql]
  400. ----
  401. FROM cooking_blog
  402. | WHERE author.keyword == "Maria Rodriguez"
  403. | KEEP title, author, rating, tags
  404. | SORT rating DESC
  405. | LIMIT 1000
  406. ----
  407. [discrete]
  408. [[step-8-complex-search-solutions]]
  409. === Step 8: Complex search solutions
  410. Real-world search often requires combining multiple types of criteria. This section shows how to build sophisticated search experiences.
  411. [discrete]
  412. [[esql-search-tutorial-combine-criteria]]
  413. ==== Combine filters with full-text search
  414. Mix filters, full-text search, and custom scoring in a single query:
  415. [source,esql]
  416. ----
  417. FROM cooking_blog METADATA _score
  418. | WHERE rating >= 4.5
  419. AND NOT category.keyword == "Dessert"
  420. AND (title:"curry spicy" OR description:"curry spicy")
  421. | SORT _score DESC
  422. | KEEP title, author, rating, tags, description
  423. | LIMIT 1000
  424. ----
  425. [discrete]
  426. [[esql-search-tutorial-advanced-relevance-scoring]]
  427. ==== Advanced relevance scoring
  428. For complex relevance scoring with combined criteria, you can use the `EVAL` command to calculate custom scores:
  429. [source,esql]
  430. ----
  431. FROM cooking_blog METADATA _score
  432. | WHERE NOT category.keyword == "Dessert"
  433. | EVAL tags_concat = MV_CONCAT(tags.keyword, ",")
  434. | WHERE tags_concat LIKE "*vegetarian*" AND rating >= 4.5
  435. | WHERE match(title, "curry spicy", {"boost": 2.0}) OR match(description, "curry spicy")
  436. | EVAL category_boost = CASE(category.keyword == "Main Course", 1.0, 0.0)
  437. | EVAL date_boost = CASE(DATE_DIFF("month", date, NOW()) <= 1, 0.5, 0.0)
  438. | EVAL custom_score = _score + category_boost + date_boost
  439. | WHERE custom_score > 0
  440. | SORT custom_score DESC
  441. | LIMIT 1000
  442. ----
  443. [discrete]
  444. [[esql-search-tutorial-learn-more]]
  445. === Learn more
  446. [discrete]
  447. [[esql-search-tutorial-documentation]]
  448. ==== Documentation
  449. This tutorial introduced the basics of search and filtering in {esql}. Building a real-world search experience requires understanding many more advanced concepts and techniques. Here are some resources once you're ready to dive deeper:
  450. * <<esql-for-search,Search with {esql}>>: Learn about all your options for search use cases with {esql}.
  451. * <<esql-search-functions,{esql} search functions>>: Explore the full list of search functions available in {esql}.
  452. * <<semantic-search,Semantic search>>: Understand your various options for semantic search in Elasticsearch.
  453. ** <<semantic-search-semantic-text,The `semantic_text` workflow>>: Learn how to use the `semantic_text` field type for semantic search. This is the recommended approach for most users looking to perform semantic search in {es}, because it abstracts away the complexity of setting up inference endpoints and models.
  454. [discrete]
  455. [[esql-search-tutorial-blog-posts]]
  456. ==== Related blog posts
  457. * https://www.elastic.co/search-labs/blog/esql-introducing-scoring-semantic-search[ES|QL, you know for Search]
  458. * https://www.elastic.co/search-labs/blog/filtering-in-esql-full-text-search-match-qstr[Introducing full text filtering in ES|QL]