>)}
URL:
> );
@@ -223,7 +225,7 @@ function SheetJSQLWriter() {
```
-## SQL
+## Databases
### SQLite
@@ -349,6 +351,9 @@ XLSX.writeFile(wb, "bun.xlsx");
This information is included for legacy deployments. Web SQL is deprecated.
has up-to-date info on browser support.
+[Firefox](https://nolanlawson.com/2014/04/26/web-sql-database-in-memoriam/)
+never supported Web SQL. Safari 13 dropped support. As of the time of writing,
+the current Chrome version (103) supports WebSQL.
::::
@@ -358,37 +363,263 @@ work as-is in WebSQL.
The public demo generates a database from workbook.
+Importing data from spreadsheets is straightforward using the `generate_sql`
+helper function from ["Building Schemas"](#building-schemas-from-worksheets):
-## Objects, K/V and "Schema-less" Databases
+```js
+const db = openDatabase('sheetql', '1.0', 'SheetJS WebSQL Test', 2097152);
+const stmts = generate_sql(ws, wsname);
+// NOTE: tx.executeSql and db.transaction use callbacks. This wraps in Promises
+for(var i = 0; i < stmts.length; ++i) await new Promise((res, rej) => {
+ db.transaction(tx =>
+ tx.executeSql(stmts[i], [],
+ (tx, data) => res(data), // if the query is successful, return the data
+ (tx, err) => rej(err) // if the query fails, reject with the error
+ ));
+});
+```
-So-called "Schema-less" databases allow for arbitrary keys and values within the
-entries in the database. K/V stores and Objects add additional restrictions.
+The result of a SQL SELECT statement is a `SQLResultSet`. The `rows` property
+is a `SQLResultSetRowList`. It is an "array-like" structure that has `length`
+and properies like `0`, `1`, etc. However, this is not a real Array object.
+A real Array can be created using `Array.from`:
-There is no natural way to translate arbitrarily shaped schemas to worksheets
-in a workbook. One common trick is to dedicate one worksheet to holding named
-keys. For example, considering the JS object:
+```js
+const db = openDatabase('sheetql', '1.0', 'SheetJS WebSQL Test', 2097152);
+db.readTransaction(tx =>
+ tx.executeSQL("SELECT * FROM DatabaseTable", [], (tx, data) => {
+ // data.rows is "array-like", so `Array.from` can make it a real array
+ const aoo = Array.from(data.rows);
+ const ws = XLSX.utils.json_to_sheet(aoo);
+ // ... it is recommended to perform an export here OR wrap in a Promise
+ })
+);
+```
-```json
-{
- "title": "SheetDB",
- "metadata": {
- "author": "SheetJS",
- "code": 7262
- },
- "data": [
- { "Name": "Barack Obama", "Index": 44 },
- { "Name": "Donald Trump", "Index": 45 },
- ]
+The following demo generates a database with hardcoded SQL statements. Queries
+can be changed in the Live Editor. The WebSQL database can be inspected in the
+"WebSQL" section of the "Application" Tab of Developer Tools:
+
+![WebSQL DevTools](pathname:///files/websql.png)
+
+```jsx live
+function SheetQL() {
+ const [out, setOut] = React.useState("");
+ const queries = [
+ 'DROP TABLE IF EXISTS Presidents',
+ 'CREATE TABLE Presidents (Name TEXT, Idx REAL)',
+ 'INSERT INTO Presidents (Name, Idx) VALUES ("Barack Obama", 44)',
+ 'INSERT INTO Presidents (Name, Idx) VALUES ("Donald Trump", 45)',
+ 'INSERT INTO Presidents (Name, Idx) VALUES ("Joseph Biden", 46)'
+ ];
+ const xport = React.useCallback(async() => {
+ // prep database
+ const db = openDatabase('sheetql', '1.0', 'SheetJS WebSQL Test', 2097152);
+
+ for(var i = 0; i < queries.length; ++i) await new Promise((res, rej) => {
+ db.transaction((tx) => {
+ tx.executeSql(queries[i], [], (tx, data) => res(data), (tx, err) => rej(err));
+ });
+ });
+
+ // pull data and generate rows
+ db.readTransaction(tx => {
+ tx.executeSql("SELECT * FROM Presidents", [], (tx, data) => {
+ const aoo = Array.from(data.rows);
+ setOut("QUERY RESULT:\n" + aoo.map(r => JSON.stringify(r)).join("\n") + "\n")
+ const ws = XLSX.utils.json_to_sheet(aoo);
+ const wb = XLSX.utils.book_new();
+ XLSX.utils.book_append_sheet(wb, ws, "Presidents");
+ XLSX.writeFile(wb, "SheetQL.xlsx");
+ });
+ });
+ });
+ return (
{out}
);
}
```
-A dedicated worksheet should store the one-off named values:
+### LocalStorage and SessionStorage
+The Storage API, encompassing `localStorage` and `sessionStorage`, describes
+simple key-value stores that only support string values and keys.
+
+Arrays of objects can be stored using `JSON.stringify` using row index as key:
+
+```js
+const aoo = XLSX.utils.sheet_to_json(ws);
+for(var i = 0; i < aoo.length; ++i) localStorage.setItem(i, JSON.stringify(aoo[i]));
```
-XXX| A | B |
----+-----------------+---------+
- 1 | Path | Value |
- 2 | title | SheetDB |
- 3 | metadata.author | SheetJS |
- 4 | metadata.code | 7262 |
+
+Recovering the array of objects is possible by using `JSON.parse`:
+
+```js
+const aoo = [];
+for(var i = 0; i < localStorage.length; ++i) aoo.push(JSON.parse(localStorage.getItem(i)));
+const ws = XLSX.utils.json_to_sheet(aoo);
```
+
+This example will fetch , fill `localStorage` with
+rows, then generate a worksheet from the rows and write to a new file.
+
+:::caution
+
+This example is for illustration purposes. If array of objects is available, it
+is strongly recommended to convert that array to a worksheet directly.
+
+:::
+
+```jsx live
+function SheetJStorage() {
+ const [url, setUrl] = React.useState("https://sheetjs.com/cd.xls");
+ const set_url = React.useCallback((evt) => setUrl(evt.target.value));
+ const [out, setOut] = React.useState("");
+ const xport = React.useCallback(async() => {
+ // get first worksheet data as array of objects
+ const ab = await (await fetch(url)).arrayBuffer();
+ const wb = XLSX.read(ab), wsname = wb.SheetNames[0];
+ const aoo = XLSX.utils.sheet_to_json(wb.Sheets[wsname]);
+
+ // reset and populate localStorage
+ localStorage.clear();
+ for(var i = 0; i < aoo.length; ++i) localStorage.setItem(i, JSON.stringify(aoo[i]));
+
+ // create new array of objects from localStorage
+ const new_aoo = [];
+ for(var i = 0; i < localStorage.length; ++i) {
+ const row = JSON.parse(localStorage.getItem(i));
+ new_aoo.push(row);
+ }
+
+ setOut(`Number of rows in LocalStorage: ${localStorage.length}`);
+
+ // create and export workbook
+ const new_ws = XLSX.utils.json_to_sheet(new_aoo);
+ const new_wb = XLSX.utils.book_new();
+ XLSX.utils.book_append_sheet(new_wb, new_ws, "Sheet1");
+ XLSX.writeFile(new_wb, "SheetJStorage.xlsx");
+ });
+
+ return ( <> {out && (<>{url}
{out}
>)}
+ URL:
+
+ > );
+}
+```
+
+
+### IndexedDB
+
+[`localForage`](https://localforage.github.io/localForage/) is a lightweight
+IndexedDB wrapper that presents an async Storage interface.
+
+Arrays of objects can be stored using `JSON.stringify` using row index as key:
+
+```js
+const aoo = XLSX.utils.sheet_to_json(ws);
+for(var i = 0; i < aoo.length; ++i) await localForage.setItem(i, JSON.stringify(aoo[i]));
+```
+
+Recovering the array of objects is possible by using `JSON.parse`:
+
+```js
+const aoo = [];
+for(var i = 0; i < localForage.length; ++i) aoo.push(JSON.parse(await localForage.getItem(i)));
+const wb = XLSX.utils.json_to_sheet(aoo);
+```
+
+
+
+### MongoDB Structured Collections
+
+:::warning MongoDB Relicense
+
+This demo was originally written when MongoDB was licensed under AGPLv3. It was
+relicensed in 2018 to the Server-Side Public License. This demo was tested with
+the "MongoDB Community Server" and may not work with the "Enterprise" Server.
+
+:::
+
+MongoDB is a popular document-oriented database engine.
+
+It is straightforward to treat collections as worksheets. Each object maps to
+a row in the table.
+
+The official NodeJS connector is [`mongodb` on NPM](https://npm.im/mongodb).
+
+Worksheets can be generated from collections by using `Collection#find`. A
+`projection` can suppress the object ID field:
+
+```js
+/* generate a worksheet from a collection */
+const aoo = await collection.find({}, {projection:{_id:0}}).toArray();
+const ws = utils.json_to_sheet(aoo);
+```
+
+Collections can be populated with data from a worksheet using `insertMany`:
+
+```js
+/* import data from a worksheet to a collection */
+const aoo = XLSX.utils.sheet_to_json(ws);
+await collection.insertMany(aoo, {ordered: true});
+```
+
+Complete Example (click to show)
+
+:::caution
+
+When this demo was last tested, the `mongodb` module did not work with Node 18.
+It was verified in Node 16.16.0.
+
+:::
+
+1) Install the dependencies:
+
+```bash
+$ npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz mongodb
+```
+
+2) Start a MongoDB server on localhost (follow official instructions)
+
+3) Save the following to `SheetJSMongoCRUD.mjs` (the key step is highlighted):
+
+```js title="SheetJSMongoCRUD.mjs"
+import { writeFile, set_fs, utils } from 'xlsx/xlsx.mjs';
+import * as fs from 'fs'; set_fs(fs);
+import { MongoClient } from 'mongodb';
+
+const url = 'mongodb://localhost:27017/sheetjs';
+const db_name = 'sheetjs';
+
+(async() => {
+/* Connect to mongodb server */
+const client = await MongoClient.connect(url, { useUnifiedTopology: true });
+
+/* Sample data table */
+const db = client.db(db_name);
+try { await db.collection('pres').drop(); } catch(e) {}
+const pres = db.collection('pres');
+await pres.insertMany([
+ { name: "Barack Obama", idx: 44 },
+ { name: "Donald Trump", idx: 45 },
+ { name: "Joseph Biden", idx: 46 }
+], {ordered: true});
+
+// highlight-start
+/* Export database to XLSX */
+const wb = utils.book_new();
+const aoo = await pres.find({}, {projection:{_id:0}}).toArray();
+const ws = utils.json_to_sheet(aoo);
+utils.book_append_sheet(wb, ws, "Presidents");
+writeFile(wb, "SheetJSMongoCRUD.xlsx");
+// highlight-end
+
+/* Close connection */
+client.close();
+})();
+```
+
+4) Run `node SheetJSMongoCRUD.mjs` and open `SheetJSMongoCRUD.xlsx`
+
+
+
+
diff --git a/docz/docs/04-getting-started/03-demos/11-nosql.md b/docz/docs/04-getting-started/03-demos/11-nosql.md
new file mode 100644
index 00000000..47c497e7
--- /dev/null
+++ b/docz/docs/04-getting-started/03-demos/11-nosql.md
@@ -0,0 +1,43 @@
+---
+sidebar_position: 11
+title: NoSQL Data Stores
+---
+
+So-called "Schema-less" databases allow for arbitrary keys and values within the
+entries in the database. K/V stores and Objects add additional restrictions.
+
+:::note
+
+These data stores are capable of storing structured data. Those use cases are
+covered in the [Database demo](./database).
+
+:::
+
+There is no natural way to translate arbitrarily shaped schemas to worksheets
+in a workbook. One common trick is to dedicate one worksheet to holding named
+keys. For example, considering the JS object:
+
+```json
+{
+ "title": "SheetDB",
+ "metadata": {
+ "author": "SheetJS",
+ "code": 7262
+ },
+ "data": [
+ { "Name": "Barack Obama", "Index": 44 },
+ { "Name": "Donald Trump", "Index": 45 },
+ ]
+}
+```
+
+A dedicated worksheet should store the one-off named values:
+
+```
+XXX| A | B |
+---+-----------------+---------+
+ 1 | Path | Value |
+ 2 | title | SheetDB |
+ 3 | metadata.author | SheetJS |
+ 4 | metadata.code | 7262 |
+```
diff --git a/docz/docs/04-getting-started/03-demos/index.md b/docz/docs/04-getting-started/03-demos/index.md
index 08aea0f6..0623fbc8 100644
--- a/docz/docs/04-getting-started/03-demos/index.md
+++ b/docz/docs/04-getting-started/03-demos/index.md
@@ -12,6 +12,9 @@ The demo projects include small runnable examples and short explainers.
- [`XMLHttpRequest and fetch`](https://github.com/SheetJS/SheetJS/tree/master/demos/xhr/)
- [`Clipboard Data`](./clipboard)
- [`Typed Arrays for Machine Learning`](./ml)
+- [`LocalStorage and SessionStorage`](./database#localstorage-and-sessionstorage)
+- [`Web SQL Database`](./database#websql)
+- [`IndexedDB`](./database#indexeddb)
### Frameworks
diff --git a/docz/static/files/websql.png b/docz/static/files/websql.png
new file mode 100644
index 00000000..02ae7cf9
Binary files /dev/null and b/docz/static/files/websql.png differ