@@ -5,29 +5,169 @@ highlighted features.
55
66---
77
8+ # v7.0
9+
10+ This important (and slightly breaking!) release adds support for ` null ` as a
11+ valid Cell and Value type, alongside ` string ` , ` number ` , and ` boolean ` .
12+
13+ ## Null Type Support
14+
15+ You can now set Cells and Values to ` null ` :
16+
17+ ``` js
18+ import {createStore } from ' tinybase' ;
19+
20+ const store = createStore ();
21+ store .setCell (' pets' , ' fido' , ' species' , ' dog' );
22+ store .setCell (' pets' , ' fido' , ' color' , null );
23+
24+ console .log (store .getCell (' pets' , ' fido' , ' color' ));
25+ // -> null
26+
27+ console .log (store .hasCell (' pets' , ' fido' , ' color' ));
28+ // -> true
29+ ```
30+
31+ To allow ` null ` values in your schema, use the new ` allowNull ` property:
32+
33+ ``` js
34+ store .setTablesSchema ({
35+ pets: {
36+ species: {type: ' string' },
37+ color: {type: ' string' , allowNull: true },
38+ },
39+ });
40+
41+ store .setCell (' pets' , ' fido' , ' color' , null );
42+ // Valid because allowNull is true
43+
44+ store .setCell (' pets' , ' fido' , ' species' , null );
45+ // Invalid - species does not allow null
46+
47+ store .delSchema ();
48+ ```
49+
50+ ## Important Distinction: ` null ` vs ` undefined `
51+
52+ It's crucial to understand the difference between ` null ` and ` undefined ` in
53+ TinyBase:
54+
55+ - ` null ` is an explicit value. A Cell set to ` null ` exists in the Store.
56+ - ` undefined ` means the Cell does not exist in the Store.
57+
58+ This means that the hasCell method will return ` true ` for a Cell with a ` null `
59+ value:
60+
61+ ``` js
62+ store .setCell (' pets' , ' fido' , ' color' , null );
63+ console .log (store .hasCell (' pets' , ' fido' , ' color' ));
64+ // -> true
65+
66+ store .delCell (' pets' , ' fido' , ' color' );
67+ console .log (store .hasCell (' pets' , ' fido' , ' color' ));
68+ // -> false
69+
70+ store .delTables ();
71+ ```
72+
73+ ## Breaking Change: Database Persistence
74+
75+ ** Important:** This release includes a breaking change for applications using
76+ database persisters (the Sqlite3Persister, PostgresPersister, or PglitePersister
77+ interfaces, for example).
78+
79+ SQL ` NULL ` values are now loaded as TinyBase ` null ` values. Previously, SQL
80+ ` NULL ` would result in Cells being absent from the Store. Now, SQL ` NULL ` maps
81+ directly to TinyBase ` null ` , which means:
82+
83+ - Tables loaded from SQL databases will be ** dense** rather than ** sparse**
84+ - Every Row will have every Cell Id present in the table schema
85+ - Cells that were SQL ` NULL ` will have the value ` null `
86+
87+ Example of the roundtrip transformation via a SQLite database:
88+
89+ ``` js
90+ import sqlite3InitModule from ' @sqlite.org/sqlite-wasm' ;
91+ import {createSqliteWasmPersister } from ' tinybase/persisters/persister-sqlite-wasm' ;
92+
93+ const sqlite3 = await sqlite3InitModule ();
94+ let db = new sqlite3.oo1.DB (' :memory:' , ' c' );
95+
96+ store .setTable (' pets' , {
97+ fido: {species: ' dog' },
98+ felix: {species: ' cat' , color: ' black' },
99+ });
100+
101+ const tabularPersister = createSqliteWasmPersister (store, sqlite3, db, {
102+ mode: ' tabular' ,
103+ tables: {save: {pets: ' pets' }, load: {pets: ' pets' }},
104+ });
105+
106+ await tabularPersister .save ();
107+ // After saving the the SQL database:
108+ // SQL table: fido (species: 'dog', color: NULL)
109+ // felix (species: 'cat', color: 'black')
110+
111+ await tabularPersister .load ();
112+ // After loading again, the Store now has a dense table with an explicit null:
113+
114+ console .log (store .getRow (' pets' , ' fido' ));
115+ // -> {species: 'dog', color: null}
116+ ```
117+
118+ This is the correct semantic mapping since SQL databases have fixed schemas
119+ where every row must account for every column. See the Database Persistence
120+ guide for more details.
121+
122+ ## Migration Guide
123+
124+ If you are using database persisters, you should:
125+
126+ 1 . ** Review your data access patterns** : If you were checking `hasCell(...) ===
127+ false` to detect missing data, you now need to check ` getCell(...) === null`
128+ for null values.
129+
130+ 2 . ** Update your schemas** : Add ` allowNull: true ` to Cell definitions that
131+ should permit null values:
132+
133+ ``` js yolo
134+ store .setTablesSchema ({
135+ pets: {
136+ species: {type: ' string' },
137+ color: {type: ' string' , allowNull: true },
138+ age: {type: ' number' , allowNull: true },
139+ },
140+ });
141+ ```
142+
143+ 3 . ** Consider memory implications** : Dense tables consume more memory than
144+ sparse tables. If you have large tables with many optional Cells, this could
145+ be significant.
146+
147+ ---
148+
8149# v6.7
9150
10151This release includes support for the Origin Private File System (OPFS) in a
11152browser. The createOpfsPersister function is the main entry point, and is
12153available in the existing persister-browser module:
13154
14155``` js
15- import {createStore } from ' tinybase' ;
16156import {createOpfsPersister } from ' tinybase/persisters/persister-browser' ;
17157
18158const opfs = await navigator .storage .getDirectory ();
19159const handle = await opfs .getFileHandle (' tinybase.json' , {create: true });
20160
21- const store = createStore ().setTables ({pets: {fido: {species: ' dog' }}});
22- const persister = createOpfsPersister (store, handle);
161+ store . delTables ().setTables ({pets: {fido: {species: ' dog' }}});
162+ const opfsPersister = createOpfsPersister (store, handle);
23163
24- await persister .save ();
164+ await opfsPersister .save ();
25165// Store JSON will be saved to the OPFS file.
26166
27- await persister .load ();
167+ await opfsPersister .load ();
28168// Store JSON will be loaded from the OPFS file.
29169
30- await persister .destroy ();
170+ await opfsPersister .destroy ();
31171```
32172
33173That's it! If you've used other TinyBase persisters, this API should be easy and
@@ -68,7 +208,7 @@ import {createMMKV} from 'react-native-mmkv';
68208import {createReactNativeMmkvPersister } from ' tinybase/persisters/persister-react-native-mmkv' ;
69209
70210const storage = createMMKV ();
71- const store = createStore () .setTables ({pets: {fido: {species: ' dog' }}});
211+ store .setTables ({pets: {fido: {species: ' dog' }}});
72212const persister = createReactNativeMmkvPersister (store, storage);
73213
74214await persister .save ();
@@ -1222,12 +1362,9 @@ and from a local SQLite database. It uses an explicit tabular one-to-one mapping
12221362for the 'pets' table:
12231363
12241364``` js
1225- import sqlite3InitModule from ' @sqlite.org/sqlite-wasm' ;
1226- import {createSqliteWasmPersister } from ' tinybase/persisters/persister-sqlite-wasm' ;
1227-
1228- const sqlite3 = await sqlite3InitModule ();
1229- const db = new sqlite3.oo1.DB (' :memory:' , ' c' );
12301365store .setTables ({pets: {fido: {species: ' dog' }}});
1366+
1367+ db = new sqlite3.oo1.DB (' :memory:' , ' c' );
12311368const sqlitePersister = createSqliteWasmPersister (store, sqlite3, db, {
12321369 mode: ' tabular' ,
12331370 tables: {load: {pets: ' pets' }, save: {pets: ' pets' }},
0 commit comments