在Node.JS透過ODBC連接impala完整教學(下)

在Node.JS透過ODBC連接Impala完整教學(下)

步驟二,在 Node.js 上連接到 Ubuntu 上的 ODBC

上一篇完成了在 Ubuntu 上安裝 ODBC Driver的部分
接下來就是透過 Node.js來連接啦
letsgo

1. 安裝套件

看了一下 Node.js 連接 ODBC的 相關套件
這套是到近期都還有有維持更新的,因此採用
node-odbc

npm install odbc

2. 設定連接

官方範例

const odbc = require('odbc');

async function connectToDatabase() {
    const connection1 = await odbc.connect('DSN=MYDSN');
    // connection1 is now an open Connection

    // or using a configuration object
    const connectionConfig = {
        connectionString: 'DSN=MYDSN',
        connectionTimeout: 10,
        loginTimeout: 10,
    }
    const connection2 = await odbc.connect(connectionConfig);
    // connection2 is now an open Connection
}

connectToDatabase();

這邊比較要注意的是
connectionString: 'DSN=MYDSN'
這個DSN就是Data Source Name
如果是按照上一篇的範例,我們是取名為impalaodbc
所以會長這樣

const connectionConfig = {
      connectionString: 'DSN=impalaodbc',
      connectionTimeout: 10,
      loginTimeout: 10,
    }

3. 執行 Query

執行 Query、Pool 可以在官方文件上查詢 API 與範例
這個套件的官方文件 API 寫得蠻清楚的
主要可以在上面看
但是值得注意的是
Impala 的欄位名稱是不分大小寫的
Impala 的欄位名稱是不分大小寫的
Impala 的欄位名稱是不分大小寫的

Impala identifiers are always case-insensitive. That is, tables named t1 and T1 always refer to the same table, regardless of quote characters. Internally, Impala always folds all specified table and column names to lowercase. This is why the column headers in query output are always displayed in lowercase.
官方文件說明

像我是從MySQL轉移過來的就會遇到問題
所以我寫了一個取代欄位名稱的 Function
第一個參數是從Impala撈回來的資料
第二個參數則是你希望取代的欄位名稱
例如: ['Id', 'InfoDate', 'ColName', 'Something'...]
直接取代每一個物件的key

function toCaseSensitiveKeys(result, newKeys) {
  // make keys array to keys object
  const replacements = {};
  if (newKeys !== undefined) {
    newKeys.forEach(key => {
      replacements[key.toLowerCase()] = key;
    })
  }
  // pair keys by replacements object
  const data = result.map(row => {
    const replacedItems = Object.keys(row).map(key => {
      const newKey = replacements[key] || key;
      return { [newKey]: row[key] };
    })
    const newResult = replacedItems.reduce((a, b) => Object.assign({}, a, b));
    return newResult;
  });
  return data;
}

最後的 Query Function

const impalaQuery = (sql, values, newKeys) => {
  return new Promise((resolve, reject) => {
    // Create Connection Pool
    const connectionConfig = {
      connectionString: 'DSN=impalaodbc',
      connectionTimeout: 10,
      loginTimeout: 10,
    }
    odbc.connect(connectionConfig, (conError, connection) => {
      if (conError) {
        reject(conError);
      }
      connection.query(sql, values, (err, rows) => {
        if (err) {
          // If execute SQL faild, print SQL
          connection.createStatement((error1, statement) => {
            if (error1) { console.log(error1, statement); return; } // handle
            statement.prepare(sql, (error2) => {
              if (error2) { console.log(error2, statement); return; } // handle
              statement.bind(values, (error3) => {
                if (error3) { console.log(error3, statement); return; } // handle
              });
            });
          });
          reject(err);
        }
        else {
          const idx = rows.indexOf('statement')
          const values = rows.slice(0, idx).map((e, i) => {
            return e
          })
          const data = toCaseSensitiveKeys(values, newKeys);
          // print SQL
          console.log(rows['statement']);
          resolve(data);
        }
      });
    });
  });
};

主要是加入了

  1. 錯誤時透過階段來偵錯
  2. 印出執行的SQL
  3. 轉換大小寫

在其他地方就可以這樣使用

const sql = 'yourSQL';
const values = [SomeParms...];
const keys = [SomeColNames...];
impalaQuery(sql, values, keys)
    .then(results => {
        res.status(200).json(results);
    })
    .catch(err => {
        console.log(err);
        res.status(500).send('DB Error');
    });

這次的在Node.JS透過ODBC連接Impala就大功告成啦!
其實蠻費工的,寫這篇文時也回顧了不少苦難
希望可以幫助到有同樣需求的人~
cry_laugh