web-dev-qa-db-ja.com

pg-promiseは整数を文字列として返します

タイプbigintの列を含むテーブルへのこの単純なクエリがあります。

ただし、クエリを実行すると、 pg-promise はこの列の値を文字列として返します。ドキュメントにそれに関する情報が見つかりません。それは標準的な動作ですか?

var ids = [180, 120];

db.any('SELECT id_brand, brand from catalog_brand WHERE id_brand in ($1:csv)', [ids])
    .then((data) => {
        // return results
    });

dataは次の形式を取り、intではなくidを文字列として使用します。

[{id_brand: "180", brand: "Ford"}, {id_brand: "120", brand: "Nike"}]

実際のタイプを返すように指示するものはありますか pg-promise

18
Antoine

歴史的に蓄積されてきたものは以下にたくさんあります。ただし、Node.js v10.4.0以降を使用している場合は、これをすべてスキップして、下部のセクション_UPDATE-2_にジャンプできます。


これは確かに標準的な動作です。

bigintは64ビットであり、すべての64ビット整数は基になる node-postgres ドライバーによってタイプstringとして返されますが、32ビット整数は次のように返されます。 number

これは、64ビット整数がJavaScriptで正確なネイティブ表示を行わないためです。これは、64ビット数値を特定の精度でしか表示できず、64ビット数値の全範囲を表すのには適していません。

参照: Node.jsで64ビット整数演算を実行する方法


この問題には3つの解決策があります。あなたに最適なものを選んでください。

ソリューション1

IDの格納に64ビット整数を使用しないでください。テーブルに40億を超えるレコードがあると予想されない場合は、代わりにデフォルトのintタイプ(32ビット)を使用してください。自動的に整数として返されます。

ソリューション2

返されたIDをオンザフライで整数に変換しますが、IDが十分に高い数値(53ビット)に達すると、変換された値が歪んだり変更されたりすることに注意してください。

ただし、文字列を64ビット整数に適切に変換できる特殊なライブラリを使用することはできますが(上記のリンクを参照)、クエリ間で使用するのは面倒な場合があります。


IDをオンザフライで変換する例:

_db.each('SELECT id_brand FROM catalog_brand WHERE id_brand in ($1:csv)', [ids], cat=> {
    cat.id_brand = parseInt(cat.id_brand)
})
    .then(rows => {
        // id_brand is now an integer in each row
    });
_

Database.each を参照してください。

別の例として、レコード数は常にbigintとして返されるため、これらを取得する最良の方法は、次のようにインライン値変換+変換を行うことです。

_db.one('SELECT count(*) FROM catalog_brand', [], c => +c.count)
    .then(count => {
        // count = a proper integer value, rather than an object with a string
    });
_

Database.one を参照してください。

ソリューション3

基礎となる node-postgres ドライバーに変換の安全性を無視させ、そのような型をどこでも整数に変換することができます。それが一般的に良い考えであるかどうかは言えませんが、pgp.pg.types.setTypeParser(...)を介して簡単に実行できるということだけです( pg-types を参照):

_// Convert bigserial + bigint (both with typeId = 20) to integer:
pgp.pg.types.setTypeParser(20, parseInt);
_

UPDATE-1

TypeScriptを介して_pg-promise_ v9以降を使用する場合は、上記のコードを次のように置き換えることができます。

_pgp.pg.types.setTypeParser(TypeId.INT8, parseInt);
_

ソリューション2と3は同じことを行いますが、2つの異なるレベルであることに注意してください。

  • ソリューション2での明示的なローカル変換
  • ソリューション3での暗黙的なグローバル変換

UPDATE-2

バージョン9.3. ライブラリのサポートがネイティブ BigInt タイプのサポートを追加しました。これは、Node.jsv10.4.0以降を実行している場合に使用できるようになりました。

ドライバーを自動的に使用するには BigInt for BIGINT + BIGSERIAL

_pgp.pg.types.setTypeParser(20, BigInt); // Type Id 20 = BIGINT | BIGSERIAL
_

詳細については、プロジェクトのWiKiの BigInt Manual を参照してください。

29
vitaly-t

@ vitaly-t答えはすべてを説明します!

@ vitaly-t回答のpostgree(ソリューション3)での暗黙的なグローバル変換の場合。

ここであなたが知る必要があること:

const typesBuiltins = {
    BOOL: 16,
    BYTEA: 17,
    CHAR: 18,
    INT8: 20,
    INT2: 21,
    INT4: 23,
    REGPROC: 24,
    TEXT: 25,
    OID: 26,
    TID: 27,
    XID: 28,
    CID: 29,
    JSON: 114,
    XML: 142,
    PG_NODE_TREE: 194,
    SMGR: 210,
    PATH: 602,
    POLYGON: 604,
    CIDR: 650,
    FLOAT4: 700,
    FLOAT8: 701,
    ABSTIME: 702,
    RELTIME: 703,
    TINTERVAL: 704,
    CIRCLE: 718,
    MACADDR8: 774,
    MONEY: 790,
    MACADDR: 829,
    INET: 869,
    ACLITEM: 1033,
    BPCHAR: 1042,
    VARCHAR: 1043,
    DATE: 1082,
    TIME: 1083,
    TIMESTAMP: 1114,
    TIMESTAMPTZ: 1184,
    INTERVAL: 1186,
    TIMETZ: 1266,
    BIT: 1560,
    VARBIT: 1562,
    NUMERIC: 1700,
    REFCURSOR: 1790,
    REGPROCEDURE: 2202,
    REGOPER: 2203,
    REGOPERATOR: 2204,
    REGCLASS: 2205,
    REGTYPE: 2206,
    UUID: 2950,
    TXID_SNAPSHOT: 2970,
    PG_LSN: 3220,
    PG_NDISTINCT: 3361,
    PG_DEPENDENCIES: 3402,
    TSVECTOR: 3614,
    TSQUERY: 3615,
    GTSVECTOR: 3642,
    REGCONFIG: 3734,
    REGDICTIONARY: 3769,
    JSONB: 3802,
    REGNAMESPACE: 4089,
    REGROLE: 4096
};

あなたはここで見つけることができます
https://github.com/brianc/node-pg-types/blob/master/lib/builtins.js

通常、この方法でアクセスできます

const pg = require('pg');

pg.types.setTypeParser(pg.types.builtins.INT8, (value: string) => {
   return parseInt(value);
});

pg.types.setTypeParser(pg.types.builtins.FLOAT8, (value: string) => {
    return parseFloat(value);
});

pg.types.setTypeParser(pg.types.builtins.NUMERIC, (value: string) => {
    return parseFloat(value);
});

これは通常、すべての数値データを処理します。

何らかの理由でpg.types.builtinsにアクセスできない場合(私の場合、何らかの理由でTypeScriptで)。あなたはそれを過ぎてコピーすることができます。または、対応するマップ番号を直接使用します。

更新(混乱を避けるため)

今のところ「pg」:「^ 7.11.0」。 pgは、組み込みをまったく含まないpg-types2.0.1を使用しています。そして、以前のすべてのバージョンもそうです。これにより、アクセスpg.types.builtins.が実行可能になりません(上記のバージョンまでのいずれのバージョンでも)。

前述の解決策は、過去をコピーすることですmapping現在のプロジェクトで行ったように。 (上記のスニペットをすべてチェックしてコピーしてください) enter image description hereenter image description here

または、リストから直接指定された対応するマッピングを使用します。

pgp.pg.types.setTypeParser(20, parseInt);

回避策としての別の解決策は、pg-typesパッケージを直接使用することです。それは最新バージョンです。

const types = require('pg-types');
// types.builtins.INT8

それ以外の場合、PRは@ vitaly-tで埋められます。これは次のリンクで確認できます。
https://github.com/brianc/node-postgres/pull/1937/commits/c7666214833715ac2494b81865cfe1ea7cef9289

pg-typesのバージョンをpgパッケージ(node-postgres)で更新します。 enter image description here

だからそれが受け入れられたら。イニシャルの例が機能し始めます。

私の情報源は当初、公式のREADME of pg-types
https://github.com/brianc/node-pg-types

enter image description here

別の最後の注意:

これはTypeScriptの使用に関するものです。

pg-types TypeScriptタイピングには、現在のバージョン"pg-types": "^2.1.0"のように組み込みが含まれていません。更新されます。したがって、自分でタイピングを追加するか、どちらかです。

typeof types & {builtins: {[key in builtinsTypes]: number}}

ここで、builtinsTypesは、すべてのプロパティ名の和集合です。

(ただし、穴オブジェクトをコピーして貼り付けると、より速く、より短く、よりクリーンになります)。

以下の列挙型でそれを行うことができます

enum TypeId {
        BOOL = 16,
        BYTEA = 17,
        CHAR = 18,
        INT8 = 20,
        INT2 = 21,
        INT4 = 23,
        REGPROC = 24,
        TEXT = 25,
        OID = 26,
        TID = 27,
        XID = 28,
        CID = 29,
        JSON = 114,
        XML = 142,
        PG_NODE_TREE = 194,
        SMGR = 210,
        PATH = 602,
        POLYGON = 604,
        CIDR = 650,
        FLOAT4 = 700,
        FLOAT8 = 701,
        ABSTIME = 702,
        RELTIME = 703,
        TINTERVAL = 704,
        CIRCLE = 718,
        MACADDR8 = 774,
        MONEY = 790,
        MACADDR = 829,
        INET = 869,
        ACLITEM = 1033,
        BPCHAR = 1042,
        VARCHAR = 1043,
        DATE = 1082,
        TIME = 1083,
        TIMESTAMP = 1114,
        TIMESTAMPTZ = 1184,
        INTERVAL = 1186,
        TIMETZ = 1266,
        BIT = 1560,
        VARBIT = 1562,
        NUMERIC = 1700,
        REFCURSOR = 1790,
        REGPROCEDURE = 2202,
        REGOPER = 2203,
        REGOPERATOR = 2204,
        REGCLASS = 2205,
        REGTYPE = 2206,
        UUID = 2950,
        TXID_SNAPSHOT = 2970,
        PG_LSN = 3220,
        PG_NDISTINCT = 3361,
        PG_DEPENDENCIES = 3402,
        TSVECTOR = 3614,
        TSQUERY = 3615,
        GTSVECTOR = 3642,
        REGCONFIG = 3734,
        REGDICTIONARY = 3769,
        JSONB = 3802,
        REGNAMESPACE = 4089,
        REGROLE = 4096
}

pg-promisehttps://github.com/vitaly-t/pg-promise/blob/v9/TypeScript/pg-subset.d.ts#L1 内で行われるように

すべてが更新されたら。 pgからの使用は行く方法です。

更新

パッケージが更新されました。そして、あなたはそれを期待通りに使うことができます。

import { types } from 'pg';

// data parsing
types.setTypeParser(types.builtins.INT8, (value: string) => {
    return parseInt(value);
});

types.setTypeParser(types.builtins.FLOAT8, (value: string) => {
    return parseFloat(value);
});

types.setTypeParser(types.builtins.NUMERIC, (value: string) => {
    return parseFloat(value);
});

上記のvitaly-tの回答の一部UPDATE-2も確認してください https://stackoverflow.com/a/39176670/7668448

2
Mohamed Allal

別の方法として、特にJavaScriptを使用している場合なし BigIntサポートは、SQLクエリで値をintにキャストすることです。

var ids = [180, 120];

// Cast id_brand to an int to ensure it is not parsed as a string.
db.any('SELECT id_brand::int, brand from catalog_brand WHERE id_brand in ($1:csv)', [ids])
    .then((data) => {
        // return results
    });

もちろん、id_brandの値が最大intより大きい場合、これは役に立ちません。

0
CJxD