
localStorage or JSON.parse / JSON.stringify to store data;Say you stored a book list using the front-end "old-school way":
[
{ "id": 1, "title": "Database Internals", "price": 59 },
{ "id": 2, "title": "Designing Data-Intensive Applications", "price": 79 },
{ "id": 3, "title": "A Philosophy of Software Design", "price": 39 }
]To find "books under 50," you'd write this:
const books = JSON.parse(await fetch("/books.json").then(r => r.text()));
const cheap = [];
for (const book of books) {
if (book.price < 50) cheap.push(book); // check each one
}You write that loop without a second thought. But it's already a miniature database — you just didn't call it that.
The first thing this series does is put that "old-school way" on the table, see its skeleton, and then turn it into a real database, piece by piece. Let's start with the humblest step.
Let's turn the book list into an array in code — that's our "table":
// "table": a pile of objects, each one a row
const books = [
{ id: 1, title: "Database Internals", price: 59 },
{ id: 2, title: "Designing Data-Intensive Applications", price: 79 },
{ id: 3, title: "A Philosophy of Software Design", price: 39 }
];Note three words that run through the whole series:
{ id: 1, ... });id, title, price);id is a number, title is a string, price is a number.Now, "find books under 50" is just writing a loop:
// our first "query"
function cheapBooks(rows) {
const result = [];
for (const row of rows) {
if (row.price < 50) result.push(row);
}
return result;
}
cheapBooks(books);
// [{ id: 1, ... }, { id: 3, ... }]With this little bit, you've built a "database." It does three things, exactly like a real one:
books array);cheapBooks function);if (row.price < 50)).That loop above is a sequential scan — from the top to the bottom, looking at every row one at a time. With 3 rows you feel nothing, but its cost hides in scale.
Let's zoom in on it with a little code. First, make 100,000 books:
// make 100,000 fake books, so we can see scale
const big = Array.from({ length: 100000 }, (_, i) => ({
id: i + 1,
title: "Book " + i,
price: (i * 7) % 200
}));Run the same query and time it:
const t0 = performance.now();
const result = cheapBooks(big);
const t1 = performance.now();
console.log(`Found ${result.length}, took ${(t1 - t0).toFixed(2)} ms`);The console shows something like:
Found 35000, took 3.2 ms3 milliseconds — sounds fast? That's because the comparison is cheap. But the point is this: it looked at every single row. Change the scale to 1 million, 10 million, and the loop count grows linearly — that's O(n). Double the data, double the time.
Now you see why "sequential scan" is called slow? Not because one step is slow, but because it guarantees it will look through every row. As long as you don't use an index, this is the only way — you just plod through everything.
Looking back, this "database" works. But you'll notice two things are off:
Problem 1: The query logic is hardcoded into the code. To find "under 50," you write a loop; to find "the one with id 2," you write another loop; to find "title is xxx," another. Each new condition is more code. That's not how a database works — you'd rather "tell it what you want" and let it figure out how.
Problem 2: It can only do a "full table scan." Whatever you ask, it never smartly jumps — it always scans from the top. As data grows, it gets slow.
These two problems are exactly what the rest of the series solves:
For now, just confirm you've built your first database yourself — an array plus a loop. That's already the foundation.
cheapBooks(big) from 100k to 1 million and watch the time. Then change the condition to row.id === 50000 and observe it still scans from the top.books for a real localStorage dataset you've used (like a todo list), and query it with the same sequential scan.cheapBooks without a for loop, using Array.prototype.filter / find, and think about whether they secretly do a sequential scan too.Keywords: row、column、sequential scan、O(n)、filter、declarative query
We've seen "how to query" — it's hardcoded and only does full table scans. Next, we lift "how to query" a level up: tell the database only what you want, and free "how to find" from the code. That's the real "database-style" query.
You'll see how a small change turns "condition" into "declaration," and how it paves the way for "making the lookup skip unnecessary rows."