Bases vs Dataview: Which One Should You Actually Use?
Short version: if you are starting new queries today, write them in Bases. If your vault runs on DataviewJS, stay where you are — that is a rewrite, not a migration. Everything in between is a judgement call, and the rest of this page is the evidence for making it.
What this verdict is based on
I built a test vault of 1,781 files, computed the correct answer for every query
independently in Python first, then ran it in Bases and compared. Three hours, 67
views, seven areas: filter syntax, tag and link matching, embedding, date filtering,
nested boolean logic, contains() semantics, and attachment handling.
What I did not do is run the same battery against Dataview in the same session. So where this page compares behaviour, it is comparing Bases-as-measured against Dataview-as-documented-and-as-I-have-used-it. The Bases numbers are measured. Treat the Dataview side as the weaker half of the comparison and verify anything that matters to you.
Where Bases is genuinely good
The filter engine is exact. This is the part I expected to find cracks in and
didn’t. I interleaved and / or / not to six levels of nesting, computed each
expected count separately, and all six matched. not wrapping a compound block
negates the entire block correctly.
Date filtering is precise to the minute. A “not modified in the last 7 days” filter returned 140 files; my independent count said 140. I then built a boundary probe — files aged between 8 and 6 days — and it returned exactly the 30 I had planted, with no timezone drift and no off-by-one at the edges.
It’s core, not a plugin. No install step, no breakage when a plugin author moves on, and it will still be there in two years. For a system you intend to depend on daily, this is not a small thing. It is arguably the single strongest argument in Bases’ favour and it has nothing to do with features.
The UI is a real editor. You can build a filter without touching YAML. Dataview has never had this, and for people who bounced off Dataview because of the query language, it is the difference between using the feature and not.
The four traps
These cost me real time, and none of them produce an error.
1. The result count lies when limit is set. The number Bases prints next to a
view is rows displayed, not rows matched. Three of my filters matched 146, 343 and
466 rows and all three displayed “25 results”. No truncation indicator. If you are
using the count to check your filter is right — and you should be — raise limit
above your expected total first.
2. file.* means every file, not every note. file.mtime, file.ext and the
rest apply to PDFs, images, and the .base files themselves. A filter meant to find
recently-modified notes returned 165 extra rows. Add file.ext == "md" unless you
genuinely want attachments.
3. contains() is three different functions. On text it is a case-insensitive
substring match. On a list it is whole-element equality, so fiction does not match
nonfiction. On a number it throws. The list behaviour fails safe, which is exactly
why it goes unnoticed until you carry the assumption over to a text property.
4. One badly-typed note poisons every view that touches that property. A single note with a string where a number belongs is enough to break a filter across the whole vault. The error names the operation, not the note, so you are hunting.
Three of these four are the same underlying problem: a wrong answer and a right answer look identical. Zero results in Bases could mean your syntax is wrong, your folder path is wrong, or nothing matches. That’s the tax you pay for a declarative system with no debugger, and it is the honest reason to check every query against a known number before you trust it.
When Dataview is still the right answer
You use DataviewJS. Bases is declarative YAML. There is no JavaScript execution surface, by design. If your vault runs on JS blocks, there is no translation — you would be re-specifying what you want from scratch, and possibly finding it can’t be expressed. Don’t start that unless you have a reason.
You need inline queries inside prose. Dataview’s inline syntax puts a computed value in the middle of a sentence. Bases produces views, not values.
You need FLATTEN, or grouped counts. GROUP BY itself exists in Bases 1.13.7 —
groupBy: with property: and direction: under a view, and it split my 800-book
table into status sections as expected. Two traps: spell it group_by and Bases
ignores it without a word, and in my test the group headers showed only the value, not
how many notes were in each group. FLATTEN I haven’t tested, so I won’t claim either way.
Your queries already work. Migration has a cost and a risk, and “it’s the newer one” isn’t a benefit. Dataview is not being removed from anything.
When Bases is the right answer
- New vault, or new queries in an existing vault. Start here. There is no reason to add a plugin dependency for work Bases does natively.
- You bounced off Dataview’s query language. The UI editor is a genuinely lower floor.
- You want the vault to keep working untouched for years. Core features get migrated across versions; plugins get abandoned.
- Filtering and sorting is the whole job. Which, for most people’s actual use, it is.
What I’d do
Leave Dataview installed. Write new things in Bases. Migrate an old query only when you were going to touch it anyway — and when you do, translate the broadest version first and check it against a count you already know, because the failure mode here is silence, not an error message.
I would not do a big-bang migration of a working vault. There is no prize for it.
Tested on the version in the box at the top of this page. The Bases figures are measured; the Dataview comparisons are not from this test round. Verify against your own install before relying on the specifics.