From aa79f2873f190e3e7073387d0b380e337cd9df37 Mon Sep 17 00:00:00 2001 From: newtbot Date: Wed, 10 Jan 2024 03:30:53 +0800 Subject: [PATCH] a --- Database/mySQL.js | 10 +- Web-Server/functions/APIDatabase.js | 520 ++++++++++++++++++++++++---- Web-Server/routes/Location.js | 3 + Web-Server/routes/Sensor.js | 3 +- Web-Server/routes/SensorData.js | 58 +++- api.MD | 6 +- 6 files changed, 526 insertions(+), 74 deletions(-) diff --git a/Database/mySQL.js b/Database/mySQL.js index d7e9f00..1cf041f 100644 --- a/Database/mySQL.js +++ b/Database/mySQL.js @@ -3,6 +3,7 @@ const path = require('path') require('dotenv').config({ path: path.resolve(__dirname, '../.env') }) const Sequelize = require("sequelize"); const fs = require('fs'); +const { escape } = require("querystring"); const sequelize = new Sequelize( "eco_saver", @@ -11,12 +12,17 @@ const sequelize = new Sequelize( { host: "mpsqldatabase.mysql.database.azure.com", dialect: 'mysql', + // attributeBehavior?: 'escape' | 'throw' | 'unsafe-legacy'; + attributeBehavior: 'escape', dialectOptions: { ssl: { ca: fs.readFileSync(path.resolve(__dirname, '../cert/DigiCertGlobalRootCA.crt.pem')), - } + }, + }, - } + }, + + ); sequelize.authenticate().then(() => { diff --git a/Web-Server/functions/APIDatabase.js b/Web-Server/functions/APIDatabase.js index e884e4f..5a8a3d8 100644 --- a/Web-Server/functions/APIDatabase.js +++ b/Web-Server/functions/APIDatabase.js @@ -174,9 +174,12 @@ async function getSensorDataById(id) { async function getData(query) { let ormQuery = {}; let whereClause = {}; - let whereNest = {}; - let whereDate = {}; - + //let whereNest = {}; + //let whereDate = {}; + //limit default value if query.limit is undefined + if (query.limit === undefined) { + query.limit = 10; + } if (query.limit !== undefined && query.order !== undefined) ormQuery = { limit: parseInt(query.limit), @@ -184,6 +187,9 @@ async function getData(query) { order: [["createdAt", query.order.toUpperCase()]], ...ormQuery, }; + } + + /* //handle year and month and week and day and hour and minute and sensorid and locationid through optional chaining else if ( query.limit || @@ -198,8 +204,6 @@ async function getData(query) { query.locationid || query.startdate || query.enddate - - //get specific data like psi or wtv ) { if (query.limit !== undefined && query.order !== undefined) ormQuery = { @@ -273,25 +277,211 @@ async function getData(query) { } if (query.startdate) { let startdate = new Date(query.startdate); - whereDate.startdate = startdate; + whereClause.startdate = startdate; } if (query.enddate) { let enddate = new Date(query.enddate); - whereDate.enddate = enddate; + whereClause.enddate = enddate; } + //WHERE NEST PREVIOUSLY if (query.sensorid) { - whereNest.sensorid = sequelize.where( + whereClause.sensorid = sequelize.where( sequelize.col("sensorid"), query.sensorid ); } if (query.locationid) { - whereNest.locationid = sequelize.where( + whereClause.locationid = sequelize.where( sequelize.col("locationid"), query.locationid ); } + //highest and lowest + if (query.psi !== undefined && query.psi === "highest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [sequelize.literal("JSON_EXTRACT(measurement, '$.psi')"), "max_psi"], + "createdAt", + ], + //group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_psi"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.psi !== undefined && query.psi === "lowest") { + //https://gist.github.com/WunGCQ/c4df1929c5975c2a79133bc8300fcd6e + + ormQuery = { + attributes: [ + [sequelize.literal("JSON_EXTRACT(measurement, '$.psi')"), "min_psi"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_psi"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.co !== undefined && query.co === "highest") { + ormQuery = { + attributes: [ + [sequelize.literal("JSON_EXTRACT(measurement, '$.co')"), "max_co"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_co"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.co !== undefined && query.co === "lowest") { + ormQuery = { + attributes: [ + [sequelize.literal("JSON_EXTRACT(measurement, '$.co')"), "min_co"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_co"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.o3 !== undefined && query.o3 === "highest") { + ormQuery = { + attributes: [ + [sequelize.literal("JSON_EXTRACT(measurement, '$.o3')"), "max_o3"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_o3"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.o3 !== undefined && query.o3 === "lowest") { + ormQuery = { + attributes: [ + [sequelize.literal("JSON_EXTRACT(measurement, '$.o3')"), "min_o3"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_o3"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.no2 !== undefined && query.no2 === "highest") { + ormQuery = { + attributes: [ + [sequelize.literal("JSON_EXTRACT(measurement, '$.no2')"), "max_no2"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_no2"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.no2 !== undefined && query.no2 === "lowest") { + ormQuery = { + attributes: [ + [sequelize.literal("JSON_EXTRACT(measurement, '$.no2')"), "min_no2"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_no2"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.so2 !== undefined && query.so2 === "highest") { + ormQuery = { + attributes: [ + [sequelize.literal("JSON_EXTRACT(measurement, '$.so2')"), "max_so2"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_so2"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.so2 !== undefined && query.so2 === "lowest") { + ormQuery = { + attributes: [ + [sequelize.literal("JSON_EXTRACT(measurement, '$.so2')"), "min_so2"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_so2"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.humidity !== undefined && query.humidity === "highest") { + ormQuery = { + attributes: [ + [ + sequelize.literal("JSON_EXTRACT(measurement, '$.humidity')"), + "max_humidity", + ], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_humidity"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.humidity !== undefined && query.humidity === "lowest") { + ormQuery = { + attributes: [ + [ + sequelize.literal("JSON_EXTRACT(measurement, '$.humidity')"), + "min_humidity", + ], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_humidity"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.windspeed !== undefined && query.windspeed === "highest") { + ormQuery = { + attributes: [ + [ + sequelize.literal("JSON_EXTRACT(measurement, '$.windspeed')"), + "max_windspeed", + ], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_windspeed"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.windspeed !== undefined && query.windspeed === "lowest") { + ormQuery = { + attributes: [ + [ + sequelize.literal("JSON_EXTRACT(measurement, '$.windspeed')"), + "min_windspeed", + ], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_windspeed"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.temperature !== undefined && query.temperature === "highest") { + ormQuery = { + attributes: [ + [ + sequelize.literal("JSON_EXTRACT(measurement, '$.temperature')"), + "max_temperature", + ], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_temperature"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.temperature !== undefined && query.temperature === "lowest") { + ormQuery = { + attributes: [ + [ + sequelize.literal("JSON_EXTRACT(measurement, '$.temperature')"), + "min_temperature", + ], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_temperature"), "ASC"]], + limit: parseInt(query.limit), + }; + } } //accept year else if (query.year !== undefined) { @@ -396,21 +586,24 @@ async function getData(query) { if (!whereClause) { return await sensorDataModel.findAll(ormQuery); - } else if (whereClause && whereNest) { + } else if (whereClause) { console.log(whereClause); - console.log(whereNest); - console.log(whereDate); + //console.log(whereNest); + //console.log(whereDate); console.log(query); console.log(ormQuery); return await sensorDataModel.findAll({ + //limit default value if query.limit is undefined or not provided + limit: query.limit, //The operators Op.and, Op.or and Op.not can be used to create arbitrarily complex nested logical comparisons. //https://sequelize.org/docs/v6/core-concepts/model-querying-basics/#examples-with-opand-and-opor where: { - [Op.and]: [whereNest, whereClause], + //[Op.and]: [whereNest, whereClause], + [Op.and]: [whereClause], createdAt: { //https://stackoverflow.com/questions/43115151/sequelize-query-to-find-all-records-that-falls-in-between-date-range - [Op.between]: [whereDate.startdate, whereDate.enddate], + [Op.between]: [whereClause.startdate, whereClause.enddate], }, }, @@ -419,59 +612,269 @@ async function getData(query) { }); } } - -async function getdataFilter(query) { - let ormQuery = {}; - //get highest and lowest data of measurement from all data - if (query.psi !== undefined && query.psi === "highest") { - ormQuery = { - where: sequelize.where( - sequelize.fn("JSON_EXTRACT", sequelize.col("measurement"), "$.psi"), - //sequelize.fn('JSON_EXTRACT', sequelize.col('aa.bb.field'), sequelize.literal(`'$.attr'`)) - sequelize.literal(`'$$.type'`), - - //max value of psi - sequelize.fn("MAX", sequelize.col("measurement.psi")) - ), - }; - } - - console.log(ormQuery); - return await sensorDataModel.findAll(ormQuery); -} +*/ +//if no query is provided /* -sequelize.fn('JSON_EXTRACT', sequelize.col('aa.bb.field'), sequelize.literal(`'$.attr'`)) - - else if (query.hour !== undefined) { - ormQuery = { - where: sequelize.where( - sequelize.fn("HOUR", sequelize.col("createdAt")), - query.hour - ), - ...ormQuery, - }; +async function getdataFilter(query) { + let allowWords = ["highest", "lowest", "Highest", "Lowest"]; + let ormQuery = {}; + //preset limit if limit is not provided + if (query.limit === undefined) { + query.limit = 10; } - - - -*/ - -/*' ormQuery = { - order: [sequelize.fn("max", sequelize.col("measurement.psi::int"))], - limit: 1, - }; - sequelize.literal('(SELECT MAX(`measurement`->>"$.psi") FROM `sensorData`)'), - 'max_psi' - if (query.highest) { - //[sequelize.fn("max", sequelize.col("measurement.psi")), "max_psi"], + if ( + allowWords.includes(query.psi) || + allowWords.includes(query.co) || + allowWords.includes(query.o3) || + allowWords.includes(query.no2) || + allowWords.includes(query.so2) || + allowWords.includes(query.humidity) || + allowWords.includes(query.windspeed) || + allowWords.includes(query.temperature) + ) { + //get highest and lowest data of measurement from all data + if (query.psi !== undefined && query.psi === "highest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [sequelize.literal("JSON_EXTRACT(measurement, '$.psi')"), "max_psi"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_psi"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.psi !== undefined && query.psi === "lowest") { + //https://gist.github.com/WunGCQ/c4df1929c5975c2a79133bc8300fcd6e ormQuery = { attributes: [ - [sequelize.fn("max", sequelize.col("measurement")), "max"], + "id", + "sensorid", + "locationid", + [sequelize.literal("JSON_EXTRACT(measurement, '$.psi')"), "min_psi"], ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_psi"), "ASC"]], + limit: parseInt(query.limit), }; } + if (query.co !== undefined && query.co === "highest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [sequelize.literal("JSON_EXTRACT(measurement, '$.co')"), "max_co"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_co"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.co !== undefined && query.co === "lowest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [sequelize.literal("JSON_EXTRACT(measurement, '$.co')"), "min_co"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_co"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.o3 !== undefined && query.o3 === "highest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [sequelize.literal("JSON_EXTRACT(measurement, '$.o3')"), "max_o3"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_o3"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.o3 !== undefined && query.o3 === "lowest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [sequelize.literal("JSON_EXTRACT(measurement, '$.o3')"), "min_o3"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_o3"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.no2 !== undefined && query.no2 === "highest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [sequelize.literal("JSON_EXTRACT(measurement, '$.no2')"), "max_no2"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_no2"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.no2 !== undefined && query.no2 === "lowest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [sequelize.literal("JSON_EXTRACT(measurement, '$.no2')"), "min_no2"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_no2"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.so2 !== undefined && query.so2 === "highest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [sequelize.literal("JSON_EXTRACT(measurement, '$.so2')"), "max_so2"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_so2"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.so2 !== undefined && query.so2 === "lowest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [sequelize.literal("JSON_EXTRACT(measurement, '$.so2')"), "min_so2"], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_so2"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.humidity !== undefined && query.humidity === "highest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [ + sequelize.literal("JSON_EXTRACT(measurement, '$.humidity')"), + "max_humidity", + ], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_humidity"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.humidity !== undefined && query.humidity === "lowest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [ + sequelize.literal("JSON_EXTRACT(measurement, '$.humidity')"), + "min_humidity", + ], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_humidity"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.windspeed !== undefined && query.windspeed === "highest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [ + sequelize.literal("JSON_EXTRACT(measurement, '$.windspeed')"), + "max_windspeed", + ], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_windspeed"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.windspeed !== undefined && query.windspeed === "lowest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [ + sequelize.literal("JSON_EXTRACT(measurement, '$.windspeed')"), + "min_windspeed", + ], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_windspeed"), "ASC"]], + limit: parseInt(query.limit), + }; + } + if (query.temperature !== undefined && query.temperature === "highest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [ + sequelize.literal("JSON_EXTRACT(measurement, '$.temperature')"), + "max_temperature", + ], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("max_temperature"), "DESC"]], + limit: parseInt(query.limit), + }; + } + if (query.temperature !== undefined && query.temperature === "lowest") { + ormQuery = { + attributes: [ + "id", + "sensorid", + "locationid", + [ + sequelize.literal("JSON_EXTRACT(measurement, '$.temperature')"), + "min_temperature", + ], + ], + group: ["id", "sensorid", "locationid"], + order: [[sequelize.literal("min_temperature"), "ASC"]], + limit: parseInt(query.limit), + }; + } + + console.log(ormQuery); + console.log(query); + return await sensorDataModel.findAll(ormQuery); + } else { + return "Please enter correct query or Please provide a query"; + } +} +*/ +async function getAverage(query) {} + +/* + */ @@ -493,4 +896,5 @@ module.exports = { getSensorDataById, getData, getdataFilter, + getAverage, }; diff --git a/Web-Server/routes/Location.js b/Web-Server/routes/Location.js index 4232017..d34f344 100644 --- a/Web-Server/routes/Location.js +++ b/Web-Server/routes/Location.js @@ -22,6 +22,7 @@ router.get("/", async (req, res, next) => { next(error); } }); +/* //add location router.post("/new", async (req, res, next) => { @@ -59,6 +60,8 @@ router.delete("/delete", async (req, res, next) => { } }); +*/ + //get location by id router.get("/:id", async (req, res, next) => { try { diff --git a/Web-Server/routes/Sensor.js b/Web-Server/routes/Sensor.js index 7e157c1..3444cea 100644 --- a/Web-Server/routes/Sensor.js +++ b/Web-Server/routes/Sensor.js @@ -20,6 +20,7 @@ router.get("/", async (req, res, next) => { next(error); } }); +/* router.post("/new", async (req, res, next) => { try { @@ -53,7 +54,7 @@ router.delete("/delete", async (req, res, next) => { next(error); } }); - +*/ router.get("/:id", async (req, res, next) => { try { const sensor = await getSensorById(req.params.id); diff --git a/Web-Server/routes/SensorData.js b/Web-Server/routes/SensorData.js index 6e897e8..4a658c2 100644 --- a/Web-Server/routes/SensorData.js +++ b/Web-Server/routes/SensorData.js @@ -8,6 +8,7 @@ const { getSensorDataById, getData, getdataFilter, + getAverage, } = require("../functions/apiDatabase.js"); const express = require("express"); @@ -23,7 +24,7 @@ router.get("/", async (req, res, next) => { next(error); } }); - +/* router.post("/new", async (req, res, next) => { try { const { id, id_sensor, id_location, sensordata } = req.body; @@ -56,7 +57,7 @@ router.delete("/delete", async (req, res, next) => { next(error); } }); - +*/ router.get("/data", async (req, res, next) => { try { let query = { @@ -94,14 +95,17 @@ router.get("/data", async (req, res, next) => { //end date enddate: req.query.enddate, - /* - //highest or lowest of psi, co, o3, no2, so2, humidity, windspeed, temperature - highest: req.query.highest, //highest or lowest of psi, co, o3, no2, so2, humidity, windspeed, temperature - lowest: req.query.lowest, + psi: req.query.psi, + co: req.query.co, + o3: req.query.o3, + no2: req.query.no2, + so2: req.query.so2, + humidity: req.query.humidity, + windspeed: req.query.windspeed, + temperature: req.query.temperature, - */ }; const data = await getData(query); @@ -116,6 +120,7 @@ router.get("/data", async (req, res, next) => { router.get("/filter", async (req, res, next) => { try { const query = { + limit: req.query.limit, psi: req.query.psi, co: req.query.co, o3: req.query.o3, @@ -124,6 +129,8 @@ router.get("/filter", async (req, res, next) => { humidity: req.query.humidity, windspeed: req.query.windspeed, temperature: req.query.temperature, + + //sensorid and locationid }; const data = await getdataFilter(query); res.status(200).json(data); @@ -134,6 +141,39 @@ router.get("/filter", async (req, res, next) => { }); +//average +router.get("/average", async (req, res, next) => { + try { + const query = { + psi: req.query.psi, + co: req.query.co, + o3: req.query.o3, + no2: req.query.no2, + so2: req.query.so2, + humidity: req.query.humidity, + windspeed: req.query.windspeed, + temperature: req.query.temperature, + //daily + day: req.query.day, + //hourly + hour: req.query.hour, + //weekly + week: req.query.week, + //monthly + month: req.query.month, + //yearly + year: req.query.year, + }; + const data = await getAverage(query); + + res.status(200).json(data); + + } + catch (error) { + console.error(error); + next(error); + } +}); router.get("/:id", async (req, res, next) => { try { @@ -153,11 +193,7 @@ module.exports = router; - **Query Parameter:** `metric` (e.g., `psi`, `co`, `o3`) - **Description:** Calculate aggregate metrics for the specified parameter. -**Filter Sensor Data by Measurement Values:** -- **Route:** `GET /api/v0/sensor-data/filter` -- **Query Parameters:** `min_psi`, `max_psi`, `min_co`, `max_co`, etc. temp and humidity too -- **Description:** Retrieve sensor data entries within specified measurement value ranges. */ /* diff --git a/api.MD b/api.MD index 0dc319a..30e2647 100644 --- a/api.MD +++ b/api.MD @@ -152,7 +152,9 @@ Hour = 1 or wtv //strong optional chaining curl 'http://localhost/api/v0/sensor-data/data?year=2023&month=1&week=1&day=1&sensorid=1&locationid=1' - http://localhost/api/v0/sensor-data/data?startdate=2023-01-01T00:00:00.000&enddate=2023-01-03T&sensorid=1&locationid=1 -//get specific data \ No newline at end of file +//get specific data +http://localhost/api/v0/sensor-data/filter?windspeed=highest&limit=1 + +//average \ No newline at end of file