是否存在等效于knex.pluck()的函数,该函数将返回多列的值?

时间:2019-02-27 02:01:11

标签: knex.js

How to return a plain value from a Knex / Postgresql query?中,我询问仅从Knex查询中获取列值,而没有包含列名称的JSON对象。建议我使用pluck(fieldName)

如果我在查询中返回单个列,那很好。如何返回相似格式的多列?我正在寻找类似pluck([fieldName1, fieldname2])pluck(fieldName1, fieldname2)的东西,对于每一行,返回["value1", "value2"]

1 个答案:

答案 0 :(得分:2)

首先让我们回顾一下knex.js当前如何实现pluck,然后看看我们是否能找到适合您需求的可行解决方案。

因此,pluck所做的本质是修改查询以在选择语句中包含pluck列,然后使用lodash.map处理响应以仅返回pluck字段值的数组(而不是结果对象数组)。这意味着它在内部对查询结果运行lodash.map(resultsArray, pluckColumnName);之类的东西。

因此,使用小巧的代码示例意味着:

常规查询:

  1. knex('product').limit(10); // regular query
  2. SELECT * FROM `product` LIMIT 10 // translates into wildcard select from the table
  3. [{id: 1, name: 'foo'}, {...}] // result is Array of Objects with all columns from the table

常规查询:

  1. knex('product').limit(10).pluck('id'); // query with pluck
  2. SELECT id FROM `product` LIMIT 10 // translates into specific column select
  3. loadsh.map(results, 'id') // knex internally runs loadsh.map on the results
  4. [1, 2, 3, ...] // we get array of pluck column values back

如果您要选择多个列,则只需将结果映射函数更改为,而不是仅返回单个值即可返回多个值的数组。这可以通过多种方式完成:

解决方案1:

最简单的方法是在查询中手动包括采摘列,然后对结果运行常规Array Map。代码看起来像这样:

// construct the query, make sure the fields you want to get values for are present
// it's best to include them in the .select statement because you really don't care for other fields
const query = knex('product').select('id', 'name').limit(10);

return query
  .then(results => {
    return results.map(result => [result.id, result.name]);
  })
  .then(pluckedResults => {
    // pluckedResults: [[1, 'foo'], [2, 'bar'], [3, 'etc'], ...]];
    return pluckedResults;
  });

解决方案2: 可以扩展knex QueryBuilder以在所有查询上提供自定义功能。因此,我们将创建multi-pluck.js并为knex定义.multiPluck QueryBuilder扩展。

/**
 * multi-pluck.js
 * We are extending knex QueryBuilder to offer multi column pluck feature
 * We add the pluck columns to the original query select statement and process result to return
 * only the values of pluck columns in the same order they are defined
 *
 * Please note that .multiPluck() needs to be last in the chain as it executes the query and breaks
 * query chaining.
 */

const QueryBuilder = require('knex/lib/query/builder');

Object.assign(QueryBuilder.prototype, {
  multiPluck: function multiPluck (...pluckColumns) {
    // add pluck columns to the query.select
    this.select(...pluckColumns);

    // run the query and map results
    return this.then(results => {

      // go over all result Objects
      return results.map(result => {

        // for each result Object, return an array of values for each pluckColumns
        return pluckColumns.map(pluckColumn => {

          // if pluck column includes table name like product.id, only use id field here
          if (pluckColumn.indexOf('.') !== -1) {
            pluckColumn = pluckColumn.split('.').slice(-1)[0];
          }

          // return given column value from this result row
          return result[pluckColumn];
        });
      });
    });
  }
});

要使用它,您将执行以下操作:

// include the DB connection file which initialises knex connection
const knex = require('./db/connection').knex;

// include our multi-pluck extension
require('./db/multi-pluck');

// example function to get data from DB using the extension
async function getData () {
  return knex('product').limit(10).multiPluck('id', 'name');
}

// run the function
getData()
  .then(pluckedResults => {
    console.log(pluckedResults); // [[1, 'foo'], [2, 'bar'], [3, 'etc'], ...]
  })
  .catch(err => {
    // handle errors as usual
  });

请注意,QueryBuilder扩展可以包含在连接初始化文件中,因此您可以在任何地方使用它们。

警告:由于multiPluck实际上运行查询,因此它会断开knex查询链,并且必须是查询上调用的最后一个函数