From a67e966cbe0eebbfb38490b765d1e2fe80ab0912 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Sun, 19 May 2024 11:14:55 +0200 Subject: [PATCH 1/3] Add Node tempaltes for database integrations --- node/query-mongo-atlas/README.md | 50 +++++++ node/query-mongo-atlas/package-lock.json | 151 ++++++++++++++++++++ node/query-mongo-atlas/package.json | 16 +++ node/query-mongo-atlas/src/main.js | 21 +++ node/query-mongo-atlas/src/mongo.js | 46 ++++++ node/query-mongo-atlas/src/utils.js | 14 ++ node/query-neo4j-auradb/README.md | 147 +++++++++++++++++++ node/query-neo4j-auradb/package-lock.json | 142 ++++++++++++++++++ node/query-neo4j-auradb/package.json | 16 +++ node/query-neo4j-auradb/src/main.js | 25 ++++ node/query-neo4j-auradb/src/neo4j.js | 52 +++++++ node/query-neo4j-auradb/src/utils.js | 14 ++ node/query-neon-postgres/README.md | 86 +++++++++++ node/query-neon-postgres/package-lock.json | 28 ++++ node/query-neon-postgres/package.json | 16 +++ node/query-neon-postgres/src/main.js | 37 +++++ node/query-neon-postgres/src/neon.js | 48 +++++++ node/query-neon-postgres/src/utils.js | 14 ++ node/query-redis-labs/README.md | 50 +++++++ node/query-redis-labs/package-lock.json | 106 ++++++++++++++ node/query-redis-labs/package.json | 16 +++ node/query-redis-labs/src/main.js | 21 +++ node/query-redis-labs/src/redis.js | 44 ++++++ node/query-redis-labs/src/utils.js | 14 ++ node/query-upstash-vector/README.md | 63 ++++++++ node/query-upstash-vector/package-lock.json | 21 +++ node/query-upstash-vector/package.json | 16 +++ node/query-upstash-vector/src/main.js | 25 ++++ node/query-upstash-vector/src/upstash.js | 49 +++++++ node/query-upstash-vector/src/utils.js | 14 ++ 30 files changed, 1362 insertions(+) create mode 100644 node/query-mongo-atlas/README.md create mode 100644 node/query-mongo-atlas/package-lock.json create mode 100644 node/query-mongo-atlas/package.json create mode 100644 node/query-mongo-atlas/src/main.js create mode 100644 node/query-mongo-atlas/src/mongo.js create mode 100644 node/query-mongo-atlas/src/utils.js create mode 100644 node/query-neo4j-auradb/README.md create mode 100644 node/query-neo4j-auradb/package-lock.json create mode 100644 node/query-neo4j-auradb/package.json create mode 100644 node/query-neo4j-auradb/src/main.js create mode 100644 node/query-neo4j-auradb/src/neo4j.js create mode 100644 node/query-neo4j-auradb/src/utils.js create mode 100644 node/query-neon-postgres/README.md create mode 100644 node/query-neon-postgres/package-lock.json create mode 100644 node/query-neon-postgres/package.json create mode 100644 node/query-neon-postgres/src/main.js create mode 100644 node/query-neon-postgres/src/neon.js create mode 100644 node/query-neon-postgres/src/utils.js create mode 100644 node/query-redis-labs/README.md create mode 100644 node/query-redis-labs/package-lock.json create mode 100644 node/query-redis-labs/package.json create mode 100644 node/query-redis-labs/src/main.js create mode 100644 node/query-redis-labs/src/redis.js create mode 100644 node/query-redis-labs/src/utils.js create mode 100644 node/query-upstash-vector/README.md create mode 100644 node/query-upstash-vector/package-lock.json create mode 100644 node/query-upstash-vector/package.json create mode 100644 node/query-upstash-vector/src/main.js create mode 100644 node/query-upstash-vector/src/upstash.js create mode 100644 node/query-upstash-vector/src/utils.js diff --git a/node/query-mongo-atlas/README.md b/node/query-mongo-atlas/README.md new file mode 100644 index 00000000..ec510d32 --- /dev/null +++ b/node/query-mongo-atlas/README.md @@ -0,0 +1,50 @@ +# 🗄️ Node.js Query Mongo Atlas Function + +A function to store warehouses to SQL database, and query to list them. + +## 🧰 Usage + +### GET / + +- Save one warehouse and query to return all of them. + +**Response** + +Sample `200` Response: + +```js +[ + { + "_id": "664897aff5fc199b80c1a132", + "location": "Street 283, Earth", + "capacity": 60 + }, + { + "_id": "664897f58f2d686e85b93692", + "location": "Street 593, Earth", + "capacity": 60 + }, + // ... +] +``` + +## ⚙️ Configuration + +| Setting | Value | +| ----------------- | ------------- | +| Runtime | Node (18.0) | +| Entrypoint | `src/main.js` | +| Build Commands | `npm install` | +| Permissions | `any` | +| Timeout (Seconds) | 15 | + +## 🔒 Environment Variables + +### MONGO_URI + +The endpoint to connect to your Mongo database. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `mongodb+srv://appwrite:Yx42hafg7Q4fgkxe@cluster0.7mslfog.mongodb.net/?retryWrites=true&w=majority&appName=Appwrite` | diff --git a/node/query-mongo-atlas/package-lock.json b/node/query-mongo-atlas/package-lock.json new file mode 100644 index 00000000..828c37c2 --- /dev/null +++ b/node/query-mongo-atlas/package-lock.json @@ -0,0 +1,151 @@ +{ + "name": "query-mongo-atlas", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "query-mongo-atlas", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "mongodb": "^6.6.2" + } + }, + "node_modules/@mongodb-js/saslprep": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@mongodb-js/saslprep/-/saslprep-1.1.7.tgz", + "integrity": "sha512-dCHW/oEX0KJ4NjDULBo3JiOaK5+6axtpBbS+ao2ZInoAL9/YRQLhXzSNAFz7hP4nzLkIqsfYAK/PDE3+XHny0Q==", + "dependencies": { + "sparse-bitfield": "^3.0.3" + } + }, + "node_modules/@types/webidl-conversions": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", + "integrity": "sha512-CiJJvcRtIgzadHCYXw7dqEnMNRjhGZlYK05Mj9OyktqV8uVT8fD2BFOB7S1uwBE3Kj2Z+4UyPmFw/Ixgw/LAlA==" + }, + "node_modules/@types/whatwg-url": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/whatwg-url/-/whatwg-url-11.0.4.tgz", + "integrity": "sha512-lXCmTWSHJvf0TRSO58nm978b8HJ/EdsSsEKLd3ODHFjo+3VGAyyTp4v50nWvwtzBxSMQrVOK7tcuN0zGPLICMw==", + "dependencies": { + "@types/webidl-conversions": "*" + } + }, + "node_modules/bson": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/bson/-/bson-6.7.0.tgz", + "integrity": "sha512-w2IquM5mYzYZv6rs3uN2DZTOBe2a0zXLj53TGDqwF4l6Sz/XsISrisXOJihArF9+BZ6Cq/GjVht7Sjfmri7ytQ==", + "engines": { + "node": ">=16.20.1" + } + }, + "node_modules/memory-pager": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", + "integrity": "sha512-ZS4Bp4r/Zoeq6+NLJpP+0Zzm0pR8whtGPf1XExKLJBAczGMnSi3It14OiNCStjQjM6NU1okjQGSxgEZN8eBYKg==" + }, + "node_modules/mongodb": { + "version": "6.6.2", + "resolved": "https://registry.npmjs.org/mongodb/-/mongodb-6.6.2.tgz", + "integrity": "sha512-ZF9Ugo2JCG/GfR7DEb4ypfyJJyiKbg5qBYKRintebj8+DNS33CyGMkWbrS9lara+u+h+yEOGSRiLhFO/g1s1aw==", + "dependencies": { + "@mongodb-js/saslprep": "^1.1.5", + "bson": "^6.7.0", + "mongodb-connection-string-url": "^3.0.0" + }, + "engines": { + "node": ">=16.20.1" + }, + "peerDependencies": { + "@aws-sdk/credential-providers": "^3.188.0", + "@mongodb-js/zstd": "^1.1.0", + "gcp-metadata": "^5.2.0", + "kerberos": "^2.0.1", + "mongodb-client-encryption": ">=6.0.0 <7", + "snappy": "^7.2.2", + "socks": "^2.7.1" + }, + "peerDependenciesMeta": { + "@aws-sdk/credential-providers": { + "optional": true + }, + "@mongodb-js/zstd": { + "optional": true + }, + "gcp-metadata": { + "optional": true + }, + "kerberos": { + "optional": true + }, + "mongodb-client-encryption": { + "optional": true + }, + "snappy": { + "optional": true + }, + "socks": { + "optional": true + } + } + }, + "node_modules/mongodb-connection-string-url": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/mongodb-connection-string-url/-/mongodb-connection-string-url-3.0.1.tgz", + "integrity": "sha512-XqMGwRX0Lgn05TDB4PyG2h2kKO/FfWJyCzYQbIhXUxz7ETt0I/FqHjUeqj37irJ+Dl1ZtU82uYyj14u2XsZKfg==", + "dependencies": { + "@types/whatwg-url": "^11.0.2", + "whatwg-url": "^13.0.0" + } + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "engines": { + "node": ">=6" + } + }, + "node_modules/sparse-bitfield": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/sparse-bitfield/-/sparse-bitfield-3.0.3.tgz", + "integrity": "sha512-kvzhi7vqKTfkh0PZU+2D2PIllw2ymqJKujUcyPMd9Y75Nv4nPbGJZXNhxsgdQab2BmlDct1YnfQCguEvHr7VsQ==", + "dependencies": { + "memory-pager": "^1.0.2" + } + }, + "node_modules/tr46": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", + "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "dependencies": { + "punycode": "^2.3.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/webidl-conversions": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", + "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", + "engines": { + "node": ">=12" + } + }, + "node_modules/whatwg-url": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-13.0.0.tgz", + "integrity": "sha512-9WWbymnqj57+XEuqADHrCJ2eSXzn8WXIW/YSGaZtb2WKAInQ6CHfaUUcTyyver0p8BDg5StLQq8h1vtZuwmOig==", + "dependencies": { + "tr46": "^4.1.1", + "webidl-conversions": "^7.0.0" + }, + "engines": { + "node": ">=16" + } + } + } +} diff --git a/node/query-mongo-atlas/package.json b/node/query-mongo-atlas/package.json new file mode 100644 index 00000000..26f3dc94 --- /dev/null +++ b/node/query-mongo-atlas/package.json @@ -0,0 +1,16 @@ +{ + "name": "query-mongo-atlas", + "version": "1.0.0", + "main": "src/main.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "mongodb": "^6.6.2" + } +} diff --git a/node/query-mongo-atlas/src/main.js b/node/query-mongo-atlas/src/main.js new file mode 100644 index 00000000..3cdfe91e --- /dev/null +++ b/node/query-mongo-atlas/src/main.js @@ -0,0 +1,21 @@ +import { throwIfMissing } from "./utils.js"; +import { getClient, insertTestData, listWarehouses } from "./mongo.js"; + +/** + * Global connection. Reused between executions + */ +let client; + +export default async ({ req, res, log, error }) => { + throwIfMissing(process.env, ['MONGO_URI']); + + if (!client) { + client = await getClient(); + } + + await insertTestData(client); + + const warehouses = await listWarehouses(client); + + return res.json(warehouses); +} \ No newline at end of file diff --git a/node/query-mongo-atlas/src/mongo.js b/node/query-mongo-atlas/src/mongo.js new file mode 100644 index 00000000..ff45d38f --- /dev/null +++ b/node/query-mongo-atlas/src/mongo.js @@ -0,0 +1,46 @@ +import { MongoClient, ServerApiVersion } from 'mongodb'; + +/** + * Throws an error if any of the keys are missing from the object + */ +export async function getClient() { + const client = new MongoClient(process.env.MONGO_URI, { + serverApi: { + version: ServerApiVersion.v1, + strict: true, + deprecationErrors: true, + } + }); + + await client.connect(); + + return client; +} + +/** + * Insert a sample warehouse + */ +export async function insertTestData(client) { + const location = `Street ${Math.round(Math.random() * 1000)}, Earth`; // Random address + const capacity = 10 + Math.round(Math.random() * 10) * 10; // Random number: 10,20,30,...,90,100 + + await client.db("main").collection("warehouses").insertOne({ + location, + capacity + }); +} + +/** + * Get all warehouses + */ +export async function listWarehouses(client, page = 1) { + const limit = 100; + const cursor = client.db("main").collection("warehouses").find().limit(limit).skip((page - 1) * limit); + + const documents = []; + for await (const doc of cursor) { + documents.push(doc); + } + + return documents; +} \ No newline at end of file diff --git a/node/query-mongo-atlas/src/utils.js b/node/query-mongo-atlas/src/utils.js new file mode 100644 index 00000000..a3d1cadf --- /dev/null +++ b/node/query-mongo-atlas/src/utils.js @@ -0,0 +1,14 @@ +/** + * Throws an error if any of the keys are missing from the object + */ +export function throwIfMissing(obj, keys) { + const missing = []; + for (let key of keys) { + if (!(key in obj) || !obj[key]) { + missing.push(key); + } + } + if (missing.length > 0) { + throw new Error(`Missing required fields: ${missing.join(', ')}`); + } + } \ No newline at end of file diff --git a/node/query-neo4j-auradb/README.md b/node/query-neo4j-auradb/README.md new file mode 100644 index 00000000..91a92858 --- /dev/null +++ b/node/query-neo4j-auradb/README.md @@ -0,0 +1,147 @@ +# 🗄️ Node.js Query Neo4j Autota DB Function + +A function to store and query warehouses, categories, and relations between them. + +## 🧰 Usage + +### GET / + +- Create relation between a warehouse and category, and query to get the graph of all nodes and relations. + +**Response** + +Sample `200` Response: + +```js +{ + "keys": [ + "w", + "c" + ], + "records": [ + { + "keys": [ + "w", + "c" + ], + "length": 2, + "_fields": [ + { + "identity": { + "low": 1, + "high": 0 + }, + "labels": [ + "Warehouse" + ], + "properties": { + "location": "Street 331, Earth", + "capacity": 80 + }, + "elementId": "4:7facff38-9bb1-4e6f-825c-b173336d1202:1" + }, + { + "identity": { + "low": 0, + "high": 0 + }, + "labels": [ + "Category" + ], + "properties": { + "name": "electronics" + }, + "elementId": "4:7facff38-9bb1-4e6f-825c-b173336d1202:0" + } + ], + "_fieldLookup": { + "w": 0, + "c": 1 + } + }, + { + "keys": [ + "w", + "c" + ], + "length": 2, + "_fields": [ + { + "identity": { + "low": 9, + "high": 0 + }, + "labels": [ + "Warehouse" + ], + "properties": { + "location": "Street 788, Earth", + "capacity": 20 + }, + "elementId": "4:7facff38-9bb1-4e6f-825c-b173336d1202:9" + }, + { + "identity": { + "low": 0, + "high": 0 + }, + "labels": [ + "Category" + ], + "properties": { + "name": "electronics" + }, + "elementId": "4:7facff38-9bb1-4e6f-825c-b173336d1202:0" + } + ], + "_fieldLookup": { + "w": 0, + "c": 1 + } + }, + // ... + ], + "summary": { + // ... + } +} +``` + +## ⚙️ Configuration + +| Setting | Value | +| ----------------- | ------------- | +| Runtime | Node (18.0) | +| Entrypoint | `src/main.js` | +| Build Commands | `npm install` | +| Permissions | `any` | +| Timeout (Seconds) | 15 | + +## 🔒 Environment Variables + +### NEO4J_URI + +The endpoint to connect to your Neo4j database. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `neo4j+s://4tg4mddo.databases.neo4j.io` | + +### NEO4J_PASSWORD + +Authentication password to access your Neo4j database. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `mCUc4PbVUQN-_NkTLJLisb6ccnwzQKKhrkF77YMctzx` | + +### NEO4J_USER + +Authentication user to access your Neo4j database. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `neo4j` | diff --git a/node/query-neo4j-auradb/package-lock.json b/node/query-neo4j-auradb/package-lock.json new file mode 100644 index 00000000..de5e68b9 --- /dev/null +++ b/node/query-neo4j-auradb/package-lock.json @@ -0,0 +1,142 @@ +{ + "name": "query-neo4j-auradb", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "query-neo4j-auradb", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "neo4j-driver": "^5.20.0" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/neo4j-driver": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/neo4j-driver/-/neo4j-driver-5.20.0.tgz", + "integrity": "sha512-VCH/ay+fmaGkl5wr6lnDZE/QLFDXzamCtF22QMjPHAcBuwee9PWq0ZbtRlkUfpYVCduWm5HjSNBbNu8SZmUn+A==", + "dependencies": { + "neo4j-driver-bolt-connection": "5.20.0", + "neo4j-driver-core": "5.20.0", + "rxjs": "^7.8.1" + } + }, + "node_modules/neo4j-driver-bolt-connection": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/neo4j-driver-bolt-connection/-/neo4j-driver-bolt-connection-5.20.0.tgz", + "integrity": "sha512-jSF2S/kBJBKGCUsOA0z4HFffVKQnHivSQagU1+lQH2fN2WIWy8iJIBngZWkvUaFIvE54a++WCVjE28CAtyzmPw==", + "dependencies": { + "buffer": "^6.0.3", + "neo4j-driver-core": "5.20.0", + "string_decoder": "^1.3.0" + } + }, + "node_modules/neo4j-driver-core": { + "version": "5.20.0", + "resolved": "https://registry.npmjs.org/neo4j-driver-core/-/neo4j-driver-core-5.20.0.tgz", + "integrity": "sha512-TJx4MyKPGw+ocev8mo+3MPI4tt7KirHPM/c3Oa6X8s4jvCKucoslld5MoBYRfBXeqqVZL4FfWoFP1pOn9U0HuQ==" + }, + "node_modules/rxjs": { + "version": "7.8.1", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz", + "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==", + "dependencies": { + "tslib": "^2.1.0" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==" + } + } +} diff --git a/node/query-neo4j-auradb/package.json b/node/query-neo4j-auradb/package.json new file mode 100644 index 00000000..9981ebee --- /dev/null +++ b/node/query-neo4j-auradb/package.json @@ -0,0 +1,16 @@ +{ + "name": "query-neo4j-auradb", + "version": "1.0.0", + "main": "src/main.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "neo4j-driver": "^5.20.0" + } +} diff --git a/node/query-neo4j-auradb/src/main.js b/node/query-neo4j-auradb/src/main.js new file mode 100644 index 00000000..7da14bfa --- /dev/null +++ b/node/query-neo4j-auradb/src/main.js @@ -0,0 +1,25 @@ +import { throwIfMissing } from "./utils.js"; +import { getClient, insertTestData, listWarehouses } from "./neo4j.js"; + +/** + * Global connection. Reused between executions + */ +let client; + +export default async ({ req, res, log, error }) => { + throwIfMissing(process.env, [ + 'NEO4J_URI', + 'NEO4J_USER', + 'NEO4J_PASSWORD', + ]); + + if (!client) { + client = await getClient(); + } + + await insertTestData(client); + + const warehouses = await listWarehouses(client); + + return res.json(warehouses); +} \ No newline at end of file diff --git a/node/query-neo4j-auradb/src/neo4j.js b/node/query-neo4j-auradb/src/neo4j.js new file mode 100644 index 00000000..1017f0b5 --- /dev/null +++ b/node/query-neo4j-auradb/src/neo4j.js @@ -0,0 +1,52 @@ +import * as neo4j from 'neo4j-driver'; + +/** + * Throws an error if any of the keys are missing from the object + */ +export async function getClient() { + const { NEO4J_URI, NEO4J_USER, NEO4J_PASSWORD } = process.env; + return neo4j.default.driver(NEO4J_URI, neo4j.default.auth.basic(NEO4J_USER, NEO4J_PASSWORD)) +} + +/** + * Insert a sample warehouse + */ +export async function insertTestData(client) { + const categories = [ 'electronics', 'fashion', 'furniture', 'cars' ]; + + const category = categories[Math.floor(Math.random() * categories.length)]; // Random category + const location = `Street ${Math.round(Math.random() * 1000)}, Earth`; // Random address + const capacity = 10 + Math.round(Math.random() * 10) * 10; // Random number: 10,20,30,...,90,100 + + await client.executeQuery( + ` + MERGE (c:Category {name: $category}) + CREATE (w:Warehouse {location: $location, capacity: $capacity}) + CREATE (w)-[:STORES]->(c) + `, + { location, capacity, category }, + { database: 'neo4j' } + ); +} + +/** + * Get all warehouses + */ +export async function listWarehouses(client, page = 1) { + const limit = 100; + const offset = (page - 1) * limit; + + return await client.executeQuery( + ` + MATCH (w:Warehouse)-[:STORES]->(c:Category) + RETURN w, c + SKIP $offset + LIMIT $limit + `, + { + limit: neo4j.default.int(limit), + offset: neo4j.default.int(offset) + }, + { database: 'neo4j' } + ); +} \ No newline at end of file diff --git a/node/query-neo4j-auradb/src/utils.js b/node/query-neo4j-auradb/src/utils.js new file mode 100644 index 00000000..a3d1cadf --- /dev/null +++ b/node/query-neo4j-auradb/src/utils.js @@ -0,0 +1,14 @@ +/** + * Throws an error if any of the keys are missing from the object + */ +export function throwIfMissing(obj, keys) { + const missing = []; + for (let key of keys) { + if (!(key in obj) || !obj[key]) { + missing.push(key); + } + } + if (missing.length > 0) { + throw new Error(`Missing required fields: ${missing.join(', ')}`); + } + } \ No newline at end of file diff --git a/node/query-neon-postgres/README.md b/node/query-neon-postgres/README.md new file mode 100644 index 00000000..d7273131 --- /dev/null +++ b/node/query-neon-postgres/README.md @@ -0,0 +1,86 @@ +# 🗄️ Node.js Query Neon Postgres Function + +A function to store warehouses to SQL database, and query to list them. + +## 🧰 Usage + +### GET / + +- Save one warehouse and query to return all of them. + +**Response** + +Sample `200` Response: + +```js +[ + { + "id": 1, + "location": "Street 108, Earth", + "capacity": 50 + }, + { + "id": 2, + "location": "Street 675, Earth", + "capacity": 70 + }, + // ... +] +``` + +## ⚙️ Configuration + +| Setting | Value | +| ----------------- | ------------- | +| Runtime | Node (18.0) | +| Entrypoint | `src/main.js` | +| Build Commands | `npm install` | +| Permissions | `any` | +| Timeout (Seconds) | 15 | + +## 🔒 Environment Variables + +### PGHOST + +The endpoint to connect to your Postgres database. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `ep-still-sea-a792sh84.eu-central-1.aws.neon.tech` | + +### PGDATABASE + +Name of our Postgres database. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `main` | + +### PGUSER + +Name of our Postgres user for authentication. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `main_owner` | + +### PGPASSWORD + +Password of our Postgres user for authentication. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `iQCfaUaaWB3B` | + +### ENDPOINT_ID + +Endpoint ID provided for your Postgres database. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `ep-still-sea-a792sh84` | diff --git a/node/query-neon-postgres/package-lock.json b/node/query-neon-postgres/package-lock.json new file mode 100644 index 00000000..e960e905 --- /dev/null +++ b/node/query-neon-postgres/package-lock.json @@ -0,0 +1,28 @@ +{ + "name": "query-neon-postgres", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "query-neon-postgres", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "postgres": "^3.4.4" + } + }, + "node_modules/postgres": { + "version": "3.4.4", + "resolved": "https://registry.npmjs.org/postgres/-/postgres-3.4.4.tgz", + "integrity": "sha512-IbyN+9KslkqcXa8AO9fxpk97PA4pzewvpi2B3Dwy9u4zpV32QicaEdgmF3eSQUzdRk7ttDHQejNgAEr4XoeH4A==", + "engines": { + "node": ">=12" + }, + "funding": { + "type": "individual", + "url": "https://github.com/sponsors/porsager" + } + } + } +} diff --git a/node/query-neon-postgres/package.json b/node/query-neon-postgres/package.json new file mode 100644 index 00000000..9ef5a8d2 --- /dev/null +++ b/node/query-neon-postgres/package.json @@ -0,0 +1,16 @@ +{ + "name": "query-neon-postgres", + "version": "1.0.0", + "main": "src/main.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "postgres": "^3.4.4" + } +} diff --git a/node/query-neon-postgres/src/main.js b/node/query-neon-postgres/src/main.js new file mode 100644 index 00000000..e5d3f7cb --- /dev/null +++ b/node/query-neon-postgres/src/main.js @@ -0,0 +1,37 @@ +import { throwIfMissing } from "./utils.js"; +import { getClient, insertTestData, listWarehouses, prepareTables } from "./neon.js"; + +/** + * Global connection. Reused between executions + */ +let client; + +/** + * Global cache to improve performance. Skip preparations if not needed + */ +let schemaPrepared = false; + +export default async ({ req, res, log, error }) => { + throwIfMissing(process.env, [ + 'PGHOST', + 'PGDATABASE', + 'PGUSER', + 'PGPASSWORD', + 'ENDPOINT_ID' + ]); + + if (!client) { + client = await getClient(); + } + + if (!schemaPrepared) { + await prepareTables(client); + schemaPrepared = true; + } + + await insertTestData(client); + + const warehouses = await listWarehouses(client); + + return res.json(warehouses); +} \ No newline at end of file diff --git a/node/query-neon-postgres/src/neon.js b/node/query-neon-postgres/src/neon.js new file mode 100644 index 00000000..5fd97cbf --- /dev/null +++ b/node/query-neon-postgres/src/neon.js @@ -0,0 +1,48 @@ +import postgres from 'postgres'; + +/** + * Throws an error if any of the keys are missing from the object + */ +export async function getClient() { + const { PGHOST, PGDATABASE, PGUSER, PGPASSWORD, ENDPOINT_ID } = process.env; + + return postgres({ + host: PGHOST, + database: PGDATABASE, + username: PGUSER, + password: PGPASSWORD, + port: 5432, + ssl: 'require', + connection: { + options: `project=${ENDPOINT_ID}`, + }, + }); +} + +/** + * Ensure table of warehouses exist + */ +export async function prepareTables(client) { + return await client`CREATE TABLE IF NOT EXISTS warehouses ( + id SERIAL PRIMARY KEY, + location VARCHAR(255), + capacity INT + );`; +} + +/** + * Insert a sample warehouse + */ +export async function insertTestData(client) { + const location = `Street ${Math.round(Math.random() * 1000)}, Earth`; // Random address + const capacity = 10 + Math.round(Math.random() * 10) * 10; // Random number: 10,20,30,...,90,100 + await client`INSERT INTO warehouses (location, capacity) VALUES (${location}, ${capacity});`; +} + +/** + * Get all warehouses + */ +export async function listWarehouses(client, page = 1) { + const limit = 100; + return await client`SELECT * FROM warehouses LIMIT ${limit} OFFSET ${(page - 1) * limit}`; +} \ No newline at end of file diff --git a/node/query-neon-postgres/src/utils.js b/node/query-neon-postgres/src/utils.js new file mode 100644 index 00000000..a3d1cadf --- /dev/null +++ b/node/query-neon-postgres/src/utils.js @@ -0,0 +1,14 @@ +/** + * Throws an error if any of the keys are missing from the object + */ +export function throwIfMissing(obj, keys) { + const missing = []; + for (let key of keys) { + if (!(key in obj) || !obj[key]) { + missing.push(key); + } + } + if (missing.length > 0) { + throw new Error(`Missing required fields: ${missing.join(', ')}`); + } + } \ No newline at end of file diff --git a/node/query-redis-labs/README.md b/node/query-redis-labs/README.md new file mode 100644 index 00000000..80494283 --- /dev/null +++ b/node/query-redis-labs/README.md @@ -0,0 +1,50 @@ +# 🗄️ Node.js Query Redis Labs Function + +A function to store warehouse to key-value database, and query to retrieve it. + +## 🧰 Usage + +### GET / + +- Set and get a warehouse record. + +**Response** + +Sample `200` Response: + +```js +{ + "location": "Street 49, Earth", + "capacity": "10" +} +``` + +## ⚙️ Configuration + +| Setting | Value | +| ----------------- | ------------- | +| Runtime | Node (18.0) | +| Entrypoint | `src/main.js` | +| Build Commands | `npm install` | +| Permissions | `any` | +| Timeout (Seconds) | 15 | + +## 🔒 Environment Variables + +### REDIS_HOST + +The endpoint to connect to your Redis database. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `redis-13258.c35.eu-central-1-1.ec2.redns.redis-cloud.com` | + +### REDIS_PASSWORD + +Authentication password to access your Redis database. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `efNNehiACfcZiwsTAjcK6xiwPyu6Dpdq` | diff --git a/node/query-redis-labs/package-lock.json b/node/query-redis-labs/package-lock.json new file mode 100644 index 00000000..2bdc1f07 --- /dev/null +++ b/node/query-redis-labs/package-lock.json @@ -0,0 +1,106 @@ +{ + "name": "query-redis-labs", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "query-redis-labs", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "redis": "^4.6.14" + } + }, + "node_modules/@redis/bloom": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", + "integrity": "sha512-HG2DFjYKbpNmVXsa0keLHp/3leGJz1mjh09f2RLGGLQZzSHpkmZWuwJbAvo3QcRY8p80m5+ZdXZdYOSBLlp7Cg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/client": { + "version": "1.5.16", + "resolved": "https://registry.npmjs.org/@redis/client/-/client-1.5.16.tgz", + "integrity": "sha512-X1a3xQ5kEMvTib5fBrHKh6Y+pXbeKXqziYuxOUo1ojQNECg4M5Etd1qqyhMap+lFUOAh8S7UYevgJHOm4A+NOg==", + "dependencies": { + "cluster-key-slot": "1.1.2", + "generic-pool": "3.9.0", + "yallist": "4.0.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@redis/graph": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@redis/graph/-/graph-1.1.1.tgz", + "integrity": "sha512-FEMTcTHZozZciLRl6GiiIB4zGm5z5F3F6a6FZCyrfxdKOhFlGkiAqlexWMBzCi4DcRoyiOsuLfW+cjlGWyExOw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/json": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@redis/json/-/json-1.0.6.tgz", + "integrity": "sha512-rcZO3bfQbm2zPRpqo82XbW8zg4G/w4W3tI7X8Mqleq9goQjAGLL7q/1n1ZX4dXEAmORVZ4s1+uKLaUOg7LrUhw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/search": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/@redis/search/-/search-1.1.6.tgz", + "integrity": "sha512-mZXCxbTYKBQ3M2lZnEddwEAks0Kc7nauire8q20oA0oA/LoA+E/b5Y5KZn232ztPb1FkIGqo12vh3Lf+Vw5iTw==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/@redis/time-series": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@redis/time-series/-/time-series-1.0.5.tgz", + "integrity": "sha512-IFjIgTusQym2B5IZJG3XKr5llka7ey84fw/NOYqESP5WUfQs9zz1ww/9+qoz4ka/S6KcGBodzlCeZ5UImKbscg==", + "peerDependencies": { + "@redis/client": "^1.0.0" + } + }, + "node_modules/cluster-key-slot": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", + "integrity": "sha512-RMr0FhtfXemyinomL4hrWcYJxmX6deFdCxpJzhDttxgO1+bcCnkk+9drydLVDmAMG7NE6aN/fl4F7ucU/90gAA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/generic-pool": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/generic-pool/-/generic-pool-3.9.0.tgz", + "integrity": "sha512-hymDOu5B53XvN4QT9dBmZxPX4CWhBPPLguTZ9MMFeFa/Kg0xWVfylOVNlJji/E7yTZWFd/q9GO5TxDLq156D7g==", + "engines": { + "node": ">= 4" + } + }, + "node_modules/redis": { + "version": "4.6.14", + "resolved": "https://registry.npmjs.org/redis/-/redis-4.6.14.tgz", + "integrity": "sha512-GrNg/e33HtsQwNXL7kJT+iNFPSwE1IPmd7wzV3j4f2z0EYxZfZE7FVTmUysgAtqQQtg5NXF5SNLR9OdO/UHOfw==", + "workspaces": [ + "./packages/*" + ], + "dependencies": { + "@redis/bloom": "1.2.0", + "@redis/client": "1.5.16", + "@redis/graph": "1.1.1", + "@redis/json": "1.0.6", + "@redis/search": "1.1.6", + "@redis/time-series": "1.0.5" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + } + } +} diff --git a/node/query-redis-labs/package.json b/node/query-redis-labs/package.json new file mode 100644 index 00000000..d827cda3 --- /dev/null +++ b/node/query-redis-labs/package.json @@ -0,0 +1,16 @@ +{ + "name": "query-redis-labs", + "version": "1.0.0", + "main": "src/main.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "redis": "^4.6.14" + } +} diff --git a/node/query-redis-labs/src/main.js b/node/query-redis-labs/src/main.js new file mode 100644 index 00000000..09abbdb4 --- /dev/null +++ b/node/query-redis-labs/src/main.js @@ -0,0 +1,21 @@ +import { throwIfMissing } from "./utils.js"; +import { getClient, getWarehouse, insertTestData } from "./redis.js"; + +/** + * Global connection. Reused between executions + */ +let client; + +export default async ({ req, res, log, error }) => { + throwIfMissing(process.env, ['REDIS_HOST', 'REDIS_PASSWORD']); + + if (!client) { + client = await getClient(); + } + + const hash = await insertTestData(client); + + const warehouse = await getWarehouse(client, hash); + + return res.json(warehouse); +} \ No newline at end of file diff --git a/node/query-redis-labs/src/redis.js b/node/query-redis-labs/src/redis.js new file mode 100644 index 00000000..260e3a0d --- /dev/null +++ b/node/query-redis-labs/src/redis.js @@ -0,0 +1,44 @@ +import { createClient } from 'redis'; +import { createHash } from 'node:crypto'; + +/** + * Throws an error if any of the keys are missing from the object + */ +export async function getClient() { + const { REDIS_PASSWORD, REDIS_HOST } = process.env; + + const client = createClient({ + password: REDIS_PASSWORD, + socket: { + host: REDIS_HOST, + port: 14714 + } + }); + + await client.connect(); + + return client; +} + +/** + * Insert a sample warehouse + */ +export async function insertTestData(client) { + const location = `Street ${Math.round(Math.random() * 1000)}, Earth`; // Random address + const capacity = 10 + Math.round(Math.random() * 10) * 10; // Random number: 10,20,30,...,90,100 + + const hash = createHash('md5').update(location).digest('hex'); + await client.hSet(`warehouses:${hash}`, { + location, + capacity + }); + + return hash; +} + +/** + * Get all details about a warehouse + */ +export async function getWarehouse(client, hash) { + return await client.hGetAll(`warehouses:${hash}`); +} \ No newline at end of file diff --git a/node/query-redis-labs/src/utils.js b/node/query-redis-labs/src/utils.js new file mode 100644 index 00000000..a3d1cadf --- /dev/null +++ b/node/query-redis-labs/src/utils.js @@ -0,0 +1,14 @@ +/** + * Throws an error if any of the keys are missing from the object + */ +export function throwIfMissing(obj, keys) { + const missing = []; + for (let key of keys) { + if (!(key in obj) || !obj[key]) { + missing.push(key); + } + } + if (missing.length > 0) { + throw new Error(`Missing required fields: ${missing.join(', ')}`); + } + } \ No newline at end of file diff --git a/node/query-upstash-vector/README.md b/node/query-upstash-vector/README.md new file mode 100644 index 00000000..2f50f5dc --- /dev/null +++ b/node/query-upstash-vector/README.md @@ -0,0 +1,63 @@ +# 🗄️ Node.js Query Upstash Vector Function + +A function to store products to vector database, and query it for similar products. + +## 🧰 Usage + +### GET / + +- Save one product and query for one most similar product. + +**Response** + +Sample `200` Response: + +```js +[ + { + "id": "18f8cb623e5115a26c8", + "score": 0.9219918, + "vector": [ + -0.048450675, + 0.055260602, + -0.02373273 + // ... + ], + "metadata": { + "name": "Product #115", + "category": "electronics", + "price": 90 + } + } +] +``` + +## ⚙️ Configuration + +| Setting | Value | +| ----------------- | ------------- | +| Runtime | Node (18.0) | +| Entrypoint | `src/main.js` | +| Build Commands | `npm install` | +| Permissions | `any` | +| Timeout (Seconds) | 15 | + +## 🔒 Environment Variables + +### UPSTASH_URL + +The endpoint to connect to your Upstash Vector database. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `https://resolved-mallard-84564-eu1-vector.upstash.io` | + +### UPSTASH_TOKEN + +Authentication token to access your Upstash Vector database. + +| Question | Answer | +| ------------ | ------------------------------ | +| Required | Yes | +| Sample Value | `oe4wNTbwHVLcDNa6oceZfhBEABsCNYh43ii6Xdq4bKBH7mq7qJkUmc4cs3ABbYyuVKWZTxVQjiNjYgydn2dkhABNes4NAuDpj7qxUAmZYqGJT78` | diff --git a/node/query-upstash-vector/package-lock.json b/node/query-upstash-vector/package-lock.json new file mode 100644 index 00000000..a1f6e69f --- /dev/null +++ b/node/query-upstash-vector/package-lock.json @@ -0,0 +1,21 @@ +{ + "name": "query-upstash-vector", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "query-upstash-vector", + "version": "1.0.0", + "license": "ISC", + "dependencies": { + "@upstash/vector": "^1.1.1" + } + }, + "node_modules/@upstash/vector": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@upstash/vector/-/vector-1.1.1.tgz", + "integrity": "sha512-40pRY6BYpNElZ0/BFiDk9p1Pn4D+WUM4qm528yYlJm6ifjcg4xQthwiE8+6j2t9rqr3FtWaNhdpLXTyj1ptNLA==" + } + } +} diff --git a/node/query-upstash-vector/package.json b/node/query-upstash-vector/package.json new file mode 100644 index 00000000..0969cc31 --- /dev/null +++ b/node/query-upstash-vector/package.json @@ -0,0 +1,16 @@ +{ + "name": "query-upstash-vector", + "version": "1.0.0", + "main": "src/main.js", + "type": "module", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC", + "description": "", + "dependencies": { + "@upstash/vector": "^1.1.1" + } +} diff --git a/node/query-upstash-vector/src/main.js b/node/query-upstash-vector/src/main.js new file mode 100644 index 00000000..08cdaf33 --- /dev/null +++ b/node/query-upstash-vector/src/main.js @@ -0,0 +1,25 @@ +import { throwIfMissing } from "./utils.js"; +import { getIndex, getRecommendedProduct, insertTestData } from "./upstash.js"; + +/** + * Global index connection. Reused between executions + */ +let index; + +export default async ({ req, res, log, error }) => { + throwIfMissing(process.env, [ + 'UPSTASH_URL', + 'UPSTASH_TOKEN' + ]); + + if (!index) { + index = await getIndex(); + } + + await insertTestData(index); + + const product = `product name Airpods in category electronics priced at 36€`; + const recommended = await getRecommendedProduct(index, product); + + return res.json(recommended); +} \ No newline at end of file diff --git a/node/query-upstash-vector/src/upstash.js b/node/query-upstash-vector/src/upstash.js new file mode 100644 index 00000000..61f4886c --- /dev/null +++ b/node/query-upstash-vector/src/upstash.js @@ -0,0 +1,49 @@ +import { Index } from "@upstash/vector" + +/** + * Throws an error if any of the keys are missing from the object + */ +export async function getIndex() { + const { UPSTASH_URL, UPSTASH_TOKEN } = process.env; + + return new Index({ + url: UPSTASH_URL, + token: UPSTASH_TOKEN + }); +} + +/** + * Insert a sample warehouse + */ +export async function insertTestData(index) { + const categories = ['electronics', 'fashion', 'furniture', 'cars']; + + const category = categories[Math.floor(Math.random() * categories.length)]; // Random category + const name = `Product #${Math.round(Math.random() * 1000)}`; // Random name + const price = 10 + Math.round(Math.random() * 90); // Random number between 10 and 100 + + // Generate ID as combination of time and randomness + const id = `${new Date().getTime().toString(16)}${Math.round(Math.random() * 1000000000).toString(16)}`; + + await index.upsert({ + id, + data: `product name ${name} in category ${category} priced at ${price}€`, + metadata: { + name, + category, + price + }, + }); +} + +/** + * Find related product to original + */ +export async function getRecommendedProduct(index, product) { + return await index.query({ + data: product, + topK: 1, + includeVectors: true, + includeMetadata: true, + }); +} \ No newline at end of file diff --git a/node/query-upstash-vector/src/utils.js b/node/query-upstash-vector/src/utils.js new file mode 100644 index 00000000..a3d1cadf --- /dev/null +++ b/node/query-upstash-vector/src/utils.js @@ -0,0 +1,14 @@ +/** + * Throws an error if any of the keys are missing from the object + */ +export function throwIfMissing(obj, keys) { + const missing = []; + for (let key of keys) { + if (!(key in obj) || !obj[key]) { + missing.push(key); + } + } + if (missing.length > 0) { + throw new Error(`Missing required fields: ${missing.join(', ')}`); + } + } \ No newline at end of file From 11b615bd89e15ebe3d7efa197f26a0407232ec6c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Wed, 22 May 2024 11:30:05 +0200 Subject: [PATCH 2/3] Typo fix --- node/query-neo4j-auradb/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/query-neo4j-auradb/README.md b/node/query-neo4j-auradb/README.md index 91a92858..65329c0e 100644 --- a/node/query-neo4j-auradb/README.md +++ b/node/query-neo4j-auradb/README.md @@ -1,4 +1,4 @@ -# 🗄️ Node.js Query Neo4j Autota DB Function +# 🗄️ Node.js Query Neo4j Aura DB Function A function to store and query warehouses, categories, and relations between them. From 234c8aa953f0fe9bc78eceaa526dff28ced4506c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Matej=20Ba=C4=8Do?= Date: Thu, 23 May 2024 10:52:31 +0000 Subject: [PATCH 3/3] Add 404 responses --- node/query-mongo-atlas/src/main.js | 4 ++++ node/query-neo4j-auradb/src/main.js | 4 ++++ node/query-neon-postgres/src/main.js | 4 ++++ node/query-redis-labs/src/main.js | 4 ++++ node/query-upstash-vector/src/main.js | 4 ++++ 5 files changed, 20 insertions(+) diff --git a/node/query-mongo-atlas/src/main.js b/node/query-mongo-atlas/src/main.js index 3cdfe91e..b8736669 100644 --- a/node/query-mongo-atlas/src/main.js +++ b/node/query-mongo-atlas/src/main.js @@ -9,6 +9,10 @@ let client; export default async ({ req, res, log, error }) => { throwIfMissing(process.env, ['MONGO_URI']); + if(req.method !== 'GET') { + return res.send('Not found.', 404); + } + if (!client) { client = await getClient(); } diff --git a/node/query-neo4j-auradb/src/main.js b/node/query-neo4j-auradb/src/main.js index 7da14bfa..d97e214b 100644 --- a/node/query-neo4j-auradb/src/main.js +++ b/node/query-neo4j-auradb/src/main.js @@ -13,6 +13,10 @@ export default async ({ req, res, log, error }) => { 'NEO4J_PASSWORD', ]); + if(req.method !== 'GET') { + return res.send('Not found.', 404); + } + if (!client) { client = await getClient(); } diff --git a/node/query-neon-postgres/src/main.js b/node/query-neon-postgres/src/main.js index e5d3f7cb..ff4de052 100644 --- a/node/query-neon-postgres/src/main.js +++ b/node/query-neon-postgres/src/main.js @@ -20,6 +20,10 @@ export default async ({ req, res, log, error }) => { 'ENDPOINT_ID' ]); + if(req.method !== 'GET') { + return res.send('Not found.', 404); + } + if (!client) { client = await getClient(); } diff --git a/node/query-redis-labs/src/main.js b/node/query-redis-labs/src/main.js index 09abbdb4..7a30ed2f 100644 --- a/node/query-redis-labs/src/main.js +++ b/node/query-redis-labs/src/main.js @@ -9,6 +9,10 @@ let client; export default async ({ req, res, log, error }) => { throwIfMissing(process.env, ['REDIS_HOST', 'REDIS_PASSWORD']); + if(req.method !== 'GET') { + return res.send('Not found.', 404); + } + if (!client) { client = await getClient(); } diff --git a/node/query-upstash-vector/src/main.js b/node/query-upstash-vector/src/main.js index 08cdaf33..7427998e 100644 --- a/node/query-upstash-vector/src/main.js +++ b/node/query-upstash-vector/src/main.js @@ -12,6 +12,10 @@ export default async ({ req, res, log, error }) => { 'UPSTASH_TOKEN' ]); + if(req.method !== 'GET') { + return res.send('Not found.', 404); + } + if (!index) { index = await getIndex(); }