Manticore Search in MediaWiki Pt. 2
As of the time of writing, it's been 23 days since my first post on the ManticoreSearch extension, which you should read first. About a day after, I stopped working on the extension, instead switching my effort over to the counterpart extension, the one that would implement the UI I talked about in that same post. Right now, that's being called ProgressiveSearch (because it focuses on progressive disclosure, creative name, I know). That post left a lot of open questions, and made some pretty bold statements, too. While those were true, for heavier and more realistic workloads, I have found significant drawbacks with later benchmarks after testing real large corpora.
But, it's not all bad news. It mostly anchors this extension down as being for smaller wikis, which was its original purposed. Personally, I'd hoped it could grow into a good fit for reliable cross-wiki searches a well. That's not impossible, just more difficult than I would of hoped. Additionally, I'll be sharing more about the project's ecosystem and the new search UI I've been working on.
The first part of this benchmark is mostly technical, so if you're looking for straightforward features and improvements, skip down towards the 'Search' section instead.
Benchmark
This is the largest benchmark on the extension so far, aiming to test multilingual support and verify findings with a larger and unique dataset. For this, it uses six different Wikipedia samples totalling 60,005 unique pages and a 5.6 GiB index. This is real data, as opposed to the duplicated data that the old test ran (709 real articles copied over and over again). It is worth noting that the new pages were also much larger in size as well (both words and bytes). It also measured the actual implemented version of the cross-wiki service instead of the earlier prototype. The results were workable, but much more modest.
Initially this benchmark was run poorly at its first attempt. Then, it was re-run again. Sometimes the difference we be referred to as the 'first run' and the 'follow-up'. They roughly cover the same things, but the follow-up was executed better. Not much data was left in from the first.
Questions left by the earlier benchmarks
| Earlier question or result | Finding here |
|---|---|
| Measure representatively different corpora | Done: six languages, each sample has 10,000 articles |
| Measure origin validation through the real service | Done: it adds little compared with the searches |
| Measure sustained edits/rebuilds | Partly: ten minutes of paced edits raised read failures (hours-long load was not tested) |
| Twenty copies, 15,800 documents, fit in 256 MiB | Does not carry over: the first rebuild was killed at about 13,400 real documents |
| 512 MiB as a starting budget at 7,900 documents | Does not carry over: 512 MiB was killed at about 36,700 and is barely usable once loaded |
| Service p95 of 31 ms at two wikis, 4 clients | Now 1,575 ms on two real wikis (different queries, see below) |
| No evidence for capacity beyond twenty copies | Still true for the wiki count, this run has six wikis with more documents |
The problem with the earlier test
'Test 2' from my previous post was a relatively easy load. The mistake was partly using duplicated documents, as well as far smaller documents. This corpora was 60,000 unique pages split across 6 different wikis and languages. The average size of the original test's docs were around 8.5 KB, while this benchmark corpora docs were around 100 KB on average (177 KB on English). These findings likely will not apply to us, but will on any wiki of a large scale.
While the duplication did play a role, the main factor was actually the size of the pages. So, as a smaller test, two fixtures with the same schema, 9,996 documents and a 1 GiB cap where tested, with six tables of English articles, and six copies of the first 1,666 documents. Timings use the search layer, across six tables, with 200 requests per row.
| Measure | Unique | Copied |
|---|---|---|
| Table disk | 1,759 MiB | 1,695 MiB |
| Load time | 116.5s | 103.5s |
| Sampled anonymous memory peak | 475 MiB | 468 MiB |
| 1 client median (p95) | 166 (1,442ms) | 163 (1,451ms) |
| 4 clients median (p95) | 424 (1,752ms) | 448 (1,762ms) |
| 4 clients failed | 24/200 | 25/200 |
Distinct titles in top 20: science | 20 | 6 |
"Anarchism" | 20 | 4 |
insource:science | 20 | 12 |
history of art | 20 | 4 |
water | 20 | 12 |
Manticore does not share data between tables, so copies cost as much as unique documents. They do score identically, so merged ranking reduces to the wiki-ID tie-break and repeats the same pages.
Environment and workload
- MediaWiki 1.46, PHP 8.3.36, MariaDB 11.8.9 and Manticore 29.9.0 in an isolated mwenv stack (manticoresearch-2216bb93), on a Ryzen 7 5700X3D under WSL2.
- Two backend CPUs with no extra swap. The real backend has a 1 GiB cap, while other caps ran on disposable daemons using a consistent (FREEZE) copy of its volume.
- Latest version of the extension
- Six isolated wikis with their own databases, tables, and content language. English, German, and Arabic used stemming, while orean, Japanese and Thai used the existing ngrams.
Latency cells rotate a common word in each language, insource:, a quoted title, and an absent term, with half at offset 0 and half at offset 100, with limit 20. Each request's language rotates through the wikis searched. Subsets are English, then German, then Korean, then all six. Each cell has a warmup, then 200 measured requests.
The earlier benchmark ran 200 requests per client, this one runs 200 per cell, for 10,200 matrix requests against its 22,665. Cells ran in a fixed order, once each. Failed requests were excluded from latency, which understates the tail near the deadline.
Cross-wiki latency and failures
Measured through the real API with origin validation, HTTP, and fresh PHP startup, with 1 GiB of memory.
"Local" rotates single-wiki searches through all six wikis.
| Scope | Documents | Clients | Median ms | p95 ms | Requests/s | Failed |
|---|---|---|---|---|---|---|
| 1 wiki | 10,001 | 1 | 71.5 | 344.7 | 6.72 | 0 |
| 1 wiki | 10,001 | 4 | 125.5 | 463.9 | 17.54 | 0 |
| 1 wiki | 10,001 | 8 | 180.9 | 1,058.2 | 16.15 | 0 |
| 2 wikis | 20,002 | 1 | 181.8 | 1,087.6 | 3.05 | 0 |
| 2 wikis | 20,002 | 4 | 295.9 | 1,485.0 | 7.12 | 0 |
| 2 wikis | 20,002 | 8 | 328.0 | 1,457.3 | 6.63 | 41 |
| 3 wikis | 30,003 | 1 | 192.0 | 1,357.7 | 2.76 | 0 |
| 3 wikis | 30,003 | 4 | 232.3 | 1,580.6 | 7.98 | 0 |
| 3 wikis | 30,003 | 8 | 333.4 | 1,261.6 | 8.01 | 29 |
| 6 wikis | 60,005 | 1 | 436.3 | 1,227.9 | 1.54 | 11 |
| 6 wikis | 60,005 | 4 | 860.5 | 1,843.6 | 3.21 | 19 |
| 6 wikis | 60,005 | 8 | 937.8 | 1,867.6 | 3.90 | 74 |
| Local | 10,000 each | 1 | 108.1 | 438.5 | 6.11 | 0 |
| Local | 10,000 each | 4 | 155.2 | 635.3 | 15.78 | 0 |
| Local | 10,000 each | 8 | 189.1 | 1,235.1 | 14.57 | 0 |
All 174 cross-wiki failures took 1.91-2.07s and returned searchbackend, matching the shared deadline in the CrossWikiSearch service. Of them, 142 were at offset 100, including every failure in the two-wiki and three-wiki cells. The service searches wikis one after another, and each fetches and highlights offset + limit rows, up to 120 per wiki here. Twelve further requests returned flagged partial results (nine cross-wiki, three local).
The wikis tables were not equal in size; two wikis are the two largest tables, and the third was a small one, so three roughly matches two. With a corrected baseline, no response had a wrong total. The first baseline read was low for two Thai queries and was not checked for partial results, re-reading it gave the values every later response returned.
Where was the time spent
Six wikis at 1 GiB and 200 requests per cell. The search layer is the implemented fan-out and merge without validation strategy that the extension uses. The service layer adds origin validation in one PHP process. "Single statement" is the earlier prototype's multi-table query.
| Layer | 1 client median / p95 / failed | 4 clients median / p95 / failed | 8 clients median / p95 / failed |
|---|---|---|---|
| Search | 467 / 1,219 / 9 | 871 / 1,713 / 18 | 1,104 / 1,838 / 78 |
| Service | 574 / 1,706 / 21 | 1,018 / 1,805 / 32 | 982 / 1,804 / 75 |
| HTTP API | 436 / 1,228 / 11 | 861 / 1,844 / 19 | 938 / 1,868 / 74 |
| Single statement | 171 / 519 / 0 | 288 / 1,131 / 0 | 183 / 450 / 0 |
Most of the time spent is search, not validation or HTTP. The first three layers overlap, and HTTP even beats the service layer here, so their gaps are within this run's noise. The single statement looks much cheaper, and is worth exploring further, but does not seem to be an equivalent or a drop-in fix. Page IDs collide across these tables, so its results and totals would be wrong. It also has no two-second deadline, highlights only 20 rows, and it ran last, on a warmer cache. It may only suggest that the per-wiki window and highlighting deserve investigation.
The earlier two-wiki service figures were 4.24 / 23.83ms at one client and 4.31 / 30.86ms at four. On two real wikis they are 166 / 1,083ms and 298 / 1,575ms. These rows include offset 100 and different queries, so the ratio is only indicative.
Merge quality
Fifty randomly sampled titles per wiki were searched locally and across all six, with the expected page's rank recorded. This also corrects the early-page sample below. Seven English cross-wiki requests failed and were excluded.
| Wiki | Plain top 1, local -> cross | Plain top 3, local -> cross | Quoted top 1, local -> cross | Quoted top 3, local -> cross |
|---|---|---|---|---|
| English | 43 -> 40 of 44 | 44 -> 44 of 44 | 48 -> 46 of 49 | 49 -> 49 of 49 |
| German | 49 -> 41 of 50 | 49 -> 47 | 49 -> 40 of 50 | 49 -> 47 |
| Korean | 43 -> 43 of 50 | 48 -> 48 | 46 -> 46 of 50 | 49 -> 49 |
| Japanese | 47 -> 47 of 50 | 50 -> 50 | 48 -> 48 of 50 | 50 -> 50 |
| Arabic | 46 -> 35 of 50 | 50 -> 47 | 48 -> 38 of 50 | 50 -> 47 |
| Thai | 46 -> 45 of 50 | 48 -> 47 | 48 -> 48 of 50 | 49 -> 49 |
| All | 274 -> 251 of 294 | 289 -> 283 | 287 -> 266 of 299 | 296 -> 291 |
This wasn't particularly interesting to me, but it was worth including.
Merging rarely pushes a known page out of the top three. It often loses first place on German and Arabic, because other wikis hold equally exact titles, including years such as 1454. That's a normal result for an exact match and isn't a ranking failure.
Reads during edits
Four six-wiki readers and one local reader ran through the real API. The writer edited disposable pages one at a time across the six wikis, then it polled until each edit was searchable. Targeting 1 edit/s, waiting for visibility held it to about 0.6 edits/s.
| Run | Cross failed | Cross median / p95 ms | Local failed | Edits | Edit to searchable, median / p95 ms |
|---|---|---|---|---|---|
| Readers only, 300 s | 162/1,280 (12.7%) | 716 / 1,743 | 5/775 | 0 | n/a |
| With edits, 600 s | 749/1,576 (47.5%) | 1,114 / 1,927 | 48/757 | 369 | 1,676 / 2,124 |
| With edits (interrupted) | 100/342 (29.2%) | 1,081 / 1,909 | 6/198 | 88 | 1,339 / 1,964 |
The third run was interrupted because the host slept (benchmarking took a while and it was late at night, so I accidentally Windows sleep). Only about 120s of its 300s window was observed, so it's only useful as a second rate. Every edit succeeded (median API time about 40 ms) and became searchable. Edits clearly raise read failures, but the mechanism and the size of the effect are not established. The two edit runs differ, the order was fixed, and the writer's polling adds a small read load. Totals are not asserted here because the fixture text intentionally matches the workload's words.
Memory
Each row is a fresh daemon on a copy of the full index, under the search layer with 200 requests per client count. These measurements are process-cold, not OS-cold.
| Cap | Outcome | 1 client median / p95 / failed | 4 clients median / p95 / failed | Absent-term median, 1 client | Limit events | Anonymous after search |
|---|---|---|---|---|---|---|
| 256 MiB | OOM-killed at startup | — | — | — | — | — |
| 512 MiB | Ran, degraded | 1,023 / 1,563 / 17 | 1,783 / 1,965 / 175 | 823 ms | 2,035,652 | 442 MiB |
| 1 GiB | Ran | 295 / 949 / 10 | 713 / 1,782 / 21 | 182 ms | 315,017 | 444 MiB |
| 2 GiB | Ran | 98 / 827 / 3 | 376 / 956 / 7 | 19 ms | 0 | 429 MiB |
| 6 GiB | Ran | 97 / 831 / 2 | 358 / 1,005 / 7 | 19 ms | 0 | 435 MiB |
Anonymous memory is already about 392 MiB right after startup, so 256 MiB cannot hold this corpus whether or not allocator memory is trimmed. Above that, the cap decides how much index stays in page cache. An absent term should do almost no work, yet it takes ten times longer at 1 GiB than at 2 GiB.
Usage peaked at 1,779 and 1,712 MiB in the two largest caps, with no limit events, so extra memory stops helping there. Every failure left at those caps was at offset 100.
Writes alongside readers
This repeats the earlier 100 paced replacements at 20/s, against the copied index at 1 GiB, with four cross-wiki readers and one local reader for six seconds. Reads are small samples.
| Tables | Cross-wiki p95 ms | Cross failed | Local p95 ms | Write median / p95 / max ms |
|---|---|---|---|---|
| 1 | 708 | 0/89 | 754 | 2.7 / 500 / 643 |
| 2 | 1,331 | 8/28 | 892 | 3.2 / 596 / 970 |
| 3 | 1,574 | 4/36 | 605 | 3.3 / 523 / 816 |
| 6 | 1,663 | 2/29 | 790 | 2.7 / 414 / 762 |
All 1,600 replacements across the 512 MiB to 6 GiB runs succeeded. Medians are at or below the earlier benchmark's, but p95 is usually much higher, at 344-722 ms across all these caps. The cause was not established.
Trimming is inconclusive
The earlier benchmark's twenty-table run relied on trimming allocator memory between tables. Testing that here failed, as an unpaced 256 MiB load was killed in its first table at 1,975 documents without trimming and 1,275 with it, before any trim ran. Direct inserts are much faster than a normal rebuild, which indexed 10,000 of the same Thai documents at that cap.
This says relatively nothing about trimming. The startup result above makes a paced retest unnecessary for this corpus.
Single-wiki retrieval and completion
These results come from the first run, with corrections from the follow-up:
| Language | Plain title top 3 | Quoted title top 3 | Native-word median / p95 ms |
|---|---|---|---|
| English | 29/30 | 29/30 | 62.1 / 70.1 |
| German | 30/30 | 30/30 | 197.4 / 231.9 |
| Korean | 22/30 | 24/30 | 60.0 / 70.7 |
| Japanese | 23/30 | 23/30 | 76.7 / 86.5 |
| Arabic | 27/30 | 28/30 | 88.5 / 93.2 |
| Thai | 26/30 | 26/30 | 215.2 / 245.4 |
I'm not going to include notes of the differences between the first run and the follow-up, as those are mostly useful for me. As a quick summary, the first run saw more misses than the follow-up did, meaning it was overstated. That was not published, and part of the original reason why I chose to run a follow-up of this benchmark.
Typo completion, with an adjacent swap, using 30 early and 50 random titles and the same exclusion. The worker skips origin validation and runs a warm pass first:
| Wiki | 50 ms (default) | 250 ms | 1s | 5s | Median / p95 ms at 5s |
|---|---|---|---|---|---|
| English | 0/59 | 7/59 | 55/59 | 55/59 | 484 / 2,716 |
| German | 0/42 | 7/42 | 40/42 | 41/42 | 362 / 790 |
| Korean | 22/29 | 22/29 | 22/29 | 22/29 | 7 / 8 |
| Japanese | 42/47 | 44/47 | 44/47 | 44/47 | 9 / 13 |
| Arabic | 0/51 | 41/51 | 44/51 | 44/51 | 140 / 248 |
| Thai | 33/36 | 35/36 | 35/36 | 35/36 | 17 / 60 |
When the helper misses its budget, completion silently falls back to exact prefix, which cannot find a typo. Raising the budget restores the results but costs hundreds of milliseconds per keystroke on English and German, so a larger budget alone is not a good fix. Korean does not improve with budget, which points to a different cause.
The first completion attempt stopped on one uncaught error from the exact-prefix fallback (kho-i, "Incomplete completion candidate query"). The retry had no errors, so this looks like a cold-read issue, but it was not investigated. Worth noting for the future.
Bulk loading and recovery
Rebuilds ran one wiki after another, and each cap restarted the daemon with the earlier indexes retained. Times aren't isolated, as imports were running alongside some of them.
| Cap | Outcome | Evidence |
|---|---|---|
| 256 MiB | OOM-killed during the second wiki, at about 13,400 documents | Thai 10,000 in 332s; Korean stopped at 3,404 |
| 512 MiB | OOM-killed during the fourth wiki, at about 36,700 documents | Korean 417s, Arabic 560s; Japanese stopped at 6,709 |
| 1 GiB | All six completed | Japanese 858s, German 875s, English 1,426s |
single-process imports hit PHP's 512 MiB limit after several thousand pages in every language. Imports were resumed in batches of 250-1,000 pages at the same limit. These were PHP failures, not Manticore failures.
Corpus
Each sample has 10,000 non-redirect, main-namespace articles plus the redirects met before that cutoff, from Wikimedia's mediawiki_content_current export of 2026-10-01. They are early-page prefixes, not random samples or matched translations.
| Wiki | Articles | Redirects | Retained XML bytes | Table disk bytes | Disk chunks |
|---|---|---|---|---|---|
| English | 10,000 | 3,217 | 584,859,504 | 1,767,081,532 | 32 |
| German | 10,000 | 1,436 | 371,782,227 | 1,378,545,318 | 32 |
| Korean | 10,000 | 1,497 | 151,851,862 | 441,903,597 | 20 |
| Japanese | 10,000 | 2,341 | 368,604,504 | 1,063,732,392 | 32 |
| Arabic | 10,000 | 6,014 | 292,697,946 | 895,871,106 | 32 |
| Thai | 10,000 | 6,124 | 207,114,455 | 482,661,625 | 24 |
Templates, Lua modules, Cite, images, revision history and Cargo data were not imported. Rendered text therefore differs from Wikipedia, and core cache and category-count jobs stayed paused.
This tests the extension with real article text, but it's not a faithful Wikipedia rendering. Indexes weren't optimized, and the effect of that many disk chunks is untested. Retained data is about 14.6 GiB, excluding Docker images, and WSL overhead.
Analysis
That's it, right? Some roughly disappointing benchmarks, but reasonable given the larger dataset. Well, yes, I was actually hoping it would magically be the most fast and stable thing in the world, but I see now that that's unrealistic.
Those benchmarks aren't conclusive though. After looking further I can see that most of the negatives come from the extension and the setup, mostly. Although memory is still from Manticore's side, but I'd argue it's still more efficient than ElasticSearch.
I found that it's improvable. Most of the avoidable cost is highlighting rows that are then discarded, as the current implementation (that was benchmarked) does. Each wiki highlights offset + limit rows before the merge, so a request can highlight up to 720 rows just to show 20. Highlighting only the merged page removed every failure at a 2 GiB cap, and took failures at 1 GiB from 102 to 20 of 768.
Six wikis, 256 requests per cell. Each cell shows failed requests, then median / p95 ms.
| Cap | Clients | As implemented | Merged page only | No highlighting |
|---|---|---|---|---|
| 2 GiB | 1 | 6, 81 / 845 | 0, 51 / 225 | 0, 25 / 189 |
| 2 GiB | 4 | 5, 326 / 946 | 0, 137 / 496 | 0, 78 / 427 |
| 2 GiB | 8 | 26, 378 / 1,685 | 0, 193 / 952 | 0, 123 / 430 |
| 1 GiB | 1 | 12, 230 / 1,172 | 0, 173 / 468 | 0, 181 / 506 |
| 1 GiB | 4 | 9, 605 / 1,512 | 0, 360 / 829 | 0, 302 / 705 |
| 1 GiB | 8 | 81, 681 / 1,874 | 20, 661 / 1,723 | 9, 468 / 1,516 |
| Total failed | 37 and 102 of 768 | 0 and 20 of 768 | 0 and 9 of 768 |
In the total row, the first number is 2 GiB and the second is 1 GiB. Failed requests are excluded from latency, as in the earlier benchmark, so the "as implemented" column understates its own tail. erged-page-only is slightly faster than no highlighting at 1 GiB with one client (173ms/181ms), which gives a sense of this run's noise.
There's some worthwhile conclusions from this; for one, broad terms gained the most. At 2 GiB with one client, the median fell from about 339ms down to 28ms without highlighting, and to 65ms with the merged page only.
Improvements to be made
I won't share too much about improvements that need to be made, rather than this short list of points:
- Only the merged page should have highlights (strongest)
- Validation should only run as far as the page needs (batched?)
- Potentially limit the per-wiki window or cross-wiki offset, or replace them with a cursor
There's a few separate things that need to be taken care of too, but I think with these and a bit more poking that the next benchmark will have far greater results. But, I'd rather base it around the actual, which will be in the form of the new search UI and features. There's a lot more findings on the backend but I think it's fine to leave it there for now. I'm going to first focus on making the single-wiki experience as fast and good as possible, and the cross-wiki search as an optional goal once all that's sorted. It feels like working backwards otherwise, really.
Search
Interestingly enough, the rest of the plan is looking quite positive. In terms of features, the two sister extensions have done a lot. I didn't showcase this in the previous blog post, but if you don't know, ManticoreSearch is the backend which carries out the searching and integrates Manticore with MediaWiki. It's responsible for all the documents, housekeeping, etc. Next in line, comes the UI extension: ProgressiveSearch. This is the extension that you'll actually see, as it replaces the default MediaWiki Special:Search page (subject to change*) with an entirely new interface.
ProgressiveSearch
It borrows some inspiration from AdvancedSearch and SearchVue, but is mostly its own thing. The extension focuses on progressive disclosure (where it gets its name), meaning that the UI is as complex as you want it to be. It starts off simple, just a usual search view with a few visual improvements such as cards with article thumbnails, facets, an easy sort dropdown, among other things. However, there are two buttons hiding in plain sight which will expand the functionality of search. In three parts:
- Results, facets, scope for articles/media/all, and sorts
- Filters, wiki scope (local vs. cross-wiki search)
- Advanced search
Let me elaborate. The UI is as complex as you need it. If you don't need filters, you can quickly scan for a result without anything taking up space. If you do need filters that aren't presented as chips/facets, then you can click the easy-to-access filters button. If you need a finely tuned search, click the Advanced button. You can also just use syntax in the search box, if you know it.
Unfortunately, while working functionality-wise, the UI is in such an early stage I don't wish to share much of it. Here's a screenshot of the main view after searching 'Snowflake Stage Tower' with a local copy of the Obby Wiki's database (early October):
Sorry for the poor cropping! But yeah, you can see some placeholders, such as near the Media and Everything tabs, where they say '0'. Arctic Tower's article thumbnail was not imported, so it falls back. The UI still needs a lot of polish, especially the article quick actions row.
This UI was configured for the Obby Wiki, so it'd need work before it looks like this on any other wiki. On the bright side, nothing is hardcoded for the Obby Wiki and the extension is completely configurable!
Still, a massive beta. Lots of work to be done.
CirrusSearch
Speaking of work, how much does it work like Cirrus? The answer is still... not much. Although, honestly, most of the completely unimplemented features are keyword/operators. I may mark a lot of these as not planned, such as neartitle: or nearcord: from GeoData. Although, in some places, ManticoreSearch does come out on top with its own operators, such as pagesize: and wordcount:, namespace:, or inbody: and inredirect:.
Actually, I'll share this entire table from the internal document:
| Keyword | CirrusSearch | ManticoreSearch | |
|---|---|---|---|
intitle: | Title and redirects + accepts regex | Canonical title only (redirects excluded) + accepts a bounded regex (see below) | 🟡 |
inbody:, inredirect: | — | Rendered body text or indexed direct redirect aliases + phrases, OR, negation, wildcards and proximity | ➕ |
namespace: | — | Positive values replace selected scope and select any listed namespace + exclusions still apply, and an unknown name is a query error + local-only | ➕ |
incategory: | Direct membership + A|B for OR; id: form | Exact direct membership + repeat to AND + no OR (a pipe is part of the name) | 🟡 |
deepcat: / deepcategory: | Subcategory tree (needs SPARQL) | No | ❌ |
hastemplate: | Yes | Yes, from templatelinks, including nested and non-Template namespaces | ✅ |
insource: | Wikitext words and phrases | Wikitext words and phrases, unstemmed and case-insensitive | ✅ |
insource:/regex/ | Yes (see below) | Yes, bounded (see below) | 🟡 |
morelike: | Several titles separated by | | One title + must lead the query and stand alone | 🟡 |
morelikethis: (combinable) | Yes | One positive local source + combines with text and filters before pagination + normal ranking and 10,000-result window + no category reranking | 🟡 |
linksto: | Yes | Exact recorded local targets, including red links, repeat to AND + negation supported | ✅ |
prefix: | Yes | No | ❌ |
subpageof: | Yes | No | ❌ |
pageid: | Yes (IDs separated by |) | Same, repeat to AND, negation supported + an invalid value is ignored with a warning | ✅ |
contentmodel: | Yes | Exact main-slot model ID, repeat to AND, negation supported, but local-only. Older pages match after a rebuild or edit | ✅ |
inlanguage: | Yes | No | ❌ |
local: | Yes | No | ❌ |
prefer-recent: | Yes | No | ❌ |
lasteditdate:, creationdate: | Calendar and relative date filters | >, >=, <, <=, exact periods (YYYY, YYYY-MM, YYYY-MM-DD), now/today with y/m/d/h offsets, wiki timezone, negation. Creation is the earliest revision still in the page history. A malformed date is a query error + pages indexed before the upgrade match only after a rebuild or edit | ✅ |
boost-templates: | Yes | No | ❌ |
filetype:, filemime:, filesize:, filew:, fileh:, fileres:, filebits: | Yes | No | ❌ |
pagesize:, wordcount: | No | Revision bytes and rendered-text words: >, >=, <, <=, =, bare number for "at least", min,max range, negation; k/m suffixes on pagesize:. Pages indexed before the upgrade match only after a rebuild or edit | ➕ |
neartitle:, nearcoord:, boost-near*: | Yes (needs GeoData) | No | ❌ |
articletopic:, hasrecommendation: | Yes (needs weighted tags) | No | ❌ |
haswbstatement:, wbstatementquantity:, inlabel: and similar | Yes (needs WikibaseCirrusSearch) | No | ❌ |
Negated keywords (-incategory:X) | Yes | Yes for every supported keyword except morelike: and morelikethis: + negated regex is a query error | 🟡 |
| Mark | Meaning |
|---|---|
| ✅ | Equivalent for practical purposes |
| 🟡 | Present, but narrower or behaves differently |
| ❌ | Not implemented |
| ➕ | ManticoreSearch does something Cirrus does not |
Structured search
There haven't been many improvements. It still works at a steady pace. There's also support for facets now. As a quick refresher:
- Exact field filters:
data:Dataset.field=valueon configured public Cargo 3.9 fields;>,>=,<,<=on integers and floats - Grouped conditions:
data:Dataset(a=1 | b=2)with AND, OR, nesting, and per-field exclusion - Text search inside a field:
indata:words and phrases on opted-in string fields, with their own weight and snippet - Short aliases: Configurable, for example
year:>=2025
I think that's all to note for now.
