简体   繁体   English

如何在单个查询中返回node.js mysql中的嵌套json

[英]How to return a nested json in node.js mysql in a single query

I'm trying to create an api that will return a nested json, coming from two related tables student and studentSubjects 我正在尝试创建一个api,它将返回一个嵌套的json,来自两个相关的表student和studentSubjects

[{
   id:"1",
   name: "John",
   subjects: [{
                id:"1",
                subject: "Math"
              },
              {
                id:"2",
                subject: "English"
              }
             ]
 },
 {
   id:"2",
   name: "Peter",
   subjects: [{
                id:"1",
                subject: "Math"
              },
              {
                id:"2",
                subject: "English"
              }
             ]
}]

My code looks like this: 我的代码看起来像这样:

this.get = function(res){
    db.acquire(function(err, con){                  
        con.query('SELECT * FROM students', function(err, results){ 

            if (err){                   
                res.send({status: 0, message: 'Database error'});
            }else{                  
                res.send({status: 1, data: results});                   
            }                                       
        })  
        con.release()           
    })
}

I know the query should have joins, but it only returns single row. 我知道查询应该有连接,但它只返回单行。 I tried also to make a loop, it won't work because its async 我也尝试做一个循环,它不会工作,因为它是异步的

Thanks for your help!! 谢谢你的帮助!!

You cannot create a nested JSON from a MySQL query because it will always return a flat result. 您无法从MySQL查询创建嵌套JSON,因为它始终返回平坦结果。

Anyway, to create a nested JSON you should create multiple queries and insert the corresponding array object where needed. 无论如何,要创建嵌套的JSON,您应该创建多个查询并在需要的地方插入相应的数组对象。

You should really consider using Promises for creating nested queries because it will allow you to make asynchronous operations back to back. 您应该考虑使用Promises来创建嵌套查询,因为它允许您背对背进行异步操作。 Below code will also close the connection if an error occurs in any of the queries. 如果在任何查询中发生错误,下面的代码也将关闭连接。

PS: I explained each step in the comments in the code below PS:我在下面的代码中解释了评论中的每一步

Imagine having a database called 'School' and three tables called 'Student', 'Subject' and 'Link_student_subject'. 想象一下,有一个名为'School'的数据库和三个名为'Student','Subject'和'Link_student_subject'的表。

// Instantiate mysql datase variables
const mysql = require( 'mysql' );
const config = {
    host     : 'localhost',
    user     : 'root',
    password : 'root',
    database : 'school'
  }
var connection;

// Instantiate express routing variables 
const express = require('express');
const router = express.Router();
module.exports = router;

// Wrapper class for MySQL client
// - Constructor creates MySQL connection
// - Connection opened when query is done
// - Promise is resolved when executing
// - Promise returns reject in case of error
// - If promise resolved rows will be the result
class Database {
    constructor( config ) {
        this.connection = mysql.createConnection( config );
    }
    query( sql, args ) {
        return new Promise( ( resolve, reject ) => {
            this.connection.query( sql, args, ( err, rows ) => {
                if ( err )
                    return reject( err );
                resolve( rows );
            } );
        } );
    }
    close() {
        return new Promise( ( resolve, reject ) => {
            this.connection.end( err => {
                if ( err )
                    return reject( err );
                resolve();
            } );
        } );
    }
}

// Function that will execute a query
// - In case of an error: ensure connection is always closed
// - In case of succes: return result and close connection afterwards
Database.execute = function( config, callback ) {
  const database = new Database( config );
  return callback( database ).then(
      result => database.close().then( () => result ),
      err => database.close().then( () => { throw err; } )
  );
};

// Instantiate Database
var database = new Database(config);

// Express routing
router.get('/students', function (req, res) {

  // Variables - Rows from Students & Subjects & Link_student_subject
  let rows_Students, rows_Subjects, rows_Link_Student_Subject;

  // Create a Promise chain by
  // executing two or more asynchronous operations back to back, 
  // where each subsequent operation starts when the previous operation succeeds, 
  // with the result from the previous step
  Database.execute( config,
      database => database.query( "select a.*, null as subjects from student a" )
      .then( rows => {
        rows_Students = rows;
        return database.query( "select * from subject" )
      } )
      .then( rows => {
        rows_Subjects = rows;
        return database.query( "select * from link_student_subject" )
      } )
      .then( rows => {
        rows_Link_Student_Subject = rows;
      } )
  ).then( () => {
      // Create your nested student JSON by looping on Students
      // and inserting the corresponding Subjects array
      for (let i = 0; i < rows_Students.length; i++) {
        let arraySubjects = [];
        for (let x = 0; x < rows_Link_Student_Subject.length; x++) {
            if(rows_Students[i].id == rows_Link_Student_Subject[x].id_student){
                arraySubjects.push(searchObjInArray(rows_Subjects, "id", rows_Link_Student_Subject[x].id_subject));
            }
        }
        rows_Students[i].subjects = arraySubjects;
      }
      res.send(JSON.stringify(rows_Students));
  } ).catch( err => {
      // handle the error
      res.send(err);
  });

});

// Function - search if object in array has a value and return that object
function searchObjInArray(array, arrayProp, searchVal){
    let result = null;
    let obj = array.find((o, i) => {
        if (o[arrayProp] == searchVal) {
        result = array[i];
        return true; // stop find function
        }
    });
    return result;
}

If you run this code with node and go to "127.0.0.1/students" it will return exactly the same JSON as in your question. 如果您使用节点运行此代码并转到“127.0.0.1/students”,它将返回与您的问题完全相同的JSON。

All credits and extra info on MySQL and promises - https://codeburst.io/node-js-mysql-and-promises-4c3be599909b 有关MySQL和承诺的所有信用和额外信息 - https://codeburst.io/node-js-mysql-and-promises-4c3be599909b

MySQL 5.7+ has a JSON datatype that you can leverage for your "subjects" field. MySQL 5.7+具有JSON数据类型,您可以将其用于“主题”字段。 Here's a great tutorial on how to use that: 这是一个关于如何使用它的好教程:

https://www.sitepoint.com/use-json-data-fields-mysql-databases/ https://www.sitepoint.com/use-json-data-fields-mysql-databases/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM