forked from sheetjs/docs.sheetjs.com
		
	websql
This commit is contained in:
		
							parent
							
								
									671ab04a12
								
							
						
					
					
						commit
						8abe41283a
					
				| @ -1,6 +1,6 @@ | ||||
| --- | ||||
| sidebar_position: 9 | ||||
| title: Databases | ||||
| sidebar_position: 10 | ||||
| title: Databases and SQL | ||||
| --- | ||||
| 
 | ||||
| import current from '/version.js'; | ||||
| @ -16,6 +16,9 @@ This demo discusses general strategies and provides examples for a variety of | ||||
| database systems.  The examples are merely intended to demonstrate very basic | ||||
| functionality. | ||||
| 
 | ||||
| Key-value stores, unstructured use of Document Databases, and other schema-less | ||||
| databases are covered in the [NoSQL demo](./nosql). | ||||
| 
 | ||||
| 
 | ||||
| ## Structured Tables | ||||
| 
 | ||||
| @ -91,9 +94,9 @@ Document databases like MongoDB tend not to require schemas. Arrays of objects | ||||
| can be used directly without setting up a schema: | ||||
| 
 | ||||
| ```js | ||||
| const aoa = XLSX.utils.sheet_to_json(ws); | ||||
| const aoo = XLSX.utils.sheet_to_json(ws); | ||||
| // highlight-next-line | ||||
| await db.collection('coll').insertMany(aoa, { ordered: true }); | ||||
| await db.collection('coll').insertMany(aoo, { ordered: true }); | ||||
| ``` | ||||
| 
 | ||||
| ::: | ||||
| @ -203,19 +206,18 @@ function SheetJSQLWriter() { | ||||
|         else values.push(`'${v.toString().replaceAll("'", "''")}'`); | ||||
|       }) | ||||
|       if(fields.length) return `INSERT INTO \`${wsname}\` (${fields.join(", ")}) VALUES (${values.join(", ")})`; | ||||
|     })).filter(x => x).slice(0, 5); | ||||
|     })).filter(x => x).slice(0, 6); | ||||
|   } | ||||
|   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() => { | ||||
|     console.log(url); | ||||
|     const ab = await (await fetch(url)).arrayBuffer(); | ||||
|     const wb = XLSX.read(ab), wsname = wb.SheetNames[0]; | ||||
|     setOut(generate_sql(wb.Sheets[wsname], wsname).join("\n")); | ||||
|   }); | ||||
| 
 | ||||
|   return ( <> {out && (<pre>{out}</pre>)} | ||||
|   return ( <> {out && (<><a href={url}>{url}</a><pre>{out}</pre></>)} | ||||
|     <b>URL: </b><input type="text" value={url} onChange={set_url} size="50"/> | ||||
|     <br/><button onClick={xport}><b>Fetch!</b></button> | ||||
|   </> ); | ||||
| @ -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. | ||||
| 
 | ||||
| <https://caniuse.com/sql-storage> 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 <http://sheetjs.com/sql> 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: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ```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 ( <pre>{out}<button onClick={xport}><b>Fetch!</b></button></pre> ); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 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 <https://sheetjs.com/cd.xls>, 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 && (<><a href={url}>{url}</a><pre>{out}</pre></>)} | ||||
|     <b>URL: </b><input type="text" value={url} onChange={set_url} size="50"/> | ||||
|     <br/><button onClick={xport}><b>Fetch!</b></button> | ||||
|   </> ); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| 
 | ||||
| ### 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}); | ||||
| ``` | ||||
| 
 | ||||
| <details><summary><b>Complete Example</b> (click to show)</summary> | ||||
| 
 | ||||
| :::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` | ||||
| 
 | ||||
| </details> | ||||
| 
 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										43
									
								
								docz/docs/04-getting-started/03-demos/11-nosql.md
									
									
									
									
									
										Normal file
									
								
							
							
								
								
								
								
								
									
									
								
							
						
						
									
										43
									
								
								docz/docs/04-getting-started/03-demos/11-nosql.md
									
									
									
									
									
										Normal file
									
								
							| @ -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 | | ||||
| ``` | ||||
| @ -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 | ||||
| 
 | ||||
|  | ||||
							
								
								
									
										
											BIN
										
									
								
								docz/static/files/websql.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						
									
										
											BIN
										
									
								
								docz/static/files/websql.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 70 KiB | 
		Loading…
	
		Reference in New Issue
	
	Block a user