forked from sheetjs/docs.sheetjs.com
		
	idb
This commit is contained in:
		
							parent
							
								
									dbe0554b9b
								
							
						
					
					
						commit
						def028682c
					
				| @ -16,15 +16,19 @@ The public demo <https://sheetjs.com/sql> generates a database from workbook. | ||||
| ## WebSQL Details | ||||
| 
 | ||||
| Importing data from spreadsheets is straightforward using the `generate_sql` | ||||
| helper function from ["Generating Tables"](/docs/demos/data/sql#generating-tables): | ||||
| helper function from ["Generating Tables"](/docs/demos/data/sql#generating-tables). | ||||
| 
 | ||||
| The Web SQL Database API is callback-based.  The following snippet wraps | ||||
| transactions in Promise objects: | ||||
| 
 | ||||
| ```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) => { | ||||
| for(var stmt of stmts) await new Promise((res, rej) => { | ||||
|   db.transaction(tx => | ||||
|     tx.executeSql(stmts[i], [], | ||||
|     tx.executeSql(stmt, [], | ||||
|       (tx, data) => res(data), // if the query is successful, return the data | ||||
|       (tx, err) => rej(err) // if the query fails, reject with the error | ||||
|   )); | ||||
| @ -33,23 +37,29 @@ for(var i = 0; i < stmts.length; ++i) await new Promise((res, rej) => { | ||||
| 
 | ||||
| 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 properties like `0`, `1`, etc.  However, this is not a real Array object. | ||||
| and properties like `0`, `1`, etc.  However, this is not a real Array object! | ||||
| 
 | ||||
| A real Array can be created using `Array.from`: | ||||
| 
 | ||||
| ```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 | ||||
|     // ... perform an export here OR wrap in a Promise | ||||
|   }) | ||||
| ); | ||||
| ``` | ||||
| 
 | ||||
| ### Live Demo | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested on 2023 February 26 | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| The following demo generates a database with 5 fixed 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: | ||||
| @ -67,16 +77,16 @@ function SheetQL() { | ||||
|     'INSERT INTO Presidents  (Name, Idx) VALUES ("Joseph Biden", 46)' | ||||
|   ]; | ||||
|   const xport = React.useCallback(async() => { | ||||
|     // prep database | ||||
|     /* 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) => { | ||||
|     for(var q of queries) await new Promise((res, rej) => { | ||||
|       db.transaction((tx) => { | ||||
|         tx.executeSql(queries[i], [], (tx, data) => res(data), (tx, err) => rej(err)); | ||||
|         tx.executeSql(q, [], (tx, data) => res(data), (tx, err) => rej(err)); | ||||
|       }); | ||||
|     }); | ||||
| 
 | ||||
|     // pull data and generate rows | ||||
|     /* pull data and generate rows */ | ||||
|     db.readTransaction(tx => { | ||||
|       tx.executeSql("SELECT * FROM Presidents", [], (tx, data) => { | ||||
|         const aoo = Array.from(data.rows); | ||||
| @ -102,16 +112,24 @@ export as XLSX. | ||||
| 
 | ||||
| [The Northwind database is available in SQLite form](https://raw.githubusercontent.com/jpwhite3/northwind-SQLite3/master/dist/northwind.db). | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested on 2023 February 26 | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ### NodeJS | ||||
| 
 | ||||
| The **`better-sqlite3`** module provides a very simple API for working with | ||||
| SQLite databases. `Statement#all` runs a prepared statement and returns an array | ||||
| of JS objects. | ||||
| 
 | ||||
| 0) [Download `northwind.db`](https://raw.githubusercontent.com/jpwhite3/northwind-SQLite3/master/dist/northwind.db). | ||||
| 
 | ||||
| 1) Install the dependencies: | ||||
| 
 | ||||
| ```bash | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz better-sqlite3 | ||||
| npm i --save https://cdn.sheetjs.com/xlsx-latest/xlsx-latest.tgz better-sqlite3@8.1.0 | ||||
| ``` | ||||
| 
 | ||||
| 2) Save the following to `node.mjs`: | ||||
| @ -157,6 +175,8 @@ XLSX.writeFile(wb, "node.xlsx"); | ||||
| 
 | ||||
| Bun ships with a built-in high-performance module `bun:sqlite`. | ||||
| 
 | ||||
| 0) [Download `northwind.db`](https://raw.githubusercontent.com/jpwhite3/northwind-SQLite3/master/dist/northwind.db). | ||||
| 
 | ||||
| 1) Install the dependencies: | ||||
| 
 | ||||
| ```bash | ||||
| @ -206,6 +226,8 @@ XLSX.writeFile(wb, "bun.xlsx"); | ||||
| 
 | ||||
| Deno `sqlite` library returns raw arrays of arrays. | ||||
| 
 | ||||
| 0) [Download `northwind.db`](https://raw.githubusercontent.com/jpwhite3/northwind-SQLite3/master/dist/northwind.db). | ||||
| 
 | ||||
| 1) Save the following to `deno.ts`: | ||||
| 
 | ||||
| ```ts title="deno.ts" | ||||
| @ -244,4 +266,4 @@ result.forEach(function(row) { | ||||
| XLSX.writeFile(wb, "deno.xlsx"); | ||||
| ``` | ||||
| 
 | ||||
| 3) Run `deno run --allow-read --allow-write deno.ts` and open `deno.xlsx` | ||||
| 2) Run `deno run --allow-read --allow-write deno.ts` and open `deno.xlsx` | ||||
|  | ||||
| @ -6,26 +6,228 @@ sidebar_custom_props: | ||||
|   type: web | ||||
| --- | ||||
| 
 | ||||
| <head> | ||||
|   <script type="text/javascript" src="https://unpkg.com/localforage@1.10.0/dist/localforage.min.js"></script> | ||||
|   <script type="text/javascript" src="https://unpkg.com/dexie@3.2.3/dist/dexie.js"></script> | ||||
| </head> | ||||
| 
 | ||||
| :::warning | ||||
| 
 | ||||
| IndexedDB is a very low-level API.  It is strongly recommended to use a wrapper | ||||
| library or [WebSQL](/docs/demos/data/websql) in production applications. | ||||
| IndexedDB is a very low-level API. | ||||
| 
 | ||||
| Browser vendors recommend using libraries or [WebSQL](/docs/demos/data/websql) | ||||
| in production applications. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ## Wrapper Libraries | ||||
| 
 | ||||
| A number of popular wrapper libraries seek to simplify IndexedDB operations. | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| The wrapper libraries in this section have been used by SheetJS users in | ||||
| production sites. | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| ### localForage | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested on 2023 February 26 with `localForage` 1.10.0 | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| `localForage` is a IndexedDB wrapper that presents an async Storage interface. | ||||
| 
 | ||||
| Arrays of objects can be stored using `JSON.stringify` using row index as key: | ||||
| Arrays of objects can be stored using `setItem` 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])); | ||||
| for(var i = 0; i < aoo.length; ++i) await localForage.setItem(i, aoo[i]); | ||||
| ``` | ||||
| 
 | ||||
| Recovering the array of objects is possible by using `JSON.parse`: | ||||
| Recovering the array of objects involves an iteration over the storage: | ||||
| 
 | ||||
| ```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); | ||||
| await localforage.iterate((v, k) => { aoa[+k] = v; }); | ||||
| const ws = XLSX.utils.json_to_sheet(aoo); | ||||
| ``` | ||||
| 
 | ||||
| #### Demo | ||||
| 
 | ||||
| This demo prepares a small IndexedDB database with some sample data. | ||||
| 
 | ||||
| After saving the exported file, the IndexedDB database can be inspected in the | ||||
| "IndexedDB" section of the "Application" Tab of Developer Tools: | ||||
| 
 | ||||
|  | ||||
| 
 | ||||
| ```jsx live | ||||
| function SheetJSLocalForage() { | ||||
|   const data = [ | ||||
|     { Name: "Barack Obama", Index: 44 }, | ||||
|     { Name: "Donald Trump", Index: 45 }, | ||||
|     { Name: "Joseph Biden", Index: 46 } | ||||
|   ]; | ||||
|   const xport = React.useCallback(async() => { | ||||
|     /* force use of IndexedDB and connect to DB */ | ||||
|     localforage.config({ | ||||
|       driver: [ localforage.INDEXEDDB ], | ||||
|       name: "SheetQL", | ||||
|       size: 2097152 | ||||
|     }); | ||||
| 
 | ||||
|     /* create sample data */ | ||||
|     await localforage.clear(); | ||||
|     for(var i = 0; i < data.length; ++i) await localforage.setItem(i, data[i]); | ||||
| 
 | ||||
|     /* pull data and generate aoa */ | ||||
|     const aoo = []; | ||||
|     await localforage.iterate((v, k) => { aoo[+k] = v; }); | ||||
| 
 | ||||
|     /* export */ | ||||
|     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, "SheetJSLocalForage.xlsx"); | ||||
|   }); | ||||
|   return ( <pre><button onClick={xport}><b>Do it!</b></button></pre> ); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### DexieJS | ||||
| 
 | ||||
| :::note | ||||
| 
 | ||||
| This demo was last tested on 2023 February 26 with DexieJS 3.2.3 | ||||
| 
 | ||||
| ::: | ||||
| 
 | ||||
| DexieJS is a minimalistic wrapper for IndexedDB.  It provides a convenient | ||||
| interface for creating multiple logical tables, well-suited for workbooks. | ||||
| 
 | ||||
| #### Importing Data | ||||
| 
 | ||||
| When configuring tables, DexieJS needs a schema. The schema definition supports | ||||
| primary keys and other properties, but they are not required: | ||||
| 
 | ||||
| ```js | ||||
| /* assuming `wb` is a workbook from XLSX.read */ | ||||
| var db = new Dexie("SheetJSDexie"); | ||||
| db.version(1).stores(Object.fromEntries(wb.SheetNames.map(n => ([n, "++"])))); | ||||
| ``` | ||||
| 
 | ||||
| After the database is configured, `bulkPut` can insert arrays of objects: | ||||
| 
 | ||||
| ```js | ||||
| /* loop over worksheet names */ | ||||
| for(let i = 0; i <= wb.SheetNames.length; ++i) { | ||||
|   /* get the worksheet for the specified index */ | ||||
|   const wsname = wb.SheetNames[i]; | ||||
|   const ws = wb.Sheets[wsname]; | ||||
|   if(!ws) continue; | ||||
|   /* generate an array of objects */ | ||||
|   const aoo = XLSX.utils.sheet_to_json(ws); | ||||
|   /* push to idb */ | ||||
|   await db[wsname].bulkPut(aoo); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| This demo inserts all data from a selected worksheet into a database, then | ||||
| fetches the data from the first worksheet in reverse: | ||||
| 
 | ||||
| ```jsx live | ||||
| /* The live editor requires this function wrapper */ | ||||
| function SheetJSDexieImport(props) { | ||||
|   const [__html, setHTML] = React.useState("Select a spreadsheet"); | ||||
| 
 | ||||
|   return (<> | ||||
|     <input type="file" onChange={async(e) => { try { | ||||
|       /* get data as an ArrayBuffer */ | ||||
|       const file = e.target.files[0]; | ||||
|       const data = await file.arrayBuffer(); | ||||
| 
 | ||||
|       /* parse worksheet */ | ||||
|       const wb = XLSX.read(data); | ||||
| 
 | ||||
|       /* load into indexeddb */ | ||||
|       await Dexie.delete("SheetJSDexie"); | ||||
|       const db = new Dexie("SheetJSDexie"); | ||||
|       const wsnames = wb.SheetNames.map(n => ([n, "++"])); | ||||
|       db.version(1).stores(Object.fromEntries(wsnames)); | ||||
| 
 | ||||
|       /* loop over worksheet names */ | ||||
|       for(let i = 0; i <= wb.SheetNames.length; ++i) { | ||||
|         /* get the worksheet for the specified index */ | ||||
|         const wsname = wb.SheetNames[i]; | ||||
|         const ws = wb.Sheets[wsname]; | ||||
|         if(!ws) continue; | ||||
|         /* generate an array of objects */ | ||||
|         const aoo = XLSX.utils.sheet_to_json(ws); | ||||
|         /* push to idb */ | ||||
|         await db[wsname].bulkPut(aoo); | ||||
|       } | ||||
| 
 | ||||
|       /* fetch the first table in reverse order */ | ||||
|       const rev = await db[wb.SheetNames[0]].reverse().toArray(); | ||||
| 
 | ||||
|       setHTML(rev.map(r => JSON.stringify(r)).join("\n")); | ||||
|     } catch(e) { setHTML(e && e.message || e); }}}/> | ||||
|     <pre dangerouslySetInnerHTML={{ __html }}/> | ||||
|   </>); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| #### Exporting Data | ||||
| 
 | ||||
| `db.tables` is a plain array of table objects. `toArray` fetches data: | ||||
| 
 | ||||
| ```js | ||||
| /* create blank workbook */ | ||||
| const wb = XLSX.utils.book_new(); | ||||
| /* loop tables */ | ||||
| for(const table of db.tables) { | ||||
|   /* get data */ | ||||
|   const aoo = await table.toArray(); | ||||
|   /* create worksheet */ | ||||
|   const ws = XLSX.utils.json_to_sheet(aoo); | ||||
|   /* add to workbook */ | ||||
|   XLSX.utils.book_append_sheet(wb, ws, table.name); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| This demo prepares a small database with some sample data. | ||||
| 
 | ||||
| ```jsx live | ||||
| function SheetJSDexieExport() { | ||||
|   const data = [ | ||||
|     { Name: "Barack Obama", Index: 44 }, | ||||
|     { Name: "Donald Trump", Index: 45 }, | ||||
|     { Name: "Joseph Biden", Index: 46 } | ||||
|   ]; | ||||
|   const xport = React.useCallback(async() => { | ||||
|     /* prepare db */ | ||||
|     await Dexie.delete("SheetJSDexie"); | ||||
|     var db = new Dexie("SheetJSDexie"); | ||||
|     db.version(1).stores({ Presidents: "++" }); | ||||
|     db.Presidents.bulkPut(data); | ||||
| 
 | ||||
|     /* pull data and generate workbook */ | ||||
|     const wb = XLSX.utils.book_new(); | ||||
|     for(const table of db.tables) { | ||||
|       const aoo = await table.toArray(); | ||||
|       const ws = XLSX.utils.json_to_sheet(aoo); | ||||
|       XLSX.utils.book_append_sheet(wb, ws, table.name); | ||||
|     } | ||||
|     XLSX.writeFile(wb, "SheetJSDexie.xlsx"); | ||||
|   }); | ||||
|   return ( <pre><button onClick={xport}><b>Do it!</b></button></pre> ); | ||||
| } | ||||
| ``` | ||||
| 
 | ||||
| ### AlaSQL | ||||
| 
 | ||||
| [AlaSQL](/docs/demos/data/alasql) ships with an IndexedDB backend. | ||||
							
								
								
									
										
											BIN
										
									
								
								docz/static/storageapi/lforage.png
									
									
									
									
									
										Normal file
									
								
							
							
								
									
								
								
								
								
								
									
									
								
							
						
						
									
										
											BIN
										
									
								
								docz/static/storageapi/lforage.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| After Width: | Height: | Size: 72 KiB | 
		Loading…
	
		Reference in New Issue
	
	Block a user