srijithrajamohan commited on
Commit
dac83bc
·
verified ·
1 Parent(s): be4e535

Publish site

Browse files
index.html CHANGED
@@ -200,7 +200,27 @@ window.Quarto = {
200
 
201
  <div class="quarto-listing quarto-listing-container-default" id="listing-listing">
202
  <div class="list quarto-listing-default">
203
- <div class="quarto-post image-right" data-index="0" data-listing-date-sort="1773806400000" data-listing-file-modified-sort="1773875458545" data-listing-date-modified-sort="NaN" data-listing-reading-time-sort="1" data-listing-word-count-sort="14">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  <div class="body">
205
  <h3 class="no-anchor listing-title">
206
  <a href="./posts/hello.html" class="no-external">Hello</a>
 
200
 
201
  <div class="quarto-listing quarto-listing-container-default" id="listing-listing">
202
  <div class="list quarto-listing-default">
203
+ <div class="quarto-post image-right" data-index="0" data-categories="c2VhcmNoJTJDcmV0cmlldmFsJTJDYm0yNSUyQ2VtYmVkZGluZ3M=" data-listing-date-sort="1773806400000" data-listing-file-modified-sort="1773878066120" data-listing-date-modified-sort="NaN" data-listing-reading-time-sort="4" data-listing-word-count-sort="632">
204
+ <div class="body">
205
+ <h3 class="no-anchor listing-title">
206
+ <a href="./posts/hybrid-search-retrieval-bm25-modernbert.html" class="no-external">Hybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples</a>
207
+ </h3>
208
+ <div class="delink listing-description"><a href="./posts/hybrid-search-retrieval-bm25-modernbert.html" class="no-external">
209
+ <p>How to combine ModernBERT embedding similarity with BM25, plus failure modes (negation and identifiers).</p>
210
+ </a></div>
211
+ </div>
212
+ <div class="metadata">
213
+ <a href="./posts/hybrid-search-retrieval-bm25-modernbert.html" class="no-external">
214
+ <div class="listing-date">
215
+ Mar 18, 2026
216
+ </div>
217
+ <div class="listing-author">
218
+ Redis AI Research
219
+ </div>
220
+ </a>
221
+ </div>
222
+ </div>
223
+ <div class="quarto-post image-right" data-index="1" data-listing-date-sort="1773806400000" data-listing-file-modified-sort="1773875458545" data-listing-date-modified-sort="NaN" data-listing-reading-time-sort="1" data-listing-word-count-sort="14">
224
  <div class="body">
225
  <h3 class="no-anchor listing-title">
226
  <a href="./posts/hello.html" class="no-external">Hello</a>
listings.json CHANGED
@@ -2,12 +2,14 @@
2
  {
3
  "listing": "/index.html",
4
  "items": [
 
5
  "/posts/hello.html"
6
  ]
7
  },
8
  {
9
  "listing": "/posts/index.html",
10
  "items": [
 
11
  "/posts/hello.html"
12
  ]
13
  }
 
2
  {
3
  "listing": "/index.html",
4
  "items": [
5
+ "/posts/hybrid-search-retrieval-bm25-modernbert.html",
6
  "/posts/hello.html"
7
  ]
8
  },
9
  {
10
  "listing": "/posts/index.html",
11
  "items": [
12
+ "/posts/hybrid-search-retrieval-bm25-modernbert.html",
13
  "/posts/hello.html"
14
  ]
15
  }
posts/hybrid-search-retrieval-bm25-modernbert.html ADDED
@@ -0,0 +1,671 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html xmlns="http://www.w3.org/1999/xhtml" lang="en" xml:lang="en"><head>
3
+
4
+ <meta charset="utf-8">
5
+ <meta name="generator" content="quarto-1.8.27">
6
+
7
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes">
8
+
9
+ <meta name="author" content="Redis AI Research">
10
+ <meta name="dcterms.date" content="2026-03-18">
11
+ <meta name="description" content="How to combine ModernBERT embedding similarity with BM25, plus failure modes (negation and identifiers).">
12
+
13
+ <title>Hybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples – AI Research @Redis</title>
14
+ <style>
15
+ code{white-space: pre-wrap;}
16
+ span.smallcaps{font-variant: small-caps;}
17
+ div.columns{display: flex; gap: min(4vw, 1.5em);}
18
+ div.column{flex: auto; overflow-x: auto;}
19
+ div.hanging-indent{margin-left: 1.5em; text-indent: -1.5em;}
20
+ ul.task-list{list-style: none;}
21
+ ul.task-list li input[type="checkbox"] {
22
+ width: 0.8em;
23
+ margin: 0 0.8em 0.2em -1em; /* quarto-specific, see https://github.com/quarto-dev/quarto-cli/issues/4556 */
24
+ vertical-align: middle;
25
+ }
26
+ </style>
27
+
28
+
29
+ <script src="../site_libs/quarto-nav/quarto-nav.js"></script>
30
+ <script src="../site_libs/quarto-nav/headroom.min.js"></script>
31
+ <script src="../site_libs/clipboard/clipboard.min.js"></script>
32
+ <script src="../site_libs/quarto-search/autocomplete.umd.js"></script>
33
+ <script src="../site_libs/quarto-search/fuse.min.js"></script>
34
+ <script src="../site_libs/quarto-search/quarto-search.js"></script>
35
+ <meta name="quarto:offset" content="../">
36
+ <script src="../site_libs/quarto-html/quarto.js" type="module"></script>
37
+ <script src="../site_libs/quarto-html/tabsets/tabsets.js" type="module"></script>
38
+ <script src="../site_libs/quarto-html/axe/axe-check.js" type="module"></script>
39
+ <script src="../site_libs/quarto-html/popper.min.js"></script>
40
+ <script src="../site_libs/quarto-html/tippy.umd.min.js"></script>
41
+ <script src="../site_libs/quarto-html/anchor.min.js"></script>
42
+ <link href="../site_libs/quarto-html/tippy.css" rel="stylesheet">
43
+ <link href="../site_libs/quarto-html/quarto-syntax-highlighting-ed96de9b727972fe78a7b5d16c58bf87.css" rel="stylesheet" id="quarto-text-highlighting-styles">
44
+ <script src="../site_libs/bootstrap/bootstrap.min.js"></script>
45
+ <link href="../site_libs/bootstrap/bootstrap-icons.css" rel="stylesheet">
46
+ <link href="../site_libs/bootstrap/bootstrap-a8976c3e89df70b272bdfba3d2fda974.min.css" rel="stylesheet" append-hash="true" id="quarto-bootstrap" data-mode="light">
47
+ <script id="quarto-search-options" type="application/json">{
48
+ "location": "navbar",
49
+ "copy-button": false,
50
+ "collapse-after": 3,
51
+ "panel-placement": "end",
52
+ "type": "overlay",
53
+ "limit": 50,
54
+ "keyboard-shortcut": [
55
+ "f",
56
+ "/",
57
+ "s"
58
+ ],
59
+ "show-item-context": false,
60
+ "language": {
61
+ "search-no-results-text": "No results",
62
+ "search-matching-documents-text": "matching documents",
63
+ "search-copy-link-title": "Copy link to search",
64
+ "search-hide-matches-text": "Hide additional matches",
65
+ "search-more-match-text": "more match in this document",
66
+ "search-more-matches-text": "more matches in this document",
67
+ "search-clear-button-title": "Clear",
68
+ "search-text-placeholder": "",
69
+ "search-detached-cancel-button-title": "Cancel",
70
+ "search-submit-button-title": "Submit",
71
+ "search-label": "Search"
72
+ }
73
+ }</script>
74
+
75
+
76
+ </head>
77
+
78
+ <body class="nav-fixed quarto-light">
79
+
80
+ <div id="quarto-search-results"></div>
81
+ <header id="quarto-header" class="headroom fixed-top">
82
+ <nav class="navbar navbar-expand-lg " data-bs-theme="dark">
83
+ <div class="navbar-container container-fluid">
84
+ <div class="navbar-brand-container mx-auto">
85
+ <a class="navbar-brand" href="../index.html">
86
+ <span class="navbar-title">AI Research <span class="citation" data-cites="Redis">@Redis</span></span>
87
+ </a>
88
+ </div>
89
+ <div id="quarto-search" class="" title="Search"></div>
90
+ <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarCollapse" aria-controls="navbarCollapse" role="menu" aria-expanded="false" aria-label="Toggle navigation" onclick="if (window.quartoToggleHeadroom) { window.quartoToggleHeadroom(); }">
91
+ <span class="navbar-toggler-icon"></span>
92
+ </button>
93
+ <div class="collapse navbar-collapse" id="navbarCollapse">
94
+ <ul class="navbar-nav navbar-nav-scroll me-auto">
95
+ <li class="nav-item">
96
+ <a class="nav-link" href="../index.html">
97
+ <span class="menu-text">Home</span></a>
98
+ </li>
99
+ <li class="nav-item">
100
+ <a class="nav-link" href="../posts/index.html">
101
+ <span class="menu-text">Posts</span></a>
102
+ </li>
103
+ </ul>
104
+ </div> <!-- /navcollapse -->
105
+ <div class="quarto-navbar-tools">
106
+ </div>
107
+ </div> <!-- /container-fluid -->
108
+ </nav>
109
+ </header>
110
+ <!-- content -->
111
+ <div id="quarto-content" class="quarto-container page-columns page-rows-contents page-layout-article page-navbar">
112
+ <!-- sidebar -->
113
+ <!-- margin-sidebar -->
114
+ <div id="quarto-margin-sidebar" class="sidebar margin-sidebar">
115
+ <nav id="TOC" role="doc-toc" class="toc-active">
116
+ <h2 id="toc-title">On this page</h2>
117
+
118
+ <ul>
119
+ <li><a href="#example-1" id="toc-example-1" class="nav-link active" data-scroll-target="#example-1">Example 1</a></li>
120
+ <li><a href="#example-2" id="toc-example-2" class="nav-link" data-scroll-target="#example-2">Example 2</a></li>
121
+ <li><a href="#failure-mode-1---negationsdirectionality" id="toc-failure-mode-1---negationsdirectionality" class="nav-link" data-scroll-target="#failure-mode-1---negationsdirectionality">Failure mode 1 - Negations/directionality</a></li>
122
+ <li><a href="#failure-mode-2---attributesidentifiers" id="toc-failure-mode-2---attributesidentifiers" class="nav-link" data-scroll-target="#failure-mode-2---attributesidentifiers">Failure mode 2 - Attributes/Identifiers</a></li>
123
+ <li><a href="#how-does-the-hybrid-score-and-rrf-fare" id="toc-how-does-the-hybrid-score-and-rrf-fare" class="nav-link" data-scroll-target="#how-does-the-hybrid-score-and-rrf-fare">How does the hybrid score and RRF fare?</a></li>
124
+ <li><a href="#but-what-did-we-want-to-achieve-with-bm25" id="toc-but-what-did-we-want-to-achieve-with-bm25" class="nav-link" data-scroll-target="#but-what-did-we-want-to-achieve-with-bm25">But what did we want to achieve with BM25?</a></li>
125
+ </ul>
126
+ </nav>
127
+ </div>
128
+ <!-- main -->
129
+ <main class="content" id="quarto-document-content">
130
+
131
+ <header id="title-block-header" class="quarto-title-block default">
132
+ <div class="quarto-title">
133
+ <h1 class="title">Hybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples</h1>
134
+ <div class="quarto-categories">
135
+ <div class="quarto-category">search</div>
136
+ <div class="quarto-category">retrieval</div>
137
+ <div class="quarto-category">bm25</div>
138
+ <div class="quarto-category">embeddings</div>
139
+ </div>
140
+ </div>
141
+
142
+ <div>
143
+ <div class="description">
144
+ How to combine ModernBERT embedding similarity with BM25, plus failure modes (negation and identifiers).
145
+ </div>
146
+ </div>
147
+
148
+
149
+ <div class="quarto-title-meta">
150
+
151
+ <div>
152
+ <div class="quarto-title-meta-heading">Author</div>
153
+ <div class="quarto-title-meta-contents">
154
+ <p>Redis AI Research </p>
155
+ </div>
156
+ </div>
157
+
158
+ <div>
159
+ <div class="quarto-title-meta-heading">Published</div>
160
+ <div class="quarto-title-meta-contents">
161
+ <p class="date">March 18, 2026</p>
162
+ </div>
163
+ </div>
164
+
165
+
166
+ </div>
167
+
168
+
169
+
170
+ </header>
171
+
172
+
173
+ <p>Source: https://redislabs.atlassian.net/wiki/spaces/RED/pages/5798592518/Hybrid+Search+Retrieval+Combining+Semantic+Search+and+BM25+Algorithm+with+Examples</p>
174
+ <p>The examples below illustrate a hybrid search retrieval of a query from a corpus of documents by combining semantic search using ModernBERT embeddings and the BM25 algorithm.</p>
175
+ <p>The scores from each are normalized between 0 and 1 in order to create a weighted average hybrid score. The raw embedding score is also shown in the columns for diagnostics. The ranks in each measure are then combined using RRF to create another score that can be used for retrieval.</p>
176
+ <p>The most correct match for each query is highlighted in each example.</p>
177
+ <section id="example-1" class="level2">
178
+ <h2 class="anchored" data-anchor-id="example-1">Example 1</h2>
179
+ <p>(Screenshot reference: <code>Screenshot 2026-01-08 at 9.07.36 PM.png</code>)</p>
180
+ </section>
181
+ <section id="example-2" class="level2">
182
+ <h2 class="anchored" data-anchor-id="example-2">Example 2</h2>
183
+ <p>(Screenshot reference: <code>Screenshot 2026-01-08 at 9.11.31 PM.png</code>)</p>
184
+ </section>
185
+ <section id="failure-mode-1---negationsdirectionality" class="level2">
186
+ <h2 class="anchored" data-anchor-id="failure-mode-1---negationsdirectionality">Failure mode 1 - Negations/directionality</h2>
187
+ <p>BM25 is not keyword search; it up-weights rare terms and not just keywords.</p>
188
+ <p>In example 1, the key term of interest or the attribute/identifier is <code>#124</code>. However, the other terms in the query <code>semantic</code>, <code>search</code>, <code>for</code>, <code>doc</code> are all relatively rare and contribute to higher BM25 scores as evidenced in the first two records in the table index 8 and 11. These have negated intents however and therefore incorrect matches. BM25 has no ability to identify negations.</p>
189
+ <p>The similarity scores fortunately are not the highest but will most likely exceed most commonly used thresholds. Semantic search can fail to identify negated intent.</p>
190
+ </section>
191
+ <section id="failure-mode-2---attributesidentifiers" class="level2">
192
+ <h2 class="anchored" data-anchor-id="failure-mode-2---attributesidentifiers">Failure mode 2 - Attributes/Identifiers</h2>
193
+ <p>“The next three docs given by ids 5, 7 and 6 have the same phrasing but with the wrong identifier of #123, #1243 and #1233 respectively which lowers their BM25 score a bit but certainly higher than all the other documents. BM25 can detect a different attribute/identifier but the actual magnitude of this will depend on what the rest of the sentence looks like.”</p>
194
+ <ul>
195
+ <li>If there is strong lexical overlap but only the identifier is different, there will be a small drop in the BM25 score.</li>
196
+ <li>If the rest of the sentence is NULL then the BM25 score is 0.</li>
197
+ <li>If there is very little overlap in the rest of the query and the doc.</li>
198
+ </ul>
199
+ <p>BM25 can identify attribute/identifier changes but is hard to put a threshold on this score.</p>
200
+ <p>W.r.t embeddings two things are notable:</p>
201
+ <ul>
202
+ <li>There are two forces at play here: the global semantic intent match and the identifier match.</li>
203
+ <li>In 8 and 11, there is negated intent whereas there is an identifier match.</li>
204
+ <li>In 5, 6 and 7 the general intent is correct but the identifier is incorrect.</li>
205
+ <li>The resulting score is a complex interaction of the two and the answer to “Can we identify negations or attribute changes with a threshold?” is heavily data-dependent.</li>
206
+ </ul>
207
+ <p>Note that the embedding scores for 5, 7 and 6 are some of the highest because attribute/identifier differences are something that embeddings struggle to detect.</p>
208
+ </section>
209
+ <section id="how-does-the-hybrid-score-and-rrf-fare" class="level2">
210
+ <h2 class="anchored" data-anchor-id="how-does-the-hybrid-score-and-rrf-fare">How does the hybrid score and RRF fare?</h2>
211
+ <p>Averaging out with a weighted hybrid score or RRF has no impact here. In example 1, record id 9 which is the closest match still does not rank highly in either of these scenarios.</p>
212
+ <p>To summarize the above for both approaches for the failure modes, it seems that embeddings seem to have the slight edge here.</p>
213
+ <table class="caption-top table">
214
+ <thead>
215
+ <tr class="header">
216
+ <th>Method</th>
217
+ <th>Negation</th>
218
+ <th>Directionality</th>
219
+ <th>Attribute/Identifier</th>
220
+ </tr>
221
+ </thead>
222
+ <tbody>
223
+ <tr class="odd">
224
+ <td>BM25</td>
225
+ <td>N</td>
226
+ <td>N</td>
227
+ <td>N (hard to put a threshold)</td>
228
+ </tr>
229
+ <tr class="even">
230
+ <td>Embedding</td>
231
+ <td>Sometimes</td>
232
+ <td>N</td>
233
+ <td>Sometimes</td>
234
+ </tr>
235
+ </tbody>
236
+ </table>
237
+ <p>We can’t eliminate all the issues, but we can hope to mitigate based on our understanding of the data.</p>
238
+ </section>
239
+ <section id="but-what-did-we-want-to-achieve-with-bm25" class="level2">
240
+ <h2 class="anchored" data-anchor-id="but-what-did-we-want-to-achieve-with-bm25">But what did we want to achieve with BM25?</h2>
241
+ <p>We are really looking to filter out matches that do not have the right attributes/identifiers we seek. BM25 looks like it could provide that, but does more than attribute matching.</p>
242
+ <ul>
243
+ <li>Extract identifiers from query.</li>
244
+ <li>Perform embedding based retrieval of query + docs to retrieve set M.</li>
245
+ <li>Filter out docs where identifiers do not match in set M.</li>
246
+ <li>Unfortunately, this does nothing to address the negation/directionality limitation.</li>
247
+ </ul>
248
+
249
+
250
+ </section>
251
+
252
+ </main> <!-- /main -->
253
+ <script id="quarto-html-after-body" type="application/javascript">
254
+ window.document.addEventListener("DOMContentLoaded", function (event) {
255
+ const icon = "";
256
+ const anchorJS = new window.AnchorJS();
257
+ anchorJS.options = {
258
+ placement: 'right',
259
+ icon: icon
260
+ };
261
+ anchorJS.add('.anchored');
262
+ const isCodeAnnotation = (el) => {
263
+ for (const clz of el.classList) {
264
+ if (clz.startsWith('code-annotation-')) {
265
+ return true;
266
+ }
267
+ }
268
+ return false;
269
+ }
270
+ const onCopySuccess = function(e) {
271
+ // button target
272
+ const button = e.trigger;
273
+ // don't keep focus
274
+ button.blur();
275
+ // flash "checked"
276
+ button.classList.add('code-copy-button-checked');
277
+ var currentTitle = button.getAttribute("title");
278
+ button.setAttribute("title", "Copied!");
279
+ let tooltip;
280
+ if (window.bootstrap) {
281
+ button.setAttribute("data-bs-toggle", "tooltip");
282
+ button.setAttribute("data-bs-placement", "left");
283
+ button.setAttribute("data-bs-title", "Copied!");
284
+ tooltip = new bootstrap.Tooltip(button,
285
+ { trigger: "manual",
286
+ customClass: "code-copy-button-tooltip",
287
+ offset: [0, -8]});
288
+ tooltip.show();
289
+ }
290
+ setTimeout(function() {
291
+ if (tooltip) {
292
+ tooltip.hide();
293
+ button.removeAttribute("data-bs-title");
294
+ button.removeAttribute("data-bs-toggle");
295
+ button.removeAttribute("data-bs-placement");
296
+ }
297
+ button.setAttribute("title", currentTitle);
298
+ button.classList.remove('code-copy-button-checked');
299
+ }, 1000);
300
+ // clear code selection
301
+ e.clearSelection();
302
+ }
303
+ const getTextToCopy = function(trigger) {
304
+ const outerScaffold = trigger.parentElement.cloneNode(true);
305
+ const codeEl = outerScaffold.querySelector('code');
306
+ for (const childEl of codeEl.children) {
307
+ if (isCodeAnnotation(childEl)) {
308
+ childEl.remove();
309
+ }
310
+ }
311
+ return codeEl.innerText;
312
+ }
313
+ const clipboard = new window.ClipboardJS('.code-copy-button:not([data-in-quarto-modal])', {
314
+ text: getTextToCopy
315
+ });
316
+ clipboard.on('success', onCopySuccess);
317
+ if (window.document.getElementById('quarto-embedded-source-code-modal')) {
318
+ const clipboardModal = new window.ClipboardJS('.code-copy-button[data-in-quarto-modal]', {
319
+ text: getTextToCopy,
320
+ container: window.document.getElementById('quarto-embedded-source-code-modal')
321
+ });
322
+ clipboardModal.on('success', onCopySuccess);
323
+ }
324
+ var localhostRegex = new RegExp(/^(?:http|https):\/\/localhost\:?[0-9]*\//);
325
+ var mailtoRegex = new RegExp(/^mailto:/);
326
+ var filterRegex = new RegExp("https:\/\/srijithrajamohan-ai-research-redis\.static\.hf\.space");
327
+ var isInternal = (href) => {
328
+ return filterRegex.test(href) || localhostRegex.test(href) || mailtoRegex.test(href);
329
+ }
330
+ // Inspect non-navigation links and adorn them if external
331
+ var links = window.document.querySelectorAll('a[href]:not(.nav-link):not(.navbar-brand):not(.toc-action):not(.sidebar-link):not(.sidebar-item-toggle):not(.pagination-link):not(.no-external):not([aria-hidden]):not(.dropdown-item):not(.quarto-navigation-tool):not(.about-link)');
332
+ for (var i=0; i<links.length; i++) {
333
+ const link = links[i];
334
+ if (!isInternal(link.href)) {
335
+ // undo the damage that might have been done by quarto-nav.js in the case of
336
+ // links that we want to consider external
337
+ if (link.dataset.originalHref !== undefined) {
338
+ link.href = link.dataset.originalHref;
339
+ }
340
+ }
341
+ }
342
+ function tippyHover(el, contentFn, onTriggerFn, onUntriggerFn) {
343
+ const config = {
344
+ allowHTML: true,
345
+ maxWidth: 500,
346
+ delay: 100,
347
+ arrow: false,
348
+ appendTo: function(el) {
349
+ return el.parentElement;
350
+ },
351
+ interactive: true,
352
+ interactiveBorder: 10,
353
+ theme: 'quarto',
354
+ placement: 'bottom-start',
355
+ };
356
+ if (contentFn) {
357
+ config.content = contentFn;
358
+ }
359
+ if (onTriggerFn) {
360
+ config.onTrigger = onTriggerFn;
361
+ }
362
+ if (onUntriggerFn) {
363
+ config.onUntrigger = onUntriggerFn;
364
+ }
365
+ window.tippy(el, config);
366
+ }
367
+ const noterefs = window.document.querySelectorAll('a[role="doc-noteref"]');
368
+ for (var i=0; i<noterefs.length; i++) {
369
+ const ref = noterefs[i];
370
+ tippyHover(ref, function() {
371
+ // use id or data attribute instead here
372
+ let href = ref.getAttribute('data-footnote-href') || ref.getAttribute('href');
373
+ try { href = new URL(href).hash; } catch {}
374
+ const id = href.replace(/^#\/?/, "");
375
+ const note = window.document.getElementById(id);
376
+ if (note) {
377
+ return note.innerHTML;
378
+ } else {
379
+ return "";
380
+ }
381
+ });
382
+ }
383
+ const xrefs = window.document.querySelectorAll('a.quarto-xref');
384
+ const processXRef = (id, note) => {
385
+ // Strip column container classes
386
+ const stripColumnClz = (el) => {
387
+ el.classList.remove("page-full", "page-columns");
388
+ if (el.children) {
389
+ for (const child of el.children) {
390
+ stripColumnClz(child);
391
+ }
392
+ }
393
+ }
394
+ stripColumnClz(note)
395
+ if (id === null || id.startsWith('sec-')) {
396
+ // Special case sections, only their first couple elements
397
+ const container = document.createElement("div");
398
+ if (note.children && note.children.length > 2) {
399
+ container.appendChild(note.children[0].cloneNode(true));
400
+ for (let i = 1; i < note.children.length; i++) {
401
+ const child = note.children[i];
402
+ if (child.tagName === "P" && child.innerText === "") {
403
+ continue;
404
+ } else {
405
+ container.appendChild(child.cloneNode(true));
406
+ break;
407
+ }
408
+ }
409
+ if (window.Quarto?.typesetMath) {
410
+ window.Quarto.typesetMath(container);
411
+ }
412
+ return container.innerHTML
413
+ } else {
414
+ if (window.Quarto?.typesetMath) {
415
+ window.Quarto.typesetMath(note);
416
+ }
417
+ return note.innerHTML;
418
+ }
419
+ } else {
420
+ // Remove any anchor links if they are present
421
+ const anchorLink = note.querySelector('a.anchorjs-link');
422
+ if (anchorLink) {
423
+ anchorLink.remove();
424
+ }
425
+ if (window.Quarto?.typesetMath) {
426
+ window.Quarto.typesetMath(note);
427
+ }
428
+ if (note.classList.contains("callout")) {
429
+ return note.outerHTML;
430
+ } else {
431
+ return note.innerHTML;
432
+ }
433
+ }
434
+ }
435
+ for (var i=0; i<xrefs.length; i++) {
436
+ const xref = xrefs[i];
437
+ tippyHover(xref, undefined, function(instance) {
438
+ instance.disable();
439
+ let url = xref.getAttribute('href');
440
+ let hash = undefined;
441
+ if (url.startsWith('#')) {
442
+ hash = url;
443
+ } else {
444
+ try { hash = new URL(url).hash; } catch {}
445
+ }
446
+ if (hash) {
447
+ const id = hash.replace(/^#\/?/, "");
448
+ const note = window.document.getElementById(id);
449
+ if (note !== null) {
450
+ try {
451
+ const html = processXRef(id, note.cloneNode(true));
452
+ instance.setContent(html);
453
+ } finally {
454
+ instance.enable();
455
+ instance.show();
456
+ }
457
+ } else {
458
+ // See if we can fetch this
459
+ fetch(url.split('#')[0])
460
+ .then(res => res.text())
461
+ .then(html => {
462
+ const parser = new DOMParser();
463
+ const htmlDoc = parser.parseFromString(html, "text/html");
464
+ const note = htmlDoc.getElementById(id);
465
+ if (note !== null) {
466
+ const html = processXRef(id, note);
467
+ instance.setContent(html);
468
+ }
469
+ }).finally(() => {
470
+ instance.enable();
471
+ instance.show();
472
+ });
473
+ }
474
+ } else {
475
+ // See if we can fetch a full url (with no hash to target)
476
+ // This is a special case and we should probably do some content thinning / targeting
477
+ fetch(url)
478
+ .then(res => res.text())
479
+ .then(html => {
480
+ const parser = new DOMParser();
481
+ const htmlDoc = parser.parseFromString(html, "text/html");
482
+ const note = htmlDoc.querySelector('main.content');
483
+ if (note !== null) {
484
+ // This should only happen for chapter cross references
485
+ // (since there is no id in the URL)
486
+ // remove the first header
487
+ if (note.children.length > 0 && note.children[0].tagName === "HEADER") {
488
+ note.children[0].remove();
489
+ }
490
+ const html = processXRef(null, note);
491
+ instance.setContent(html);
492
+ }
493
+ }).finally(() => {
494
+ instance.enable();
495
+ instance.show();
496
+ });
497
+ }
498
+ }, function(instance) {
499
+ });
500
+ }
501
+ let selectedAnnoteEl;
502
+ const selectorForAnnotation = ( cell, annotation) => {
503
+ let cellAttr = 'data-code-cell="' + cell + '"';
504
+ let lineAttr = 'data-code-annotation="' + annotation + '"';
505
+ const selector = 'span[' + cellAttr + '][' + lineAttr + ']';
506
+ return selector;
507
+ }
508
+ const selectCodeLines = (annoteEl) => {
509
+ const doc = window.document;
510
+ const targetCell = annoteEl.getAttribute("data-target-cell");
511
+ const targetAnnotation = annoteEl.getAttribute("data-target-annotation");
512
+ const annoteSpan = window.document.querySelector(selectorForAnnotation(targetCell, targetAnnotation));
513
+ const lines = annoteSpan.getAttribute("data-code-lines").split(",");
514
+ const lineIds = lines.map((line) => {
515
+ return targetCell + "-" + line;
516
+ })
517
+ let top = null;
518
+ let height = null;
519
+ let parent = null;
520
+ if (lineIds.length > 0) {
521
+ //compute the position of the single el (top and bottom and make a div)
522
+ const el = window.document.getElementById(lineIds[0]);
523
+ top = el.offsetTop;
524
+ height = el.offsetHeight;
525
+ parent = el.parentElement.parentElement;
526
+ if (lineIds.length > 1) {
527
+ const lastEl = window.document.getElementById(lineIds[lineIds.length - 1]);
528
+ const bottom = lastEl.offsetTop + lastEl.offsetHeight;
529
+ height = bottom - top;
530
+ }
531
+ if (top !== null && height !== null && parent !== null) {
532
+ // cook up a div (if necessary) and position it
533
+ let div = window.document.getElementById("code-annotation-line-highlight");
534
+ if (div === null) {
535
+ div = window.document.createElement("div");
536
+ div.setAttribute("id", "code-annotation-line-highlight");
537
+ div.style.position = 'absolute';
538
+ parent.appendChild(div);
539
+ }
540
+ div.style.top = top - 2 + "px";
541
+ div.style.height = height + 4 + "px";
542
+ div.style.left = 0;
543
+ let gutterDiv = window.document.getElementById("code-annotation-line-highlight-gutter");
544
+ if (gutterDiv === null) {
545
+ gutterDiv = window.document.createElement("div");
546
+ gutterDiv.setAttribute("id", "code-annotation-line-highlight-gutter");
547
+ gutterDiv.style.position = 'absolute';
548
+ const codeCell = window.document.getElementById(targetCell);
549
+ const gutter = codeCell.querySelector('.code-annotation-gutter');
550
+ gutter.appendChild(gutterDiv);
551
+ }
552
+ gutterDiv.style.top = top - 2 + "px";
553
+ gutterDiv.style.height = height + 4 + "px";
554
+ }
555
+ selectedAnnoteEl = annoteEl;
556
+ }
557
+ };
558
+ const unselectCodeLines = () => {
559
+ const elementsIds = ["code-annotation-line-highlight", "code-annotation-line-highlight-gutter"];
560
+ elementsIds.forEach((elId) => {
561
+ const div = window.document.getElementById(elId);
562
+ if (div) {
563
+ div.remove();
564
+ }
565
+ });
566
+ selectedAnnoteEl = undefined;
567
+ };
568
+ // Handle positioning of the toggle
569
+ window.addEventListener(
570
+ "resize",
571
+ throttle(() => {
572
+ elRect = undefined;
573
+ if (selectedAnnoteEl) {
574
+ selectCodeLines(selectedAnnoteEl);
575
+ }
576
+ }, 10)
577
+ );
578
+ function throttle(fn, ms) {
579
+ let throttle = false;
580
+ let timer;
581
+ return (...args) => {
582
+ if(!throttle) { // first call gets through
583
+ fn.apply(this, args);
584
+ throttle = true;
585
+ } else { // all the others get throttled
586
+ if(timer) clearTimeout(timer); // cancel #2
587
+ timer = setTimeout(() => {
588
+ fn.apply(this, args);
589
+ timer = throttle = false;
590
+ }, ms);
591
+ }
592
+ };
593
+ }
594
+ // Attach click handler to the DT
595
+ const annoteDls = window.document.querySelectorAll('dt[data-target-cell]');
596
+ for (const annoteDlNode of annoteDls) {
597
+ annoteDlNode.addEventListener('click', (event) => {
598
+ const clickedEl = event.target;
599
+ if (clickedEl !== selectedAnnoteEl) {
600
+ unselectCodeLines();
601
+ const activeEl = window.document.querySelector('dt[data-target-cell].code-annotation-active');
602
+ if (activeEl) {
603
+ activeEl.classList.remove('code-annotation-active');
604
+ }
605
+ selectCodeLines(clickedEl);
606
+ clickedEl.classList.add('code-annotation-active');
607
+ } else {
608
+ // Unselect the line
609
+ unselectCodeLines();
610
+ clickedEl.classList.remove('code-annotation-active');
611
+ }
612
+ });
613
+ }
614
+ const findCites = (el) => {
615
+ const parentEl = el.parentElement;
616
+ if (parentEl) {
617
+ const cites = parentEl.dataset.cites;
618
+ if (cites) {
619
+ return {
620
+ el,
621
+ cites: cites.split(' ')
622
+ };
623
+ } else {
624
+ return findCites(el.parentElement)
625
+ }
626
+ } else {
627
+ return undefined;
628
+ }
629
+ };
630
+ var bibliorefs = window.document.querySelectorAll('a[role="doc-biblioref"]');
631
+ for (var i=0; i<bibliorefs.length; i++) {
632
+ const ref = bibliorefs[i];
633
+ const citeInfo = findCites(ref);
634
+ if (citeInfo) {
635
+ tippyHover(citeInfo.el, function() {
636
+ var popup = window.document.createElement('div');
637
+ citeInfo.cites.forEach(function(cite) {
638
+ var citeDiv = window.document.createElement('div');
639
+ citeDiv.classList.add('hanging-indent');
640
+ citeDiv.classList.add('csl-entry');
641
+ var biblioDiv = window.document.getElementById('ref-' + cite);
642
+ if (biblioDiv) {
643
+ citeDiv.innerHTML = biblioDiv.innerHTML;
644
+ }
645
+ popup.appendChild(citeDiv);
646
+ });
647
+ return popup.innerHTML;
648
+ });
649
+ }
650
+ }
651
+ });
652
+ </script>
653
+ </div> <!-- /content -->
654
+ <footer class="footer">
655
+ <div class="nav-footer">
656
+ <div class="nav-footer-left">
657
+ <p>Discover this project on Hugging Face Spaces: https://huggingface.co/spaces/srijithrajamohan/ai-research-redis</p>
658
+ </div>
659
+ <div class="nav-footer-center">
660
+ &nbsp;
661
+ </div>
662
+ <div class="nav-footer-right">
663
+ &nbsp;
664
+ </div>
665
+ </div>
666
+ </footer>
667
+
668
+
669
+
670
+
671
+ </body></html>
posts/index.html CHANGED
@@ -199,7 +199,27 @@ window.Quarto = {
199
 
200
  <div class="quarto-listing quarto-listing-container-default" id="listing-listing">
201
  <div class="list quarto-listing-default">
202
- <div class="quarto-post image-right" data-index="0" data-listing-date-sort="1773806400000" data-listing-file-modified-sort="1773875458545" data-listing-date-modified-sort="NaN" data-listing-reading-time-sort="1" data-listing-word-count-sort="14">
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
  <div class="body">
204
  <h3 class="no-anchor listing-title">
205
  <a href="../posts/hello.html" class="no-external">Hello</a>
 
199
 
200
  <div class="quarto-listing quarto-listing-container-default" id="listing-listing">
201
  <div class="list quarto-listing-default">
202
+ <div class="quarto-post image-right" data-index="0" data-categories="c2VhcmNoJTJDcmV0cmlldmFsJTJDYm0yNSUyQ2VtYmVkZGluZ3M=" data-listing-date-sort="1773806400000" data-listing-file-modified-sort="1773878066120" data-listing-date-modified-sort="NaN" data-listing-reading-time-sort="4" data-listing-word-count-sort="632">
203
+ <div class="body">
204
+ <h3 class="no-anchor listing-title">
205
+ <a href="../posts/hybrid-search-retrieval-bm25-modernbert.html" class="no-external">Hybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples</a>
206
+ </h3>
207
+ <div class="delink listing-description"><a href="../posts/hybrid-search-retrieval-bm25-modernbert.html" class="no-external">
208
+ <p>How to combine ModernBERT embedding similarity with BM25, plus failure modes (negation and identifiers).</p>
209
+ </a></div>
210
+ </div>
211
+ <div class="metadata">
212
+ <a href="../posts/hybrid-search-retrieval-bm25-modernbert.html" class="no-external">
213
+ <div class="listing-date">
214
+ Mar 18, 2026
215
+ </div>
216
+ <div class="listing-author">
217
+ Redis AI Research
218
+ </div>
219
+ </a>
220
+ </div>
221
+ </div>
222
+ <div class="quarto-post image-right" data-index="1" data-listing-date-sort="1773806400000" data-listing-file-modified-sort="1773875458545" data-listing-date-modified-sort="NaN" data-listing-reading-time-sort="1" data-listing-word-count-sort="14">
223
  <div class="body">
224
  <h3 class="no-anchor listing-title">
225
  <a href="../posts/hello.html" class="no-external">Hello</a>
search.json CHANGED
@@ -4,14 +4,63 @@
4
  "href": "index.html",
5
  "title": "AI Research @Redis",
6
  "section": "",
7
- "text": "Homepage of the AI Research team at Redis.\n\n\n\n\n\n\n\n\n\nHello\n\n\nFirst Quarto post (template).\n\n\n\n\n\nMar 18, 2026\n\n\nRedis AI Research\n\n\n\n\n\nNo matching items"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  },
9
  {
10
  "objectID": "posts/index.html",
11
  "href": "posts/index.html",
12
  "title": "Posts",
13
  "section": "",
14
- "text": "Hello\n\n\nFirst Quarto post (template).\n\n\n\n\n\nMar 18, 2026\n\n\nRedis AI Research\n\n\n\n\n\nNo matching items"
15
  },
16
  {
17
  "objectID": "posts/hello.html",
 
4
  "href": "index.html",
5
  "title": "AI Research @Redis",
6
  "section": "",
7
+ "text": "Homepage of the AI Research team at Redis.\n\n\n\n\n\n\n\n\n\nHybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples\n\n\nHow to combine ModernBERT embedding similarity with BM25, plus failure modes (negation and identifiers).\n\n\n\n\n\nMar 18, 2026\n\n\nRedis AI Research\n\n\n\n\n\n\n\nHello\n\n\nFirst Quarto post (template).\n\n\n\n\n\nMar 18, 2026\n\n\nRedis AI Research\n\n\n\n\n\nNo matching items"
8
+ },
9
+ {
10
+ "objectID": "posts/hybrid-search-retrieval-bm25-modernbert.html",
11
+ "href": "posts/hybrid-search-retrieval-bm25-modernbert.html",
12
+ "title": "Hybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples",
13
+ "section": "",
14
+ "text": "Source: https://redislabs.atlassian.net/wiki/spaces/RED/pages/5798592518/Hybrid+Search+Retrieval+Combining+Semantic+Search+and+BM25+Algorithm+with+Examples\nThe examples below illustrate a hybrid search retrieval of a query from a corpus of documents by combining semantic search using ModernBERT embeddings and the BM25 algorithm.\nThe scores from each are normalized between 0 and 1 in order to create a weighted average hybrid score. The raw embedding score is also shown in the columns for diagnostics. The ranks in each measure are then combined using RRF to create another score that can be used for retrieval.\nThe most correct match for each query is highlighted in each example."
15
+ },
16
+ {
17
+ "objectID": "posts/hybrid-search-retrieval-bm25-modernbert.html#example-1",
18
+ "href": "posts/hybrid-search-retrieval-bm25-modernbert.html#example-1",
19
+ "title": "Hybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples",
20
+ "section": "Example 1",
21
+ "text": "Example 1\n(Screenshot reference: Screenshot 2026-01-08 at 9.07.36 PM.png)"
22
+ },
23
+ {
24
+ "objectID": "posts/hybrid-search-retrieval-bm25-modernbert.html#example-2",
25
+ "href": "posts/hybrid-search-retrieval-bm25-modernbert.html#example-2",
26
+ "title": "Hybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples",
27
+ "section": "Example 2",
28
+ "text": "Example 2\n(Screenshot reference: Screenshot 2026-01-08 at 9.11.31 PM.png)"
29
+ },
30
+ {
31
+ "objectID": "posts/hybrid-search-retrieval-bm25-modernbert.html#failure-mode-1---negationsdirectionality",
32
+ "href": "posts/hybrid-search-retrieval-bm25-modernbert.html#failure-mode-1---negationsdirectionality",
33
+ "title": "Hybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples",
34
+ "section": "Failure mode 1 - Negations/directionality",
35
+ "text": "Failure mode 1 - Negations/directionality\nBM25 is not keyword search; it up-weights rare terms and not just keywords.\nIn example 1, the key term of interest or the attribute/identifier is #124. However, the other terms in the query semantic, search, for, doc are all relatively rare and contribute to higher BM25 scores as evidenced in the first two records in the table index 8 and 11. These have negated intents however and therefore incorrect matches. BM25 has no ability to identify negations.\nThe similarity scores fortunately are not the highest but will most likely exceed most commonly used thresholds. Semantic search can fail to identify negated intent."
36
+ },
37
+ {
38
+ "objectID": "posts/hybrid-search-retrieval-bm25-modernbert.html#failure-mode-2---attributesidentifiers",
39
+ "href": "posts/hybrid-search-retrieval-bm25-modernbert.html#failure-mode-2---attributesidentifiers",
40
+ "title": "Hybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples",
41
+ "section": "Failure mode 2 - Attributes/Identifiers",
42
+ "text": "Failure mode 2 - Attributes/Identifiers\n“The next three docs given by ids 5, 7 and 6 have the same phrasing but with the wrong identifier of #123, #1243 and #1233 respectively which lowers their BM25 score a bit but certainly higher than all the other documents. BM25 can detect a different attribute/identifier but the actual magnitude of this will depend on what the rest of the sentence looks like.”\n\nIf there is strong lexical overlap but only the identifier is different, there will be a small drop in the BM25 score.\nIf the rest of the sentence is NULL then the BM25 score is 0.\nIf there is very little overlap in the rest of the query and the doc.\n\nBM25 can identify attribute/identifier changes but is hard to put a threshold on this score.\nW.r.t embeddings two things are notable:\n\nThere are two forces at play here: the global semantic intent match and the identifier match.\nIn 8 and 11, there is negated intent whereas there is an identifier match.\nIn 5, 6 and 7 the general intent is correct but the identifier is incorrect.\nThe resulting score is a complex interaction of the two and the answer to “Can we identify negations or attribute changes with a threshold?” is heavily data-dependent.\n\nNote that the embedding scores for 5, 7 and 6 are some of the highest because attribute/identifier differences are something that embeddings struggle to detect."
43
+ },
44
+ {
45
+ "objectID": "posts/hybrid-search-retrieval-bm25-modernbert.html#how-does-the-hybrid-score-and-rrf-fare",
46
+ "href": "posts/hybrid-search-retrieval-bm25-modernbert.html#how-does-the-hybrid-score-and-rrf-fare",
47
+ "title": "Hybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples",
48
+ "section": "How does the hybrid score and RRF fare?",
49
+ "text": "How does the hybrid score and RRF fare?\nAveraging out with a weighted hybrid score or RRF has no impact here. In example 1, record id 9 which is the closest match still does not rank highly in either of these scenarios.\nTo summarize the above for both approaches for the failure modes, it seems that embeddings seem to have the slight edge here.\n\n\n\nMethod\nNegation\nDirectionality\nAttribute/Identifier\n\n\n\n\nBM25\nN\nN\nN (hard to put a threshold)\n\n\nEmbedding\nSometimes\nN\nSometimes\n\n\n\nWe can’t eliminate all the issues, but we can hope to mitigate based on our understanding of the data."
50
+ },
51
+ {
52
+ "objectID": "posts/hybrid-search-retrieval-bm25-modernbert.html#but-what-did-we-want-to-achieve-with-bm25",
53
+ "href": "posts/hybrid-search-retrieval-bm25-modernbert.html#but-what-did-we-want-to-achieve-with-bm25",
54
+ "title": "Hybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples",
55
+ "section": "But what did we want to achieve with BM25?",
56
+ "text": "But what did we want to achieve with BM25?\nWe are really looking to filter out matches that do not have the right attributes/identifiers we seek. BM25 looks like it could provide that, but does more than attribute matching.\n\nExtract identifiers from query.\nPerform embedding based retrieval of query + docs to retrieve set M.\nFilter out docs where identifiers do not match in set M.\nUnfortunately, this does nothing to address the negation/directionality limitation."
57
  },
58
  {
59
  "objectID": "posts/index.html",
60
  "href": "posts/index.html",
61
  "title": "Posts",
62
  "section": "",
63
+ "text": "Hybrid Search Retrieval: Combining Semantic Search and BM25 Algorithm with Examples\n\n\nHow to combine ModernBERT embedding similarity with BM25, plus failure modes (negation and identifiers).\n\n\n\n\n\nMar 18, 2026\n\n\nRedis AI Research\n\n\n\n\n\n\n\nHello\n\n\nFirst Quarto post (template).\n\n\n\n\n\nMar 18, 2026\n\n\nRedis AI Research\n\n\n\n\n\nNo matching items"
64
  },
65
  {
66
  "objectID": "posts/hello.html",
sitemap.xml CHANGED
@@ -4,6 +4,10 @@
4
  <loc>https://srijithrajamohan-ai-research-redis.static.hf.space/index.html</loc>
5
  <lastmod>2026-03-18T23:10:24.034Z</lastmod>
6
  </url>
 
 
 
 
7
  <url>
8
  <loc>https://srijithrajamohan-ai-research-redis.static.hf.space/posts/index.html</loc>
9
  <lastmod>2026-03-18T23:10:26.244Z</lastmod>
 
4
  <loc>https://srijithrajamohan-ai-research-redis.static.hf.space/index.html</loc>
5
  <lastmod>2026-03-18T23:10:24.034Z</lastmod>
6
  </url>
7
+ <url>
8
+ <loc>https://srijithrajamohan-ai-research-redis.static.hf.space/posts/hybrid-search-retrieval-bm25-modernbert.html</loc>
9
+ <lastmod>2026-03-18T23:54:26.120Z</lastmod>
10
+ </url>
11
  <url>
12
  <loc>https://srijithrajamohan-ai-research-redis.static.hf.space/posts/index.html</loc>
13
  <lastmod>2026-03-18T23:10:26.244Z</lastmod>