CodeFoodPixels / node-promise-mysql

A wrapper for mysqljs/mysql that wraps function calls with Bluebird promises.
MIT License
338 stars 64 forks source link

I have a undefined problem in this configuration #92

Closed PedroRuiz closed 6 years ago

PedroRuiz commented 6 years ago

I have a table of users like that:

+-------------------------+-----------------------+------+-----+---------+----------------+
| Field                   | Type                  | Null | Key | Default | Extra          |
+-------------------------+-----------------------+------+-----+---------+----------------+
| id                      | mediumint(8) unsigned | NO   | PRI | NULL    | auto_increment |
| ip_address              | varchar(45)           | NO   |     | NULL    |                |
| username                | varchar(100)          | NO   |     | NULL    |                |
| password                | varchar(80)           | NO   |     | NULL    |                |
| salt                    | varchar(40)           | YES  |     | NULL    |                |
| email                   | varchar(254)          | NO   | UNI | NULL    |                |
| activation_code         | varchar(40)           | YES  |     | NULL    |                |
| forgotten_password_code | varchar(40)           | YES  |     | NULL    |                |
| forgotten_password_time | int(11) unsigned      | YES  |     | NULL    |                |
| remember_code           | varchar(40)           | YES  |     | NULL    |                |
| created_on              | int(11) unsigned      | NO   |     | NULL    |                |
| last_login              | int(11) unsigned      | YES  |     | NULL    |                |
| active                  | tinyint(1) unsigned   | YES  |     | NULL    |                |
| first_name              | varchar(50)           | YES  |     | NULL    |                |
| last_name               | varchar(50)           | YES  |     | NULL    |                |
| company                 | varchar(100)          | YES  |     | NULL    |                |
| phone                   | varchar(20)           | YES  |     | NULL    |                |
+-------------------------+-----------------------+------+-----+---------+----------------+

gruops table as follows:

+-------------+-----------------------+------+-----+---------+----------------+
| Field       | Type                  | Null | Key | Default | Extra          |
+-------------+-----------------------+------+-----+---------+----------------+
| id          | mediumint(8) unsigned | NO   | PRI | NULL    | auto_increment |
| name        | varchar(20)           | NO   |     | NULL    |                |
| description | varchar(100)          | NO   |     | NULL    |                |
+-------------+-----------------------+------+-----+---------+----------------+

and I have too a relation, called users_groups, between users and groups like that:

+----------+-----------------------+------+-----+---------+----------------+
| Field    | Type                  | Null | Key | Default | Extra          |
+----------+-----------------------+------+-----+---------+----------------+
| id       | mediumint(8) unsigned | NO   | PRI | NULL    | auto_increment |
| user_id  | mediumint(8) unsigned | NO   | MUL | NULL    |                |
| group_id | mediumint(8) unsigned | NO   | MUL | NULL    |                |
+----------+-----------------------+------+-----+---------+----------------+

My intention is collect any group's data of a due user,

/**
* Name: promise
*
* @author Pedro Ruiz Hidalgo
*         
* Location: pi/typescript/promise2/promise
*
* Created:  22 Jun 2018
*
*
* Description:
*
* Requirements:
*
* @package
* @property
*
* @version 1.0
*/
'use strict';
var require;
var mysql = require('promise-mysql');
var connection;
mysql.createConnection({
    host: host',
    user: 'user',
    password: 'pass',
    database: 'test'
}).then(function (conn) {
    connection = conn;
    return connection.query('select group_id from users_groups where user_id=2');
}).then(function (rows) {
    var result;
    for (var _i = 0; _i < rows.length; _i++) {
        var group = rows[_i];
        console.log('Resultados parciales antes de query: ' + JSON.stringify(group.group_id), 'select * from groups where id=' + group.group_id);
        result.push(connection.query('select * from groups where id=' + group.group_id));
    }
    connection.end();
    return result;
}).then(function (rows) {
    for (var _i = 0; _i < rows.length; _i++) {
        var group = rows[_i];
        console.log('resultados parciales de query: ' + JSON.stringify(group));
        console.log('Resultados finales: ' + JSON.stringify(group));
    }
}).catch(function (error) {
    if (connection && connection.end)
        connection.end();
    //logs out the error
    console.log(error);
});
/** this ends this file
* pi/typescript/promise2/promise
**/

but I got:

Resultados parciales antes de query: 1 select * from groups where id=1
TypeError: Cannot read property 'push' of undefined
    at /home/pi/typescript/promise2/promise.js:41:16
    at tryCatcher (/home/pi/node_modules/bluebird/js/release/util.js:16:23)
    at Promise._settlePromiseFromHandler (/home/pi/node_modules/bluebird/js/release/promise.js:512:31)
    at Promise._settlePromise (/home/pi/node_modules/bluebird/js/release/promise.js:569:18)
    at Promise._settlePromise0 (/home/pi/node_modules/bluebird/js/release/promise.js:614:10)
    at Promise._settlePromises (/home/pi/node_modules/bluebird/js/release/promise.js:693:18)
    at Async._drainQueue (/home/pi/node_modules/bluebird/js/release/async.js:133:16)
    at Async._drainQueues (/home/pi/node_modules/bluebird/js/release/async.js:143:10)
    at Immediate.Async.drainQueues [as _onImmediate] (/home/pi/node_modules/bluebird/js/release/async.js:17:14)
    at runCallback (timers.js:763:18)
    at tryOnImmediate (timers.js:734:5)
    at processImmediate (timers.js:716:5)

This javascript comes of $ bash tsc promise.ts

/**
* Name: promise
*
* @author Pedro Ruiz Hidalgo
*         
* Location: pi/typescript/promise2/promise
*
* Created:  22 Jun 2018
*
*
* Description:
*
* Requirements:
*
* @package
* @property
*
* @version 1.0
*/

var require: any;

var mysql = require('promise-mysql');
var connection: any;

mysql.createConnection({
    host: 'host',
    user: 'user',
    password: 'pass',
    database: 'test'
}).then( function( conn ) {

    connection = conn;
    return connection.query('select group_id from users_groups where user_id=2');

}).then ( function( rows ) {

    var result: any[];

    for (let group of rows) 
    {
        console.log('Resultados parciales antes de query: ' + JSON.stringify(group.group_id), 'select * from groups where id=' + group.group_id);
        result.push(  connection.query('select * from groups where id=' + group.group_id) );
    }
    connection.end();
    return result;

}).then( function( rows ) {

    for (let group of rows) {
        console.log('resultados parciales de query: ' + JSON.stringify(group));
        console.log('Resultados finales: ' + JSON.stringify(group));
    }

}).catch(function (error) {
        if (connection && connection.end) connection.end();
        //logs out the error
        console.log(error);
});

/** this ends this file
* pi/typescript/promise2/promise
**/

If your first idea is solve that in a myslq sentence, please forget it: I am trying to chain promises in my real (may be computational?) world.

Thanks in advance!

move-zig commented 6 years ago

It looks like you're treating result as an array, but you haven't initialized it. Try

var result = [];

But on top of that, result is not a promise so you can't return it and use it in the following then().

...
  var result;
...
  result.push(...);
...
  return result;
}).then(function (rows) {
...
PedroRuiz commented 6 years ago

Thanks @move-zig, I will try it.

PedroRuiz commented 6 years ago

Dear @move-zig, I treat to translate my PHP code into TypeScript. It seems I have not enough knowledge of this new language still. You would be so kind show me how?

thanks in advance, sir!

move-zig commented 6 years ago

This works for me:

import mysql from 'promise-mysql';

let connection: mysql.Connection;

mysql.createConnection({
  host: 'host',
  user: 'user',
  password: 'password',
  database: 'test',
}).then((conn) => {

  connection = conn;
  return connection.query('select group_id from users_groups where user_id=2');

}).then((rows) => {

  const promises = [];
  for (let _i = 0; _i < rows.length; _i++) {
    const group = rows[_i];
    console.log('Resultados parciales antes de query: ' + JSON.stringify(group.group_id), 'select * from groups where id=' + group.group_id);
    promises.push(connection.query('select * from groups where id = ?', group.group_id));
  }

  connection.end();

  return Promise.all(promises);

}).then((rows) => {

  for (let _i = 0; _i < rows.length; _i++) {
    const group = rows[_i];
    console.log('resultados parciales de query: ' + JSON.stringify(group));
    console.log('Resultados finales: ' + JSON.stringify(group));
  }

}).catch((error) => {

  if (connection && connection.end) {
    connection.end();
  }
  //logs out the error
  console.log(error);

});

Major Changes

Minor Changes

And if you want to write in a style you might be more familiar with, consider async/await syntax. This is how I would write it:

import mysql from 'promise-mysql';
import Bluebird from 'bluebird';

(async (userId) => {

  try {

    // create the initial connection
    const connection = await mysql.createConnection({
      host: 'host',
      user: 'user',
      password: 'root',
      database: 'test'
    });

    try {

      // run the initial query
      const userGroupSelectSql = 'SELECT group_id FROM users_groups WHERE user_id = ?';
      const userGroups = await connection.query(userGroupSelectSql, userId);

      // array for storing our promises
      const promises: Bluebird<any>[] = [];

      // run the subqueries
      const groupSelectSql = 'SELECT * FROM groups WHERE id = ?';
      for (const userGroup of userGroups) {
        console.log(`Resultados parciales antes de query: select * from groups where id = ${userGroup.group_id}`);
        const promise = connection.query(groupSelectSql, userGroup.group_id); // create the promise (not using `await` here)
        promises.push(promise); // store the promise in the array
      }

      // here is where we'll `await` all the promises at once
      // note: that instead of an array of database results, we're going to get an array of array of database results
      const groupsArray: any[][] = await Promise.all(promises);

      for (const groups of groupsArray) { // loop through the array of promise resolutions
        for (const group of groups) { // loop through the database results
          console.log('Resultados finales: ' + JSON.stringify(group));
        }
      }

    } finally {
      // close the connection
      connection.end();
    }

  } catch (error) {
    // write errors to the console
    console.log(error);
  }

})(2);

Also, have you considered using an SQL join instead of running additional queries in a loop?

SELECT g.*
FROM groups g
LEFT JOIN users_groups ug ON ug.group_id = g.id
WHERE ug.user_id = ?
PedroRuiz commented 6 years ago

Tone of thanks!!!

CodeFoodPixels commented 6 years ago

Thanks for jumping on this @move-zig!

@PedroRuiz if that's solved the issue, please close it