Retrievers examples

Retrievers examples

Add example data

To begin with, lets create the retrievers_example index, and add some documents to it.

  1. resp = client.indices.create(
  2. index="retrievers_example",
  3. mappings={
  4. "properties": {
  5. "vector": {
  6. "type": "dense_vector",
  7. "dims": 3,
  8. "similarity": "l2_norm",
  9. "index": True
  10. },
  11. "text": {
  12. "type": "text"
  13. },
  14. "year": {
  15. "type": "integer"
  16. },
  17. "topic": {
  18. "type": "keyword"
  19. }
  20. }
  21. },
  22. )
  23. print(resp)
  24. resp1 = client.index(
  25. index="retrievers_example",
  26. id="1",
  27. document={
  28. "vector": [
  29. 0.23,
  30. 0.67,
  31. 0.89
  32. ],
  33. "text": "Large language models are revolutionizing information retrieval by boosting search precision, deepening contextual understanding, and reshaping user experiences in data-rich environments.",
  34. "year": 2024,
  35. "topic": [
  36. "llm",
  37. "ai",
  38. "information_retrieval"
  39. ]
  40. },
  41. )
  42. print(resp1)
  43. resp2 = client.index(
  44. index="retrievers_example",
  45. id="2",
  46. document={
  47. "vector": [
  48. 0.12,
  49. 0.56,
  50. 0.78
  51. ],
  52. "text": "Artificial intelligence is transforming medicine, from advancing diagnostics and tailoring treatment plans to empowering predictive patient care for improved health outcomes.",
  53. "year": 2023,
  54. "topic": [
  55. "ai",
  56. "medicine"
  57. ]
  58. },
  59. )
  60. print(resp2)
  61. resp3 = client.index(
  62. index="retrievers_example",
  63. id="3",
  64. document={
  65. "vector": [
  66. 0.45,
  67. 0.32,
  68. 0.91
  69. ],
  70. "text": "AI is redefining security by enabling advanced threat detection, proactive risk analysis, and dynamic defenses against increasingly sophisticated cyber threats.",
  71. "year": 2024,
  72. "topic": [
  73. "ai",
  74. "security"
  75. ]
  76. },
  77. )
  78. print(resp3)
  79. resp4 = client.index(
  80. index="retrievers_example",
  81. id="4",
  82. document={
  83. "vector": [
  84. 0.34,
  85. 0.21,
  86. 0.98
  87. ],
  88. "text": "Elastic introduces Elastic AI Assistant, the open, generative AI sidekick powered by ESRE to democratize cybersecurity and enable users of every skill level.",
  89. "year": 2023,
  90. "topic": [
  91. "ai",
  92. "elastic",
  93. "assistant"
  94. ]
  95. },
  96. )
  97. print(resp4)
  98. resp5 = client.index(
  99. index="retrievers_example",
  100. id="5",
  101. document={
  102. "vector": [
  103. 0.11,
  104. 0.65,
  105. 0.47
  106. ],
  107. "text": "Learn how to spin up a deployment of our hosted Elasticsearch Service and use Elastic Observability to gain deeper insight into the behavior of your applications and systems.",
  108. "year": 2024,
  109. "topic": [
  110. "documentation",
  111. "observability",
  112. "elastic"
  113. ]
  114. },
  115. )
  116. print(resp5)
  117. resp6 = client.indices.refresh(
  118. index="retrievers_example",
  119. )
  120. print(resp6)
  1. const response = await client.indices.create({
  2. index: "retrievers_example",
  3. mappings: {
  4. properties: {
  5. vector: {
  6. type: "dense_vector",
  7. dims: 3,
  8. similarity: "l2_norm",
  9. index: true,
  10. },
  11. text: {
  12. type: "text",
  13. },
  14. year: {
  15. type: "integer",
  16. },
  17. topic: {
  18. type: "keyword",
  19. },
  20. },
  21. },
  22. });
  23. console.log(response);
  24. const response1 = await client.index({
  25. index: "retrievers_example",
  26. id: 1,
  27. document: {
  28. vector: [0.23, 0.67, 0.89],
  29. text: "Large language models are revolutionizing information retrieval by boosting search precision, deepening contextual understanding, and reshaping user experiences in data-rich environments.",
  30. year: 2024,
  31. topic: ["llm", "ai", "information_retrieval"],
  32. },
  33. });
  34. console.log(response1);
  35. const response2 = await client.index({
  36. index: "retrievers_example",
  37. id: 2,
  38. document: {
  39. vector: [0.12, 0.56, 0.78],
  40. text: "Artificial intelligence is transforming medicine, from advancing diagnostics and tailoring treatment plans to empowering predictive patient care for improved health outcomes.",
  41. year: 2023,
  42. topic: ["ai", "medicine"],
  43. },
  44. });
  45. console.log(response2);
  46. const response3 = await client.index({
  47. index: "retrievers_example",
  48. id: 3,
  49. document: {
  50. vector: [0.45, 0.32, 0.91],
  51. text: "AI is redefining security by enabling advanced threat detection, proactive risk analysis, and dynamic defenses against increasingly sophisticated cyber threats.",
  52. year: 2024,
  53. topic: ["ai", "security"],
  54. },
  55. });
  56. console.log(response3);
  57. const response4 = await client.index({
  58. index: "retrievers_example",
  59. id: 4,
  60. document: {
  61. vector: [0.34, 0.21, 0.98],
  62. text: "Elastic introduces Elastic AI Assistant, the open, generative AI sidekick powered by ESRE to democratize cybersecurity and enable users of every skill level.",
  63. year: 2023,
  64. topic: ["ai", "elastic", "assistant"],
  65. },
  66. });
  67. console.log(response4);
  68. const response5 = await client.index({
  69. index: "retrievers_example",
  70. id: 5,
  71. document: {
  72. vector: [0.11, 0.65, 0.47],
  73. text: "Learn how to spin up a deployment of our hosted Elasticsearch Service and use Elastic Observability to gain deeper insight into the behavior of your applications and systems.",
  74. year: 2024,
  75. topic: ["documentation", "observability", "elastic"],
  76. },
  77. });
  78. console.log(response5);
  79. const response6 = await client.indices.refresh({
  80. index: "retrievers_example",
  81. });
  82. console.log(response6);
  1. PUT retrievers_example
  2. {
  3. "mappings": {
  4. "properties": {
  5. "vector": {
  6. "type": "dense_vector",
  7. "dims": 3,
  8. "similarity": "l2_norm",
  9. "index": true
  10. },
  11. "text": {
  12. "type": "text"
  13. },
  14. "year": {
  15. "type": "integer"
  16. },
  17. "topic": {
  18. "type": "keyword"
  19. }
  20. }
  21. }
  22. }
  23. POST /retrievers_example/_doc/1
  24. {
  25. "vector": [0.23, 0.67, 0.89],
  26. "text": "Large language models are revolutionizing information retrieval by boosting search precision, deepening contextual understanding, and reshaping user experiences in data-rich environments.",
  27. "year": 2024,
  28. "topic": ["llm", "ai", "information_retrieval"]
  29. }
  30. POST /retrievers_example/_doc/2
  31. {
  32. "vector": [0.12, 0.56, 0.78],
  33. "text": "Artificial intelligence is transforming medicine, from advancing diagnostics and tailoring treatment plans to empowering predictive patient care for improved health outcomes.",
  34. "year": 2023,
  35. "topic": ["ai", "medicine"]
  36. }
  37. POST /retrievers_example/_doc/3
  38. {
  39. "vector": [0.45, 0.32, 0.91],
  40. "text": "AI is redefining security by enabling advanced threat detection, proactive risk analysis, and dynamic defenses against increasingly sophisticated cyber threats.",
  41. "year": 2024,
  42. "topic": ["ai", "security"]
  43. }
  44. POST /retrievers_example/_doc/4
  45. {
  46. "vector": [0.34, 0.21, 0.98],
  47. "text": "Elastic introduces Elastic AI Assistant, the open, generative AI sidekick powered by ESRE to democratize cybersecurity and enable users of every skill level.",
  48. "year": 2023,
  49. "topic": ["ai", "elastic", "assistant"]
  50. }
  51. POST /retrievers_example/_doc/5
  52. {
  53. "vector": [0.11, 0.65, 0.47],
  54. "text": "Learn how to spin up a deployment of our hosted Elasticsearch Service and use Elastic Observability to gain deeper insight into the behavior of your applications and systems.",
  55. "year": 2024,
  56. "topic": ["documentation", "observability", "elastic"]
  57. }
  58. POST /retrievers_example/_refresh

Now that we have our documents in place, let’s try to run some queries using retrievers.

Example: Combining query and kNN with RRF

First, let’s examine how to combine two different types of queries: a kNN query and a query_string query. While these queries may produce scores in different ranges, we can use Reciprocal Rank Fusion (rrf) to combine the results and generate a merged final result list.

To implement this in the retriever framework, we start with the top-level element: our rrf retriever. This retriever operates on top of two other retrievers: a knn retriever and a standard retriever. Our query structure would look like this:

  1. resp = client.search(
  2. index="retrievers_example",
  3. retriever={
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "query_string": {
  10. "query": "(information retrieval) OR (artificial intelligence)",
  11. "default_field": "text"
  12. }
  13. }
  14. }
  15. },
  16. {
  17. "knn": {
  18. "field": "vector",
  19. "query_vector": [
  20. 0.23,
  21. 0.67,
  22. 0.89
  23. ],
  24. "k": 3,
  25. "num_candidates": 5
  26. }
  27. }
  28. ],
  29. "rank_window_size": 10,
  30. "rank_constant": 1
  31. }
  32. },
  33. source=False,
  34. )
  35. print(resp)
  1. const response = await client.search({
  2. index: "retrievers_example",
  3. retriever: {
  4. rrf: {
  5. retrievers: [
  6. {
  7. standard: {
  8. query: {
  9. query_string: {
  10. query: "(information retrieval) OR (artificial intelligence)",
  11. default_field: "text",
  12. },
  13. },
  14. },
  15. },
  16. {
  17. knn: {
  18. field: "vector",
  19. query_vector: [0.23, 0.67, 0.89],
  20. k: 3,
  21. num_candidates: 5,
  22. },
  23. },
  24. ],
  25. rank_window_size: 10,
  26. rank_constant: 1,
  27. },
  28. },
  29. _source: false,
  30. });
  31. console.log(response);
  1. GET /retrievers_example/_search
  2. {
  3. "retriever": {
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "query_string": {
  10. "query": "(information retrieval) OR (artificial intelligence)",
  11. "default_field": "text"
  12. }
  13. }
  14. }
  15. },
  16. {
  17. "knn": {
  18. "field": "vector",
  19. "query_vector": [
  20. 0.23,
  21. 0.67,
  22. 0.89
  23. ],
  24. "k": 3,
  25. "num_candidates": 5
  26. }
  27. }
  28. ],
  29. "rank_window_size": 10,
  30. "rank_constant": 1
  31. }
  32. },
  33. "_source": false
  34. }

This returns the following response based on the final rrf score for each result.

Example response

  1. {
  2. "took": 42,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 3,
  13. "relation": "eq"
  14. },
  15. "max_score": 0.8333334,
  16. "hits": [
  17. {
  18. "_index": "retrievers_example",
  19. "_id": "1",
  20. "_score": 0.8333334
  21. },
  22. {
  23. "_index": "retrievers_example",
  24. "_id": "2",
  25. "_score": 0.8333334
  26. },
  27. {
  28. "_index": "retrievers_example",
  29. "_id": "3",
  30. "_score": 0.25
  31. }
  32. ]
  33. }
  34. }

Example: Grouping results by year with collapse

In our result set, we have many documents with the same year value. We can clean this up using the collapse parameter with our retriever. This, as with the standard collapse feature, enables grouping results by any field and returns only the highest-scoring document from each group. In this example we’ll collapse our results based on the year field.

  1. resp = client.search(
  2. index="retrievers_example",
  3. retriever={
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "query_string": {
  10. "query": "(information retrieval) OR (artificial intelligence)",
  11. "default_field": "text"
  12. }
  13. }
  14. }
  15. },
  16. {
  17. "knn": {
  18. "field": "vector",
  19. "query_vector": [
  20. 0.23,
  21. 0.67,
  22. 0.89
  23. ],
  24. "k": 3,
  25. "num_candidates": 5
  26. }
  27. }
  28. ],
  29. "rank_window_size": 10,
  30. "rank_constant": 1
  31. }
  32. },
  33. collapse={
  34. "field": "year",
  35. "inner_hits": {
  36. "name": "topic related documents",
  37. "_source": [
  38. "year"
  39. ]
  40. }
  41. },
  42. source=False,
  43. )
  44. print(resp)
  1. const response = await client.search({
  2. index: "retrievers_example",
  3. retriever: {
  4. rrf: {
  5. retrievers: [
  6. {
  7. standard: {
  8. query: {
  9. query_string: {
  10. query: "(information retrieval) OR (artificial intelligence)",
  11. default_field: "text",
  12. },
  13. },
  14. },
  15. },
  16. {
  17. knn: {
  18. field: "vector",
  19. query_vector: [0.23, 0.67, 0.89],
  20. k: 3,
  21. num_candidates: 5,
  22. },
  23. },
  24. ],
  25. rank_window_size: 10,
  26. rank_constant: 1,
  27. },
  28. },
  29. collapse: {
  30. field: "year",
  31. inner_hits: {
  32. name: "topic related documents",
  33. _source: ["year"],
  34. },
  35. },
  36. _source: false,
  37. });
  38. console.log(response);
  1. GET /retrievers_example/_search
  2. {
  3. "retriever": {
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "query_string": {
  10. "query": "(information retrieval) OR (artificial intelligence)",
  11. "default_field": "text"
  12. }
  13. }
  14. }
  15. },
  16. {
  17. "knn": {
  18. "field": "vector",
  19. "query_vector": [
  20. 0.23,
  21. 0.67,
  22. 0.89
  23. ],
  24. "k": 3,
  25. "num_candidates": 5
  26. }
  27. }
  28. ],
  29. "rank_window_size": 10,
  30. "rank_constant": 1
  31. }
  32. },
  33. "collapse": {
  34. "field": "year",
  35. "inner_hits": {
  36. "name": "topic related documents",
  37. "_source": [
  38. "year"
  39. ]
  40. }
  41. },
  42. "_source": false
  43. }

This returns the following response with collapsed results.

Example response

  1. {
  2. "took": 42,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 3,
  13. "relation": "eq"
  14. },
  15. "max_score": 0.8333334,
  16. "hits": [
  17. {
  18. "_index": "retrievers_example",
  19. "_id": "1",
  20. "_score": 0.8333334,
  21. "fields": {
  22. "year": [
  23. 2024
  24. ]
  25. },
  26. "inner_hits": {
  27. "topic related documents": {
  28. "hits": {
  29. "total": {
  30. "value": 2,
  31. "relation": "eq"
  32. },
  33. "max_score": 0.8333334,
  34. "hits": [
  35. {
  36. "_index": "retrievers_example",
  37. "_id": "1",
  38. "_score": 0.8333334,
  39. "_source": {
  40. "year": 2024
  41. }
  42. },
  43. {
  44. "_index": "retrievers_example",
  45. "_id": "3",
  46. "_score": 0.25,
  47. "_source": {
  48. "year": 2024
  49. }
  50. }
  51. ]
  52. }
  53. }
  54. }
  55. },
  56. {
  57. "_index": "retrievers_example",
  58. "_id": "2",
  59. "_score": 0.8333334,
  60. "fields": {
  61. "year": [
  62. 2023
  63. ]
  64. },
  65. "inner_hits": {
  66. "topic related documents": {
  67. "hits": {
  68. "total": {
  69. "value": 1,
  70. "relation": "eq"
  71. },
  72. "max_score": 0.8333334,
  73. "hits": [
  74. {
  75. "_index": "retrievers_example",
  76. "_id": "2",
  77. "_score": 0.8333334,
  78. "_source": {
  79. "year": 2023
  80. }
  81. }
  82. ]
  83. }
  84. }
  85. }
  86. }
  87. ]
  88. }
  89. }

Example: Highlighting results based on nested sub-retrievers

Highlighting is now also available for nested sub-retrievers matches. For example, consider the same rrf retriever as above, with a knn and standard retriever as its sub-retrievers. We can specify a highlight section, as defined in highlighting documentation, and compute highlights for the top results.

  1. resp = client.search(
  2. index="retrievers_example",
  3. retriever={
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "query_string": {
  10. "query": "(information retrieval) OR (artificial intelligence)",
  11. "default_field": "text"
  12. }
  13. }
  14. }
  15. },
  16. {
  17. "knn": {
  18. "field": "vector",
  19. "query_vector": [
  20. 0.23,
  21. 0.67,
  22. 0.89
  23. ],
  24. "k": 3,
  25. "num_candidates": 5
  26. }
  27. }
  28. ],
  29. "rank_window_size": 10,
  30. "rank_constant": 1
  31. }
  32. },
  33. highlight={
  34. "fields": {
  35. "text": {
  36. "fragment_size": 150,
  37. "number_of_fragments": 3
  38. }
  39. }
  40. },
  41. source=False,
  42. )
  43. print(resp)
  1. const response = await client.search({
  2. index: "retrievers_example",
  3. retriever: {
  4. rrf: {
  5. retrievers: [
  6. {
  7. standard: {
  8. query: {
  9. query_string: {
  10. query: "(information retrieval) OR (artificial intelligence)",
  11. default_field: "text",
  12. },
  13. },
  14. },
  15. },
  16. {
  17. knn: {
  18. field: "vector",
  19. query_vector: [0.23, 0.67, 0.89],
  20. k: 3,
  21. num_candidates: 5,
  22. },
  23. },
  24. ],
  25. rank_window_size: 10,
  26. rank_constant: 1,
  27. },
  28. },
  29. highlight: {
  30. fields: {
  31. text: {
  32. fragment_size: 150,
  33. number_of_fragments: 3,
  34. },
  35. },
  36. },
  37. _source: false,
  38. });
  39. console.log(response);
  1. GET /retrievers_example/_search
  2. {
  3. "retriever": {
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "query_string": {
  10. "query": "(information retrieval) OR (artificial intelligence)",
  11. "default_field": "text"
  12. }
  13. }
  14. }
  15. },
  16. {
  17. "knn": {
  18. "field": "vector",
  19. "query_vector": [
  20. 0.23,
  21. 0.67,
  22. 0.89
  23. ],
  24. "k": 3,
  25. "num_candidates": 5
  26. }
  27. }
  28. ],
  29. "rank_window_size": 10,
  30. "rank_constant": 1
  31. }
  32. },
  33. "highlight": {
  34. "fields": {
  35. "text": {
  36. "fragment_size": 150,
  37. "number_of_fragments": 3
  38. }
  39. }
  40. },
  41. "_source": false
  42. }

This would highlight the text field, based on the matches produced by the standard retriever. The highlighted snippets would then be included in the response as usual, i.e. under each search hit.

Example response

  1. {
  2. "took": 42,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 3,
  13. "relation": "eq"
  14. },
  15. "max_score": 0.8333334,
  16. "hits": [
  17. {
  18. "_index": "retrievers_example",
  19. "_id": "1",
  20. "_score": 0.8333334,
  21. "highlight": {
  22. "text": [
  23. "Large language models are revolutionizing <em>information</em> <em>retrieval</em> by boosting search precision, deepening contextual understanding, and reshaping user experiences"
  24. ]
  25. }
  26. },
  27. {
  28. "_index": "retrievers_example",
  29. "_id": "2",
  30. "_score": 0.8333334,
  31. "highlight": {
  32. "text": [
  33. "<em>Artificial</em> <em>intelligence</em> is transforming medicine, from advancing diagnostics and tailoring treatment plans to empowering predictive patient care for improved"
  34. ]
  35. }
  36. },
  37. {
  38. "_index": "retrievers_example",
  39. "_id": "3",
  40. "_score": 0.25
  41. }
  42. ]
  43. }
  44. }

Example: Computing inner hits from nested sub-retrievers

We can also define inner_hits to be computed on any of the sub-retrievers, and propagate those computations to the top level compound retriever. For example, let’s create a new index with a knn field, nested under the nested_field field, and index a couple of documents.

  1. resp = client.indices.create(
  2. index="retrievers_example_nested",
  3. mappings={
  4. "properties": {
  5. "nested_field": {
  6. "type": "nested",
  7. "properties": {
  8. "paragraph_id": {
  9. "type": "keyword"
  10. },
  11. "nested_vector": {
  12. "type": "dense_vector",
  13. "dims": 3,
  14. "similarity": "l2_norm",
  15. "index": True
  16. }
  17. }
  18. },
  19. "topic": {
  20. "type": "keyword"
  21. }
  22. }
  23. },
  24. )
  25. print(resp)
  26. resp1 = client.index(
  27. index="retrievers_example_nested",
  28. id="1",
  29. document={
  30. "nested_field": [
  31. {
  32. "paragraph_id": "1a",
  33. "nested_vector": [
  34. -1.12,
  35. -0.59,
  36. 0.78
  37. ]
  38. },
  39. {
  40. "paragraph_id": "1b",
  41. "nested_vector": [
  42. -0.12,
  43. 1.56,
  44. 0.42
  45. ]
  46. },
  47. {
  48. "paragraph_id": "1c",
  49. "nested_vector": [
  50. 1,
  51. -1,
  52. 0
  53. ]
  54. }
  55. ],
  56. "topic": [
  57. "ai"
  58. ]
  59. },
  60. )
  61. print(resp1)
  62. resp2 = client.index(
  63. index="retrievers_example_nested",
  64. id="2",
  65. document={
  66. "nested_field": [
  67. {
  68. "paragraph_id": "2a",
  69. "nested_vector": [
  70. 0.23,
  71. 1.24,
  72. 0.65
  73. ]
  74. }
  75. ],
  76. "topic": [
  77. "information_retrieval"
  78. ]
  79. },
  80. )
  81. print(resp2)
  82. resp3 = client.index(
  83. index="retrievers_example_nested",
  84. id="3",
  85. document={
  86. "topic": [
  87. "ai"
  88. ]
  89. },
  90. )
  91. print(resp3)
  92. resp4 = client.indices.refresh(
  93. index="retrievers_example_nested",
  94. )
  95. print(resp4)
  1. const response = await client.indices.create({
  2. index: "retrievers_example_nested",
  3. mappings: {
  4. properties: {
  5. nested_field: {
  6. type: "nested",
  7. properties: {
  8. paragraph_id: {
  9. type: "keyword",
  10. },
  11. nested_vector: {
  12. type: "dense_vector",
  13. dims: 3,
  14. similarity: "l2_norm",
  15. index: true,
  16. },
  17. },
  18. },
  19. topic: {
  20. type: "keyword",
  21. },
  22. },
  23. },
  24. });
  25. console.log(response);
  26. const response1 = await client.index({
  27. index: "retrievers_example_nested",
  28. id: 1,
  29. document: {
  30. nested_field: [
  31. {
  32. paragraph_id: "1a",
  33. nested_vector: [-1.12, -0.59, 0.78],
  34. },
  35. {
  36. paragraph_id: "1b",
  37. nested_vector: [-0.12, 1.56, 0.42],
  38. },
  39. {
  40. paragraph_id: "1c",
  41. nested_vector: [1, -1, 0],
  42. },
  43. ],
  44. topic: ["ai"],
  45. },
  46. });
  47. console.log(response1);
  48. const response2 = await client.index({
  49. index: "retrievers_example_nested",
  50. id: 2,
  51. document: {
  52. nested_field: [
  53. {
  54. paragraph_id: "2a",
  55. nested_vector: [0.23, 1.24, 0.65],
  56. },
  57. ],
  58. topic: ["information_retrieval"],
  59. },
  60. });
  61. console.log(response2);
  62. const response3 = await client.index({
  63. index: "retrievers_example_nested",
  64. id: 3,
  65. document: {
  66. topic: ["ai"],
  67. },
  68. });
  69. console.log(response3);
  70. const response4 = await client.indices.refresh({
  71. index: "retrievers_example_nested",
  72. });
  73. console.log(response4);
  1. PUT retrievers_example_nested
  2. {
  3. "mappings": {
  4. "properties": {
  5. "nested_field": {
  6. "type": "nested",
  7. "properties": {
  8. "paragraph_id": {
  9. "type": "keyword"
  10. },
  11. "nested_vector": {
  12. "type": "dense_vector",
  13. "dims": 3,
  14. "similarity": "l2_norm",
  15. "index": true
  16. }
  17. }
  18. },
  19. "topic": {
  20. "type": "keyword"
  21. }
  22. }
  23. }
  24. }
  25. POST /retrievers_example_nested/_doc/1
  26. {
  27. "nested_field": [
  28. {
  29. "paragraph_id": "1a",
  30. "nested_vector": [
  31. -1.12,
  32. -0.59,
  33. 0.78
  34. ]
  35. },
  36. {
  37. "paragraph_id": "1b",
  38. "nested_vector": [
  39. -0.12,
  40. 1.56,
  41. 0.42
  42. ]
  43. },
  44. {
  45. "paragraph_id": "1c",
  46. "nested_vector": [
  47. 1,
  48. -1,
  49. 0
  50. ]
  51. }
  52. ],
  53. "topic": [
  54. "ai"
  55. ]
  56. }
  57. POST /retrievers_example_nested/_doc/2
  58. {
  59. "nested_field": [
  60. {
  61. "paragraph_id": "2a",
  62. "nested_vector": [
  63. 0.23,
  64. 1.24,
  65. 0.65
  66. ]
  67. }
  68. ],
  69. "topic": [
  70. "information_retrieval"
  71. ]
  72. }
  73. POST /retrievers_example_nested/_doc/3
  74. {
  75. "topic": [
  76. "ai"
  77. ]
  78. }
  79. POST /retrievers_example_nested/_refresh

Now we can run an rrf retriever query and also compute inner hits for the nested_field.nested_vector field, based on the knn query specified.

  1. resp = client.search(
  2. index="retrievers_example_nested",
  3. retriever={
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "nested": {
  10. "path": "nested_field",
  11. "inner_hits": {
  12. "name": "nested_vector",
  13. "_source": False,
  14. "fields": [
  15. "nested_field.paragraph_id"
  16. ]
  17. },
  18. "query": {
  19. "knn": {
  20. "field": "nested_field.nested_vector",
  21. "query_vector": [
  22. 1,
  23. 0,
  24. 0.5
  25. ],
  26. "k": 10
  27. }
  28. }
  29. }
  30. }
  31. }
  32. },
  33. {
  34. "standard": {
  35. "query": {
  36. "term": {
  37. "topic": "ai"
  38. }
  39. }
  40. }
  41. }
  42. ],
  43. "rank_window_size": 10,
  44. "rank_constant": 1
  45. }
  46. },
  47. source=[
  48. "topic"
  49. ],
  50. )
  51. print(resp)
  1. const response = await client.search({
  2. index: "retrievers_example_nested",
  3. retriever: {
  4. rrf: {
  5. retrievers: [
  6. {
  7. standard: {
  8. query: {
  9. nested: {
  10. path: "nested_field",
  11. inner_hits: {
  12. name: "nested_vector",
  13. _source: false,
  14. fields: ["nested_field.paragraph_id"],
  15. },
  16. query: {
  17. knn: {
  18. field: "nested_field.nested_vector",
  19. query_vector: [1, 0, 0.5],
  20. k: 10,
  21. },
  22. },
  23. },
  24. },
  25. },
  26. },
  27. {
  28. standard: {
  29. query: {
  30. term: {
  31. topic: "ai",
  32. },
  33. },
  34. },
  35. },
  36. ],
  37. rank_window_size: 10,
  38. rank_constant: 1,
  39. },
  40. },
  41. _source: ["topic"],
  42. });
  43. console.log(response);
  1. GET /retrievers_example_nested/_search
  2. {
  3. "retriever": {
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "nested": {
  10. "path": "nested_field",
  11. "inner_hits": {
  12. "name": "nested_vector",
  13. "_source": false,
  14. "fields": [
  15. "nested_field.paragraph_id"
  16. ]
  17. },
  18. "query": {
  19. "knn": {
  20. "field": "nested_field.nested_vector",
  21. "query_vector": [
  22. 1,
  23. 0,
  24. 0.5
  25. ],
  26. "k": 10
  27. }
  28. }
  29. }
  30. }
  31. }
  32. },
  33. {
  34. "standard": {
  35. "query": {
  36. "term": {
  37. "topic": "ai"
  38. }
  39. }
  40. }
  41. }
  42. ],
  43. "rank_window_size": 10,
  44. "rank_constant": 1
  45. }
  46. },
  47. "_source": [
  48. "topic"
  49. ]
  50. }

This would propagate the inner_hits defined for the knn query to the rrf retriever, and compute inner hits for rrf‘s top results.

Example response

  1. {
  2. "took": 42,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 3,
  13. "relation": "eq"
  14. },
  15. "max_score": 1.0,
  16. "hits": [
  17. {
  18. "_index": "retrievers_example_nested",
  19. "_id": "1",
  20. "_score": 1.0,
  21. "_source": {
  22. "topic": [
  23. "ai"
  24. ]
  25. },
  26. "inner_hits": {
  27. "nested_vector": {
  28. "hits": {
  29. "total": {
  30. "value": 3,
  31. "relation": "eq"
  32. },
  33. "max_score": 0.44353113,
  34. "hits": [
  35. {
  36. "_index": "retrievers_example_nested",
  37. "_id": "1",
  38. "_nested": {
  39. "field": "nested_field",
  40. "offset": 2
  41. },
  42. "_score": 0.44353113,
  43. "fields": {
  44. "nested_field": [
  45. {
  46. "paragraph_id": [
  47. "1c"
  48. ]
  49. }
  50. ]
  51. }
  52. },
  53. {
  54. "_index": "retrievers_example_nested",
  55. "_id": "1",
  56. "_nested": {
  57. "field": "nested_field",
  58. "offset": 1
  59. },
  60. "_score": 0.26567122,
  61. "fields": {
  62. "nested_field": [
  63. {
  64. "paragraph_id": [
  65. "1b"
  66. ]
  67. }
  68. ]
  69. }
  70. },
  71. {
  72. "_index": "retrievers_example_nested",
  73. "_id": "1",
  74. "_nested": {
  75. "field": "nested_field",
  76. "offset": 0
  77. },
  78. "_score": 0.18478848,
  79. "fields": {
  80. "nested_field": [
  81. {
  82. "paragraph_id": [
  83. "1a"
  84. ]
  85. }
  86. ]
  87. }
  88. }
  89. ]
  90. }
  91. }
  92. }
  93. },
  94. {
  95. "_index": "retrievers_example_nested",
  96. "_id": "2",
  97. "_score": 0.33333334,
  98. "_source": {
  99. "topic": [
  100. "information_retrieval"
  101. ]
  102. },
  103. "inner_hits": {
  104. "nested_vector": {
  105. "hits": {
  106. "total": {
  107. "value": 1,
  108. "relation": "eq"
  109. },
  110. "max_score": 0.32002488,
  111. "hits": [
  112. {
  113. "_index": "retrievers_example_nested",
  114. "_id": "2",
  115. "_nested": {
  116. "field": "nested_field",
  117. "offset": 0
  118. },
  119. "_score": 0.32002488,
  120. "fields": {
  121. "nested_field": [
  122. {
  123. "paragraph_id": [
  124. "2a"
  125. ]
  126. }
  127. ]
  128. }
  129. }
  130. ]
  131. }
  132. }
  133. }
  134. },
  135. {
  136. "_index": "retrievers_example_nested",
  137. "_id": "3",
  138. "_score": 0.33333334,
  139. "_source": {
  140. "topic": [
  141. "ai"
  142. ]
  143. },
  144. "inner_hits": {
  145. "nested_vector": {
  146. "hits": {
  147. "total": {
  148. "value": 0,
  149. "relation": "eq"
  150. },
  151. "max_score": null,
  152. "hits": []
  153. }
  154. }
  155. }
  156. }
  157. ]
  158. }
  159. }

Note: if using more than one inner_hits we need to provide custom names for each inner_hits so that they are unique across all retrievers within the request.

Example: Combine RRF with aggregations

Retrievers support both composability and most of the standard _search functionality. For instance, we can compute aggregations with the rrf retriever. When using a compound retriever, the aggregations are computed based on its nested retrievers. In the following example, the terms aggregation for the topic field will include all results, not just the top rank_window_size, from the 2 nested retrievers, i.e. all documents whose year field is greater than 2023, and whose topic field matches the term elastic.

  1. resp = client.search(
  2. index="retrievers_example",
  3. retriever={
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "range": {
  10. "year": {
  11. "gt": 2023
  12. }
  13. }
  14. }
  15. }
  16. },
  17. {
  18. "standard": {
  19. "query": {
  20. "term": {
  21. "topic": "elastic"
  22. }
  23. }
  24. }
  25. }
  26. ],
  27. "rank_window_size": 10,
  28. "rank_constant": 1
  29. }
  30. },
  31. source=False,
  32. aggs={
  33. "topics": {
  34. "terms": {
  35. "field": "topic"
  36. }
  37. }
  38. },
  39. )
  40. print(resp)
  1. const response = await client.search({
  2. index: "retrievers_example",
  3. retriever: {
  4. rrf: {
  5. retrievers: [
  6. {
  7. standard: {
  8. query: {
  9. range: {
  10. year: {
  11. gt: 2023,
  12. },
  13. },
  14. },
  15. },
  16. },
  17. {
  18. standard: {
  19. query: {
  20. term: {
  21. topic: "elastic",
  22. },
  23. },
  24. },
  25. },
  26. ],
  27. rank_window_size: 10,
  28. rank_constant: 1,
  29. },
  30. },
  31. _source: false,
  32. aggs: {
  33. topics: {
  34. terms: {
  35. field: "topic",
  36. },
  37. },
  38. },
  39. });
  40. console.log(response);
  1. GET retrievers_example/_search
  2. {
  3. "retriever": {
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "range": {
  10. "year": {
  11. "gt": 2023
  12. }
  13. }
  14. }
  15. }
  16. },
  17. {
  18. "standard": {
  19. "query": {
  20. "term": {
  21. "topic": "elastic"
  22. }
  23. }
  24. }
  25. }
  26. ],
  27. "rank_window_size": 10,
  28. "rank_constant": 1
  29. }
  30. },
  31. "_source": false,
  32. "aggs": {
  33. "topics": {
  34. "terms": {
  35. "field": "topic"
  36. }
  37. }
  38. }
  39. }

Example response

  1. {
  2. "took": 42,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 4,
  13. "relation": "eq"
  14. },
  15. "max_score": 0.5833334,
  16. "hits": [
  17. {
  18. "_index": "retrievers_example",
  19. "_id": "5",
  20. "_score": 0.5833334
  21. },
  22. {
  23. "_index": "retrievers_example",
  24. "_id": "1",
  25. "_score": 0.5
  26. },
  27. {
  28. "_index": "retrievers_example",
  29. "_id": "4",
  30. "_score": 0.5
  31. },
  32. {
  33. "_index": "retrievers_example",
  34. "_id": "3",
  35. "_score": 0.33333334
  36. }
  37. ]
  38. },
  39. "aggregations": {
  40. "topics": {
  41. "doc_count_error_upper_bound": 0,
  42. "sum_other_doc_count": 0,
  43. "buckets": [
  44. {
  45. "key": "ai",
  46. "doc_count": 3
  47. },
  48. {
  49. "key": "elastic",
  50. "doc_count": 2
  51. },
  52. {
  53. "key": "assistant",
  54. "doc_count": 1
  55. },
  56. {
  57. "key": "documentation",
  58. "doc_count": 1
  59. },
  60. {
  61. "key": "information_retrieval",
  62. "doc_count": 1
  63. },
  64. {
  65. "key": "llm",
  66. "doc_count": 1
  67. },
  68. {
  69. "key": "observability",
  70. "doc_count": 1
  71. },
  72. {
  73. "key": "security",
  74. "doc_count": 1
  75. }
  76. ]
  77. }
  78. }
  79. }

Example: Explainability with multiple retrievers

By adding explain: true to the request, each retriever will now provide a detailed explanation of all the steps and calculations required to compute the final score. Composability is fully supported in the context of explain, and each retriever will provide its own explanation, as shown in the example below.

  1. resp = client.search(
  2. index="retrievers_example",
  3. retriever={
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "term": {
  10. "topic": "elastic"
  11. }
  12. }
  13. }
  14. },
  15. {
  16. "rrf": {
  17. "retrievers": [
  18. {
  19. "standard": {
  20. "query": {
  21. "query_string": {
  22. "query": "(information retrieval) OR (artificial intelligence)",
  23. "default_field": "text"
  24. }
  25. }
  26. }
  27. },
  28. {
  29. "knn": {
  30. "field": "vector",
  31. "query_vector": [
  32. 0.23,
  33. 0.67,
  34. 0.89
  35. ],
  36. "k": 3,
  37. "num_candidates": 5
  38. }
  39. }
  40. ],
  41. "rank_window_size": 10,
  42. "rank_constant": 1
  43. }
  44. }
  45. ],
  46. "rank_window_size": 10,
  47. "rank_constant": 1
  48. }
  49. },
  50. source=False,
  51. size=1,
  52. explain=True,
  53. )
  54. print(resp)
  1. const response = await client.search({
  2. index: "retrievers_example",
  3. retriever: {
  4. rrf: {
  5. retrievers: [
  6. {
  7. standard: {
  8. query: {
  9. term: {
  10. topic: "elastic",
  11. },
  12. },
  13. },
  14. },
  15. {
  16. rrf: {
  17. retrievers: [
  18. {
  19. standard: {
  20. query: {
  21. query_string: {
  22. query:
  23. "(information retrieval) OR (artificial intelligence)",
  24. default_field: "text",
  25. },
  26. },
  27. },
  28. },
  29. {
  30. knn: {
  31. field: "vector",
  32. query_vector: [0.23, 0.67, 0.89],
  33. k: 3,
  34. num_candidates: 5,
  35. },
  36. },
  37. ],
  38. rank_window_size: 10,
  39. rank_constant: 1,
  40. },
  41. },
  42. ],
  43. rank_window_size: 10,
  44. rank_constant: 1,
  45. },
  46. },
  47. _source: false,
  48. size: 1,
  49. explain: true,
  50. });
  51. console.log(response);
  1. GET /retrievers_example/_search
  2. {
  3. "retriever": {
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "standard": {
  8. "query": {
  9. "term": {
  10. "topic": "elastic"
  11. }
  12. }
  13. }
  14. },
  15. {
  16. "rrf": {
  17. "retrievers": [
  18. {
  19. "standard": {
  20. "query": {
  21. "query_string": {
  22. "query": "(information retrieval) OR (artificial intelligence)",
  23. "default_field": "text"
  24. }
  25. }
  26. }
  27. },
  28. {
  29. "knn": {
  30. "field": "vector",
  31. "query_vector": [
  32. 0.23,
  33. 0.67,
  34. 0.89
  35. ],
  36. "k": 3,
  37. "num_candidates": 5
  38. }
  39. }
  40. ],
  41. "rank_window_size": 10,
  42. "rank_constant": 1
  43. }
  44. }
  45. ],
  46. "rank_window_size": 10,
  47. "rank_constant": 1
  48. }
  49. },
  50. "_source": false,
  51. "size": 1,
  52. "explain": true
  53. }

The output of which, albeit a bit verbose, will provide all the necessary info to assist in debugging and reason with ranking.

Example response

  1. {
  2. "took": 42,
  3. "timed_out": false,
  4. "_shards": {
  5. "total": 1,
  6. "successful": 1,
  7. "skipped": 0,
  8. "failed": 0
  9. },
  10. "hits": {
  11. "total": {
  12. "value": 5,
  13. "relation": "eq"
  14. },
  15. "max_score": 0.5,
  16. "hits": [
  17. {
  18. "_shard": "[retrievers_example][0]",
  19. "_node": "jnrdZFKS3abUgWVsVdj2Vg",
  20. "_index": "retrievers_example",
  21. "_id": "1",
  22. "_score": 0.5,
  23. "_explanation": {
  24. "value": 0.5,
  25. "description": "rrf score: [0.5] computed for initial ranks [0, 1] with rankConstant: [1] as sum of [1 / (rank + rankConstant)] for each query",
  26. "details": [
  27. {
  28. "value": 0.0,
  29. "description": "rrf score: [0], result not found in query at index [0]",
  30. "details": []
  31. },
  32. {
  33. "value": 1,
  34. "description": "rrf score: [0.5], for rank [1] in query at index [1] computed as [1 / (1 + 1)], for matching query with score",
  35. "details": [
  36. {
  37. "value": 0.8333334,
  38. "description": "rrf score: [0.8333334] computed for initial ranks [2, 1] with rankConstant: [1] as sum of [1 / (rank + rankConstant)] for each query",
  39. "details": [
  40. {
  41. "value": 2,
  42. "description": "rrf score: [0.33333334], for rank [2] in query at index [0] computed as [1 / (2 + 1)], for matching query with score",
  43. "details": [
  44. {
  45. "value": 2.8129659,
  46. "description": "sum of:",
  47. "details": [
  48. {
  49. "value": 1.4064829,
  50. "description": "weight(text:information in 0) [PerFieldSimilarity], result of:",
  51. "details": [
  52. ***
  53. ]
  54. },
  55. {
  56. "value": 1.4064829,
  57. "description": "weight(text:retrieval in 0) [PerFieldSimilarity], result of:",
  58. "details": [
  59. ***
  60. ]
  61. }
  62. ]
  63. }
  64. ]
  65. },
  66. {
  67. "value": 1,
  68. "description": "rrf score: [0.5], for rank [1] in query at index [1] computed as [1 / (1 + 1)], for matching query with score",
  69. "details": [
  70. {
  71. "value": 1,
  72. "description": "doc [0] with an original score of [1.0] is at rank [1] from the following source queries.",
  73. "details": [
  74. {
  75. "value": 1.0,
  76. "description": "found vector with calculated similarity: 1.0",
  77. "details": []
  78. }
  79. ]
  80. }
  81. ]
  82. }
  83. ]
  84. }
  85. ]
  86. }
  87. ]
  88. }
  89. }
  90. ]
  91. }
  92. }

Example: Rerank results of an RRF retriever

To demonstrate the full functionality of retrievers, the following examples also require access to a semantic reranking model set up using the Elastic inference APIs.

In this example we’ll set up a reranking service and use it with the text_similarity_reranker retriever to rerank our top results.

  1. resp = client.inference.put(
  2. task_type="rerank",
  3. inference_id="my-rerank-model",
  4. inference_config={
  5. "service": "cohere",
  6. "service_settings": {
  7. "model_id": "rerank-english-v3.0",
  8. "api_key": "{{COHERE_API_KEY}}"
  9. }
  10. },
  11. )
  12. print(resp)
  1. const response = await client.inference.put({
  2. task_type: "rerank",
  3. inference_id: "my-rerank-model",
  4. inference_config: {
  5. service: "cohere",
  6. service_settings: {
  7. model_id: "rerank-english-v3.0",
  8. api_key: "{{COHERE_API_KEY}}",
  9. },
  10. },
  11. });
  12. console.log(response);
  1. PUT _inference/rerank/my-rerank-model
  2. {
  3. "service": "cohere",
  4. "service_settings": {
  5. "model_id": "rerank-english-v3.0",
  6. "api_key": "{{COHERE_API_KEY}}"
  7. }
  8. }

Let’s start by reranking the results of the rrf retriever in our previous example.

  1. resp = client.search(
  2. index="retrievers_example",
  3. retriever={
  4. "text_similarity_reranker": {
  5. "retriever": {
  6. "rrf": {
  7. "retrievers": [
  8. {
  9. "standard": {
  10. "query": {
  11. "query_string": {
  12. "query": "(information retrieval) OR (artificial intelligence)",
  13. "default_field": "text"
  14. }
  15. }
  16. }
  17. },
  18. {
  19. "knn": {
  20. "field": "vector",
  21. "query_vector": [
  22. 0.23,
  23. 0.67,
  24. 0.89
  25. ],
  26. "k": 3,
  27. "num_candidates": 5
  28. }
  29. }
  30. ],
  31. "rank_window_size": 10,
  32. "rank_constant": 1
  33. }
  34. },
  35. "field": "text",
  36. "inference_id": "my-rerank-model",
  37. "inference_text": "What are the state of the art applications of AI in information retrieval?"
  38. }
  39. },
  40. source=False,
  41. )
  42. print(resp)
  1. const response = await client.search({
  2. index: "retrievers_example",
  3. retriever: {
  4. text_similarity_reranker: {
  5. retriever: {
  6. rrf: {
  7. retrievers: [
  8. {
  9. standard: {
  10. query: {
  11. query_string: {
  12. query:
  13. "(information retrieval) OR (artificial intelligence)",
  14. default_field: "text",
  15. },
  16. },
  17. },
  18. },
  19. {
  20. knn: {
  21. field: "vector",
  22. query_vector: [0.23, 0.67, 0.89],
  23. k: 3,
  24. num_candidates: 5,
  25. },
  26. },
  27. ],
  28. rank_window_size: 10,
  29. rank_constant: 1,
  30. },
  31. },
  32. field: "text",
  33. inference_id: "my-rerank-model",
  34. inference_text:
  35. "What are the state of the art applications of AI in information retrieval?",
  36. },
  37. },
  38. _source: false,
  39. });
  40. console.log(response);
  1. GET retrievers_example/_search
  2. {
  3. "retriever": {
  4. "text_similarity_reranker": {
  5. "retriever": {
  6. "rrf": {
  7. "retrievers": [
  8. {
  9. "standard": {
  10. "query": {
  11. "query_string": {
  12. "query": "(information retrieval) OR (artificial intelligence)",
  13. "default_field": "text"
  14. }
  15. }
  16. }
  17. },
  18. {
  19. "knn": {
  20. "field": "vector",
  21. "query_vector": [
  22. 0.23,
  23. 0.67,
  24. 0.89
  25. ],
  26. "k": 3,
  27. "num_candidates": 5
  28. }
  29. }
  30. ],
  31. "rank_window_size": 10,
  32. "rank_constant": 1
  33. }
  34. },
  35. "field": "text",
  36. "inference_id": "my-rerank-model",
  37. "inference_text": "What are the state of the art applications of AI in information retrieval?"
  38. }
  39. },
  40. "_source": false
  41. }

Example: RRF with semantic reranker

For this example, we’ll replace the rrf’s standard retriever with the text_similarity_reranker retriever, using the my-rerank-model reranker we previously configured. Since this is a reranker, it needs an initial pool of documents to work with. In this case, we’ll rerank the top rank_window_size documents matching the ai topic.

  1. resp = client.search(
  2. index="retrievers_example",
  3. retriever={
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "knn": {
  8. "field": "vector",
  9. "query_vector": [
  10. 0.23,
  11. 0.67,
  12. 0.89
  13. ],
  14. "k": 3,
  15. "num_candidates": 5
  16. }
  17. },
  18. {
  19. "text_similarity_reranker": {
  20. "retriever": {
  21. "standard": {
  22. "query": {
  23. "term": {
  24. "topic": "ai"
  25. }
  26. }
  27. }
  28. },
  29. "field": "text",
  30. "inference_id": "my-rerank-model",
  31. "inference_text": "Can I use generative AI to identify user intent and improve search relevance?"
  32. }
  33. }
  34. ],
  35. "rank_window_size": 10,
  36. "rank_constant": 1
  37. }
  38. },
  39. source=False,
  40. )
  41. print(resp)
  1. const response = await client.search({
  2. index: "retrievers_example",
  3. retriever: {
  4. rrf: {
  5. retrievers: [
  6. {
  7. knn: {
  8. field: "vector",
  9. query_vector: [0.23, 0.67, 0.89],
  10. k: 3,
  11. num_candidates: 5,
  12. },
  13. },
  14. {
  15. text_similarity_reranker: {
  16. retriever: {
  17. standard: {
  18. query: {
  19. term: {
  20. topic: "ai",
  21. },
  22. },
  23. },
  24. },
  25. field: "text",
  26. inference_id: "my-rerank-model",
  27. inference_text:
  28. "Can I use generative AI to identify user intent and improve search relevance?",
  29. },
  30. },
  31. ],
  32. rank_window_size: 10,
  33. rank_constant: 1,
  34. },
  35. },
  36. _source: false,
  37. });
  38. console.log(response);
  1. GET /retrievers_example/_search
  2. {
  3. "retriever": {
  4. "rrf": {
  5. "retrievers": [
  6. {
  7. "knn": {
  8. "field": "vector",
  9. "query_vector": [
  10. 0.23,
  11. 0.67,
  12. 0.89
  13. ],
  14. "k": 3,
  15. "num_candidates": 5
  16. }
  17. },
  18. {
  19. "text_similarity_reranker": {
  20. "retriever": {
  21. "standard": {
  22. "query": {
  23. "term": {
  24. "topic": "ai"
  25. }
  26. }
  27. }
  28. },
  29. "field": "text",
  30. "inference_id": "my-rerank-model",
  31. "inference_text": "Can I use generative AI to identify user intent and improve search relevance?"
  32. }
  33. }
  34. ],
  35. "rank_window_size": 10,
  36. "rank_constant": 1
  37. }
  38. },
  39. "_source": false
  40. }

Example: Chaining multiple semantic rerankers

Full composability means we can chain together multiple retrievers of the same type. For instance, imagine we have a computationally expensive reranker that’s specialized for AI content. We can rerank the results of a text_similarity_reranker using another text_similarity_reranker retriever. Each reranker can operate on different fields and/or use different inference services.

  1. resp = client.search(
  2. index="retrievers_example",
  3. retriever={
  4. "text_similarity_reranker": {
  5. "retriever": {
  6. "text_similarity_reranker": {
  7. "retriever": {
  8. "knn": {
  9. "field": "vector",
  10. "query_vector": [
  11. 0.23,
  12. 0.67,
  13. 0.89
  14. ],
  15. "k": 3,
  16. "num_candidates": 5
  17. }
  18. },
  19. "rank_window_size": 100,
  20. "field": "text",
  21. "inference_id": "my-rerank-model",
  22. "inference_text": "What are the state of the art applications of AI in information retrieval?"
  23. }
  24. },
  25. "rank_window_size": 10,
  26. "field": "text",
  27. "inference_id": "my-other-more-expensive-rerank-model",
  28. "inference_text": "Applications of Large Language Models in technology and their impact on user satisfaction"
  29. }
  30. },
  31. source=False,
  32. )
  33. print(resp)
  1. const response = await client.search({
  2. index: "retrievers_example",
  3. retriever: {
  4. text_similarity_reranker: {
  5. retriever: {
  6. text_similarity_reranker: {
  7. retriever: {
  8. knn: {
  9. field: "vector",
  10. query_vector: [0.23, 0.67, 0.89],
  11. k: 3,
  12. num_candidates: 5,
  13. },
  14. },
  15. rank_window_size: 100,
  16. field: "text",
  17. inference_id: "my-rerank-model",
  18. inference_text:
  19. "What are the state of the art applications of AI in information retrieval?",
  20. },
  21. },
  22. rank_window_size: 10,
  23. field: "text",
  24. inference_id: "my-other-more-expensive-rerank-model",
  25. inference_text:
  26. "Applications of Large Language Models in technology and their impact on user satisfaction",
  27. },
  28. },
  29. _source: false,
  30. });
  31. console.log(response);
  1. GET retrievers_example/_search
  2. {
  3. "retriever": {
  4. "text_similarity_reranker": {
  5. "retriever": {
  6. "text_similarity_reranker": {
  7. "retriever": {
  8. "knn": {
  9. "field": "vector",
  10. "query_vector": [
  11. 0.23,
  12. 0.67,
  13. 0.89
  14. ],
  15. "k": 3,
  16. "num_candidates": 5
  17. }
  18. },
  19. "rank_window_size": 100,
  20. "field": "text",
  21. "inference_id": "my-rerank-model",
  22. "inference_text": "What are the state of the art applications of AI in information retrieval?"
  23. }
  24. },
  25. "rank_window_size": 10,
  26. "field": "text",
  27. "inference_id": "my-other-more-expensive-rerank-model",
  28. "inference_text": "Applications of Large Language Models in technology and their impact on user satisfaction"
  29. }
  30. },
  31. "_source": false
  32. }

Note that our example applies two reranking steps. First, we rerank the top 100 documents from the knn search using the my-rerank-model reranker. Then we pick the top 10 results and rerank them using the more fine-grained my-other-more-expensive-rerank-model.