From c627311751e92d83e73926c5d536238dd2171995 Mon Sep 17 00:00:00 2001 From: Ziyang Hu Date: Sat, 26 Nov 2022 17:31:52 +0800 Subject: [PATCH] update NodeJS libraries and instructions --- README.md | 89 ------------------ cozo-lib-nodejs/README.md | 115 +++++++++++++++++++++-- cozo-lib-nodejs/example.js | 19 ++++ cozo-lib-nodejs/index.js | 29 ++++-- cozo-lib-wasm/wasm-react-demo/src/App.js | 3 - scripts/compress.sh | 11 +++ 6 files changed, 158 insertions(+), 108 deletions(-) create mode 100644 cozo-lib-nodejs/example.js diff --git a/README.md b/README.md index 4720dee6..9f570fcd 100644 --- a/README.md +++ b/README.md @@ -99,95 +99,6 @@ please raise it [here](https://github.com/cozodb/cozo/discussions). ## Getting started -In this section we will learn to run three queries to illustrate how to use Cozo in each of the -supported language. We will run the queries against a local database with the relative path `_test_db`. - -For all languages, the first query is a standard hello world: - -``` -?[] <- [['hello', 'world!']] -``` - -The second one illustrates the use of a named parameter `$name`: - -``` -?[] <- [['hello', 'world', $name]] -``` - -The third one contains an error, and illustrates how you get nice error messages printed out: - -``` -?[a] <- [[1, 2]] -``` - -### Python + Jupyter notebook (recommended) - -You should already have JupyterLab installed, and have installed Cozo by `pip install "pycozo[embedded,pandas]"`. - -Start your jupyter lab server, open the web UI, and start a Python 3 kernel. - -In a cell, run - -``` -%load_ext pycozo.ipyext_direct -%cozo_path _test_db -``` - -this opens a local database with relative path `_test_db`. - -To set the parameter for the second query, run - -``` -%cozo_set name 'Jupyter' -``` - -After that, just type queries in the cells and run them. -For more tricks, refer [here](https://github.com/cozodb/pycozo). - -### Python - -You should have Cozo installed by running `pip install "pycozo[embedded]"`. -The following scripts runs the three queries in turn: - -```python -from pycozo import Client - -db = Client(path='_test_db', dataframe=False) - - -def print_query(script, params=None): - try: - print(db.run(script, params)) - except Exception as e: - print(repr(e)) - - -print_query("?[] <- [['hello', 'world!']]") -print_query("?[] <- [['hello', 'world', $name]]", {"name": "Python"}) -print_query("?[a] <- [[1, 2]]") -``` - -### NodeJS - -You should have Cozo installed by running `npm install --save cozo-node"`. -The following scripts runs the three queries in turn: - -```javascript -const {CozoDb} = require('cozo-node') - -const db = new CozoDb('_test_db') - -function printQuery(query, params) { - db.run(query, params) - .then(data => console.log(data)) - .catch(err => console.error(err.display || err.message)) -} - -printQuery("?[] <- [['hello', 'world!']]") -printQuery("?[] <- [['hello', 'world', $name]]", {"name": "JavaScript"}) -printQuery("?[a] <- [[1, 2]]") -``` - ### Clojure You should already have the package `com.github.zh217/cozo-clj` installed. diff --git a/cozo-lib-nodejs/README.md b/cozo-lib-nodejs/README.md index 8b0b36d5..b650046b 100644 --- a/cozo-lib-nodejs/README.md +++ b/cozo-lib-nodejs/README.md @@ -4,27 +4,126 @@ Embedded [CozoDB](https://github.com/cozodb/cozo) for NodeJS. +This document describes how to set up the Cozo module for use in NodeJS. +To learn how to use CozoDB (CozoScript), follow +the [tutorial](https://nbviewer.org/github/cozodb/cozo-docs/blob/main/tutorial/tutorial.ipynb) +first and then read the [manual](https://cozodb.github.io/current/manual/). You can run all the queries +described in the tutorial with an in-browser DB [here](https://cozodb.github.io/wasm-demo/). + ## Installation ```bash npm install --save cozo-node ``` -If that doesn't work because there are no precompiled binaries for your platform, +If that doesn't work because there are no precompiled binaries for your platform, scroll below to the building section. ## Usage -Refer to the main [docs](https://github.com/cozodb/cozo#nodejs). +```javascript +const {CozoDb} = require('cozo-node') + +const db = new CozoDb() + +function printQuery(query, params) { + db.run(query, params) + .then(data => console.log(data)) + .catch(err => console.error(err.display || err.message)) +} + +printQuery("?[] <- [['hello', 'world!']]") +printQuery("?[] <- [['hello', 'world', $name]]", {"name": "JavaScript"}) +printQuery("?[a] <- [[1, 2]]") +``` + +### API + +```ts +class CozoDb { + /** + * Constructor + * + * @param engine: defaults to 'mem', the in-memory non-persistent engine. + * 'sqlite', 'rocksdb' and maybe others are available, + * depending on compile time flags. + * @param path: path to store the data on disk, defaults to 'data.db', + * may not be applicable for some engines such as 'mem' + * @param options: defaults to {}, ignored by all the engines in the published NodeJS artefact + */ + constructor(engine: string, path: string, options: object): CozoDb; + + /** + * You must call this method for any database you no longer want to use: + * otherwise the native resources associated with it may linger for as + * long as your program runs. Simply `delete` the variable is not enough. + */ + close(): void; + + /** + * Runs a query + * + * @param script: the query + * @param params: the parameters as key-value pairs, defaults to {} + */ + async run(script: string, params: object): object; + + /** + * Export several relations + * + * @param relations: names of relations to export, in an array. + * @param as_objects: defaults to `false`, changes the return format. + */ + async exportRelations(relations: Array, as_objects: boolean): object; + + /** + * Import several relations + * + * @param data: in the same form as returned by `exportRelations`. The relations + * must already exist in the database. + */ + async importRelations(data: object): object; + + /** + * Backup database + * + * @param path: path to file to store the backup. + */ + async backup(path: string): object; + + /** + * Restore from a backup. Will fail if the current database already contains data. + * + * @param path: path to the backup file. + */ + async restore(path: string): object; + + /** + * Import several relations from a backup. The relations must already exist in the database. + * + * @param path: path to the backup file. + * @param rels: the relations to import. + */ + async importRelationsFromBackup(path: string, rels: Array): object; +} +``` ## Building -Building `cozo-node` requires a [supported version of Node and Rust](https://github.com/neon-bindings/neon#platform-support). +Building `cozo-node` requires a [Rust toolchain](https://rustup.rs). Run + +```bash +cargo build --release -p cozo-node -F compact -F storage-rocksdb +``` + +and then find the dynamic library (names can vary a lot, the file extension is `.so` on Linux, `.dylib` on Mac, +and `.dll` on Windows) under the `../target/` folder (you may need to search for it). +Copy it to the file `native/6/index.node` under this directory (create intermediate directories if they don't exist). -Refer to the [script for linux](build_linux.sh), the [script for mac](build_mac.sh), -or the [script for windows](build_win.ps1) for the commands required. +If you did everything correctly, you should get the hello world message printed out when you run -After building, `npm install .` will install the package. +```bash +node example.js +``` -This project was bootstrapped by [create-neon](https://www.npmjs.com/package/create-neon). -To learn more about Neon, see the [Neon documentation](https://neon-bindings.com). +under this directory. \ No newline at end of file diff --git a/cozo-lib-nodejs/example.js b/cozo-lib-nodejs/example.js new file mode 100644 index 00000000..d12d1511 --- /dev/null +++ b/cozo-lib-nodejs/example.js @@ -0,0 +1,19 @@ +/* + * Copyright 2022, The Cozo Project Authors. + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. + * If a copy of the MPL was not distributed with this file, + * You can obtain one at https://mozilla.org/MPL/2.0/. + */ + +const {CozoDb} = require('.'); + +(async () => { + const db = new CozoDb() + try { + const result = await db.run('?[a] <- [["hello"], ["world"]]'); + console.log(result.rows) + } catch (e) { + console.error(e) + } +})() \ No newline at end of file diff --git a/cozo-lib-nodejs/index.js b/cozo-lib-nodejs/index.js index b28e0be7..9feb8a89 100644 --- a/cozo-lib-nodejs/index.js +++ b/cozo-lib-nodejs/index.js @@ -12,8 +12,8 @@ const binding_path = binary.find(path.resolve(path.join(__dirname, './package.js const native = require(binding_path); class CozoDb { - constructor(path) { - this.db_id = native.open_db(path) + constructor(engine, path, options) { + this.db_id = native.open_db(engine || 'mem', path || 'data.db', JSON.stringify(options || {})) } close() { @@ -34,10 +34,10 @@ class CozoDb { }) } - exportRelations(rels) { + exportRelations(relations, as_objects) { return new Promise((resolve, reject) => { - const rels_str = JSON.stringify(rels); - native.export_relations(rels_str, (result_str) => { + const rels_str = JSON.stringify({relations, as_objects: as_objects || false}); + native.export_relations(this.db_id, rels_str, (result_str) => { const result = JSON.parse(result_str); if (result.ok) { resolve(result) @@ -51,7 +51,20 @@ class CozoDb { importRelations(data) { return new Promise((resolve, reject) => { const rels_str = JSON.stringify(data); - native.import_relations(rels_str, (result_str) => { + native.import_relations(this.db_id, rels_str, (result_str) => { + const result = JSON.parse(result_str); + if (result.ok) { + resolve(result) + } else { + reject(result) + } + }) + }) + } + + importRelationsFromBackup(path, rels) { + return new Promise((resolve, reject) => { + native.import_relations(this.db_id, path, JSON.stringify(rels), (result_str) => { const result = JSON.parse(result_str); if (result.ok) { resolve(result) @@ -64,7 +77,7 @@ class CozoDb { backup(path) { return new Promise((resolve, reject) => { - native.backup_db(path, (result_str) => { + native.backup_db(this.db_id, path, (result_str) => { const result = JSON.parse(result_str); if (result.ok) { resolve(result) @@ -77,7 +90,7 @@ class CozoDb { restore(path) { return new Promise((resolve, reject) => { - native.restore_db(path, (result_str) => { + native.restore_db(this.db_id, path, (result_str) => { const result = JSON.parse(result_str); if (result.ok) { resolve(result) diff --git a/cozo-lib-wasm/wasm-react-demo/src/App.js b/cozo-lib-wasm/wasm-react-demo/src/App.js index 59e3ca3b..d952f351 100644 --- a/cozo-lib-wasm/wasm-react-demo/src/App.js +++ b/cozo-lib-wasm/wasm-react-demo/src/App.js @@ -44,12 +44,10 @@ function App() { }) }, []); - const renderCell = (colIdx) => (rowIdx) => {displayValue(queryResults.rows[rowIdx][colIdx])} - function handleKeyDown(e) { if (e.key === 'Enter' && e.shiftKey) { e.preventDefault(); @@ -63,7 +61,6 @@ function App() { } } - function typeInTextarea(newText, el = document.activeElement) { const [start, end] = [el.selectionStart, el.selectionEnd]; el.setRangeText(newText, start, end, 'end'); diff --git a/scripts/compress.sh b/scripts/compress.sh index 33f056cd..cdf8b028 100755 --- a/scripts/compress.sh +++ b/scripts/compress.sh @@ -1,6 +1,9 @@ #!/usr/bin/env bash +VERSION=$(cat ./VERSION) + gzip release/*java*.dll +gzip release/libcozo_node* for f in release/*.exe release/*.dll release/*.lib; do zip $f.zip $f @@ -9,6 +12,14 @@ done gzip release/*.a release/*.so release/*.dylib release/*-darwin release/*-gnu release/*-musl +mkdir -p cozo-lib-nodejs/build/stage/$VERSION/ + +cp release/libcozo_node-$VERSION-aarch64-apple-darwin.dylib.gz cozo-lib-nodejs/build/stage/$VERSION/6-darwin-arm64.tar.gz +cp release/libcozo_node-$VERSION-x86_64-apple-darwin.dylib.gz cozo-lib-nodejs/build/stage/$VERSION/6-darwin-x64.tar.gz +cp release/libcozo_node-$VERSION-x86_64-unknown-linux-gnu.so.gz cozo-lib-nodejs/build/stage/$VERSION/6-linux-x64.tar.gz +cp release/libcozo_node-$VERSION-aarch64-unknown-linux-gnu.so.gz cozo-lib-nodejs/build/stage/$VERSION/6-linux-arm64.tar.gz +cp release/libcozo_node-$VERSION-x86_64-pc-windows-msvc.dll.gz cozo-lib-nodejs/build/stage/$VERSION/6-win32-x64.tar.gz + for f in release/*; do gpg --armor --detach-sign $f done \ No newline at end of file