@monlite/core 0.2.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +87 -0
- package/dist/index.cjs +689 -74
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +219 -7
- package/dist/index.d.ts +219 -7
- package/dist/index.js +686 -75
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -250,10 +250,97 @@ const grouped = await users.groupBy({
|
|
|
250
250
|
orderBy: { _count: "desc" },
|
|
251
251
|
});
|
|
252
252
|
// [ { role: "admin", _count: 5, _sum: { age: 140 } }, … ]
|
|
253
|
+
|
|
254
|
+
// groupBy + having (filter groups by an aggregate, like SQL HAVING)
|
|
255
|
+
await users.groupBy({
|
|
256
|
+
by: ["role"],
|
|
257
|
+
_count: true,
|
|
258
|
+
_sum: { age: true },
|
|
259
|
+
having: {
|
|
260
|
+
_count: { gte: 2 }, // keep groups with COUNT(*) >= 2
|
|
261
|
+
_sum: { age: { gt: 50 } }, // and SUM(age) > 50
|
|
262
|
+
},
|
|
263
|
+
});
|
|
264
|
+
// having comparisons: equals, not, gt, gte, lt, lte — on _count and on
|
|
265
|
+
// _sum/_avg/_min/_max of any field.
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
### distinct
|
|
269
|
+
|
|
270
|
+
```ts
|
|
271
|
+
await users.distinct("role"); // ["admin", "editor"]
|
|
272
|
+
await users.distinct("age", { role: "admin" }); // [28, 31]
|
|
273
|
+
|
|
274
|
+
// Array fields are unwound — each element is a value (like MongoDB):
|
|
275
|
+
await users.distinct("tags"); // ["a", "b", "c"]
|
|
253
276
|
```
|
|
254
277
|
|
|
255
278
|
---
|
|
256
279
|
|
|
280
|
+
## Structured collections (the SQL skin)
|
|
281
|
+
|
|
282
|
+
By default a collection is **document mode** — schema-free, every field stored
|
|
283
|
+
as JSON. Pass a `schema` to make it a **structured collection**: the declared
|
|
284
|
+
fields become real, typed SQL columns (fast, indexable, joinable, constrainable)
|
|
285
|
+
and any *other* fields overflow into a JSON column. **The CRUD/query API is
|
|
286
|
+
identical** — `find`, `where`, `orderBy`, `groupBy`, `distinct`, updates — only
|
|
287
|
+
the storage underneath changes.
|
|
288
|
+
|
|
289
|
+
```ts
|
|
290
|
+
const orders = db.collection("orders", {
|
|
291
|
+
schema: {
|
|
292
|
+
user_id: { type: "TEXT", index: true, references: "users(_id)" },
|
|
293
|
+
amount: "REAL",
|
|
294
|
+
status: { type: "TEXT", notNull: true, default: "pending" },
|
|
295
|
+
meta: "JSON", // objects/arrays, transparently (de)serialized
|
|
296
|
+
},
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
// Same API as document collections — but `amount`/`status` are real columns:
|
|
300
|
+
await orders.create({ data: { user_id: "u1", amount: 100, status: "paid", note: "rush" } });
|
|
301
|
+
await orders.findMany({ where: { amount: { gte: 50 }, status: "paid" } });
|
|
302
|
+
await orders.groupBy({ by: ["status"], _sum: { amount: true } });
|
|
303
|
+
|
|
304
|
+
// Undeclared fields (like `note`) still work — they overflow into JSON.
|
|
305
|
+
await orders.findMany({ where: { note: { contains: "rush" } } });
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
Because the columns are native, they join, constrain, and index like any SQL
|
|
309
|
+
table — including from the raw SQL hatch with no `json_extract`:
|
|
310
|
+
|
|
311
|
+
```ts
|
|
312
|
+
await db.$queryRaw`
|
|
313
|
+
SELECT u.name, SUM(o.amount) AS revenue
|
|
314
|
+
FROM users u JOIN orders o ON o.user_id = u._id
|
|
315
|
+
GROUP BY u._id
|
|
316
|
+
`;
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
Column types: `"TEXT" | "INTEGER" | "REAL" | "BLOB" | "JSON"`. A full column
|
|
320
|
+
definition supports `index`, `unique`, `notNull`, `default`, and `references`.
|
|
321
|
+
|
|
322
|
+
### Do I have to care: JSON vs native columns?
|
|
323
|
+
|
|
324
|
+
- **For correctness — no.** Both modes return identical results through the same API.
|
|
325
|
+
- **For performance & SQL interop — a little.** Native columns + native indexes are
|
|
326
|
+
faster and join/constrain cleanly; JSON is for when the shape is unknown or varies.
|
|
327
|
+
|
|
328
|
+
monlite never hides which is which:
|
|
329
|
+
|
|
330
|
+
```ts
|
|
331
|
+
orders.mode; // "structured" | "document"
|
|
332
|
+
await db.$schema("orders"); // physical columns: [{ name, type, notNull, primaryKey }, …]
|
|
333
|
+
createDb("./app.db", { verbose: (sql) => console.log(sql) }); // see json_extract vs bare columns
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
> **Rule of thumb:** unknown/flexible shape → document (JSON); known/stable shape
|
|
337
|
+
> with heavy joins, reporting, or external SQL tooling → structured (native columns).
|
|
338
|
+
|
|
339
|
+
> Note: structured collections are not yet covered by `@monlite/sync` (document
|
|
340
|
+
> collections are) — that's planned follow-up work.
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
257
344
|
## SQL escape hatch
|
|
258
345
|
|
|
259
346
|
When you need full SQL power — complex joins, analytics, cross-collection
|