[英]How to query JSON in a Postgres table from an Express REST API
我正在嘗試通過 Express REST API 查詢 Postgres 數據庫。一列 (json_object_data) 包括 json,我希望在其上查詢“名稱”鍵。
如果我直接在數據庫控制台中輸入查詢,它就可以正常工作。 給我帶來問題的是將搜索詞傳遞到 HTTP 對 Express 路線的請求中。
僅供參考,其他不嘗試查詢 json 列的 Express 應用程序請求(GET、POST、PUT 和 DELETE)工作正常。
節點快遞代碼:
app.get("/authorsname/:name", async(req, res) => {
const { author_name } = req.params;
console.log(req.params);
try {
const getAuthors = await pool.query("select json_object_data from \"jason-elwood/authors\".\"authors\" where lower(json_object_data ->> 'name') like lower($1%)", [author_name]);
res.json(getAuthors);
console.log(req.params);
} catch (error) {
console.error(error.message);
}
});
示例 json(來自 json_object_data 列):
{
"name": "Jason Elwood",
"personal_name": "Jason Elwood",
"last_modified": {"type": "/type/datetime", "value": "2008-08-20T17:57:31.650087"},
"key": "/authors/OL1000204A",
"birth_date": "1982",
"type": {"key": "/type/author"},
"revision": 2
}
http GET請求:
http://localhost:3000/authorsname/jason
結果:
server is listening on port 3000
{ name: 'jason' }
node:events:371
throw er; // Unhandled 'error' event
^
error: unable to bind
at Parser.parseErrorMessage (/Users/jasonelwood/Development/OpenbookAPIs/node_modules/pg-protocol/dist/parser.js:287:98)
at Parser.handlePacket (/Users/jasonelwood/Development/OpenbookAPIs/node_modules/pg-protocol/dist/parser.js:126:29)
at Parser.parse (/Users/jasonelwood/Development/OpenbookAPIs/node_modules/pg-protocol/dist/parser.js:39:38)
at Socket.<anonymous> (/Users/jasonelwood/Development/OpenbookAPIs/node_modules/pg-protocol/dist/index.js:11:42)
at Socket.emit (node:events:394:28)
at addChunk (node:internal/streams/readable:315:12)
at readableAddChunk (node:internal/streams/readable:289:9)
at Socket.Readable.push (node:internal/streams/readable:228:10)
at TCP.onStreamRead (node:internal/stream_base_commons:199:23)
Emitted 'error' event on BoundPool instance at:
at Client.idleListener (/Users/jasonelwood/Development/OpenbookAPIs/node_modules/pg-pool/index.js:57:10)
at Client.emit (node:events:394:28)
at Client._handleErrorEvent (/Users/jasonelwood/Development/OpenbookAPIs/node_modules/pg/lib/client.js:319:10)
at Client._handleErrorMessage (/Users/jasonelwood/Development/OpenbookAPIs/node_modules/pg/lib/client.js:330:12)
at Connection.emit (node:events:394:28)
at /Users/jasonelwood/Development/OpenbookAPIs/node_modules/pg/lib/connection.js:114:12
at Parser.parse (/Users/jasonelwood/Development/OpenbookAPIs/node_modules/pg-protocol/dist/parser.js:40:17)
at Socket.<anonymous> (/Users/jasonelwood/Development/OpenbookAPIs/node_modules/pg-protocol/dist/index.js:11:42)
at Socket.emit (node:events:394:28)
at addChunk (node:internal/streams/readable:315:12) {
length: 92,
severity: 'FATAL',
code: 'XX000',
detail: 'cannot get parse message ""',
hint: undefined,
position: undefined,
internalPosition: undefined,
internalQuery: undefined,
where: undefined,
schema: undefined,
table: undefined,
column: undefined,
dataType: undefined,
constraint: undefined,
file: 'pool_proto_modules.c',
line: '1414',
routine: undefined,
client: Client {
_events: [Object: null prototype] { error: [Function (anonymous)] },
_eventsCount: 1,
_maxListeners: undefined,
connectionParameters: ConnectionParameters {
user: 'omitted',
database: 'omitted',
port: 5432,
host: 'omitted',
binary: false,
options: undefined,
ssl: false,
client_encoding: '',
replication: undefined,
isDomainSocket: false,
application_name: undefined,
fallback_application_name: undefined,
statement_timeout: false,
idle_in_transaction_session_timeout: false,
query_timeout: false,
connect_timeout: 0
},
user: 'omitted',
database: 'omitted',
port: 5432,
host: 'omitted',
replication: undefined,
_Promise: [Function: Promise],
_types: TypeOverrides {
_types: {
getTypeParser: [Function: getTypeParser],
setTypeParser: [Function: setTypeParser],
arrayParser: { create: [Function: create] },
builtins: {
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
}
},
text: {},
binary: {}
},
_ending: true,
_connecting: false,
_connected: true,
_connectionError: false,
_queryable: false,
connection: Connection {
_events: [Object: null prototype] {
newListener: [Function (anonymous)],
connect: [Function (anonymous)],
sslconnect: [Function (anonymous)],
authenticationCleartextPassword: [Function: bound _handleAuthCleartextPassword],
authenticationMD5Password: [Function: bound _handleAuthMD5Password],
authenticationSASL: [Function: bound _handleAuthSASL],
authenticationSASLContinue: [Function: bound _handleAuthSASLContinue],
authenticationSASLFinal: [Function: bound _handleAuthSASLFinal],
backendKeyData: [Function: bound _handleBackendKeyData],
error: [Function: bound _handleErrorEvent],
errorMessage: [Function: bound _handleErrorMessage],
readyForQuery: [Function: bound _handleReadyForQuery],
notice: [Function: bound _handleNotice],
rowDescription: [Function: bound _handleRowDescription],
dataRow: [Function: bound _handleDataRow],
portalSuspended: [Function: bound _handlePortalSuspended],
emptyQuery: [Function: bound _handleEmptyQuery],
commandComplete: [Function: bound _handleCommandComplete],
parseComplete: [Function: bound _handleParseComplete],
copyInResponse: [Function: bound _handleCopyInResponse],
copyData: [Function: bound _handleCopyData],
notification: [Function: bound _handleNotification],
end: [
[Function: bound onceWrapper] {
listener: [Function (anonymous)]
},
[Function: bound onceWrapper] {
listener: [Function (anonymous)]
},
[Function: bound onceWrapper] {
listener: [Function (anonymous)]
}
]
},
_eventsCount: 23,
_maxListeners: undefined,
stream: <ref *1> Socket {
connecting: false,
_hadError: false,
_parent: null,
_host: 'db.bit.io',
_readableState: ReadableState {
objectMode: false,
highWaterMark: 16384,
buffer: BufferList { head: null, tail: null, length: 0 },
length: 0,
pipes: [],
flowing: true,
ended: false,
endEmitted: false,
reading: false,
constructed: true,
sync: false,
needReadable: true,
emittedReadable: false,
readableListening: false,
resumeScheduled: false,
errorEmitted: false,
emitClose: false,
autoDestroy: true,
destroyed: true,
errored: null,
closed: true,
closeEmitted: false,
defaultEncoding: 'utf8',
awaitDrainWriters: null,
multiAwaitDrain: false,
readingMore: false,
dataEmitted: true,
decoder: null,
encoding: null,
[Symbol(kPaused)]: false
},
_events: [Object: null prototype] {
end: [
[Function: onReadableStreamEnd],
[Function (anonymous)],
[Function (anonymous)]
],
error: [Function: reportStreamError],
close: [Function (anonymous)],
data: [Function (anonymous)]
},
_eventsCount: 4,
_maxListeners: undefined,
_writableState: <ref *2> WritableState {
objectMode: false,
highWaterMark: 16384,
finalCalled: false,
needDrain: false,
ending: false,
ended: false,
finished: false,
destroyed: true,
decodeStrings: false,
defaultEncoding: 'utf8',
length: 0,
writing: false,
corked: 0,
sync: false,
bufferProcessing: false,
onwrite: [Function: bound onwrite],
writecb: null,
writelen: 0,
afterWriteTickInfo: {
count: 1,
cb: [Function (anonymous)],
stream: [Circular *1],
state: [Circular *2]
},
buffered: [],
bufferedIndex: 0,
allBuffers: true,
allNoop: true,
pendingcb: 1,
constructed: true,
prefinished: false,
errorEmitted: false,
emitClose: false,
autoDestroy: true,
errored: null,
closed: true,
closeEmitted: false,
[Symbol(kOnFinished)]: []
},
allowHalfOpen: false,
_sockname: null,
_pendingData: null,
_pendingEncoding: '',
server: null,
_server: null,
[Symbol(async_id_symbol)]: 22,
[Symbol(kHandle)]: null,
[Symbol(kSetNoDelay)]: true,
[Symbol(lastWriteQueueSize)]: 0,
[Symbol(timeout)]: null,
[Symbol(kBuffer)]: null,
[Symbol(kBufferCb)]: null,
[Symbol(kBufferGen)]: null,
[Symbol(kCapture)]: false,
[Symbol(kBytesRead)]: 784,
[Symbol(kBytesWritten)]: 297
},
_keepAlive: false,
_keepAliveInitialDelayMillis: 0,
lastBuffer: false,
parsedStatements: {},
ssl: false,
_ending: true,
_emitMessage: false,
_connecting: true,
[Symbol(kCapture)]: false
},
queryQueue: [],
binary: false,
processID: -1631365830,
secretKey: -1498717443,
ssl: false,
_connectionTimeoutMillis: 0,
_connectionCallback: null,
release: [Function (anonymous)],
activeQuery: null,
readyForQuery: true,
hasExecuted: true,
_poolUseCount: 1,
[Symbol(kCapture)]: false
}
}
[nodemon] app crashed - waiting for file changes before starting...
直接在數據庫控制台中發出相同的請求(產生成功的響應):
select json_object_data from "jason-elwood/authors"."authors" where lower(json_object_data ->> 'name') like lower('jason%');
我猜這與查詢的結構有關,尤其是我在請求字符串中包含請求參數的方式。 我嘗試將整個請求用單引號引起來,而不是 escaping 表名,如下所示:
const getAuthors = await pool.query('select json_object_data from "jason-elwood/authors"."authors" where lower(json_object_data ->> "name") like lower($1%)', [author_name]);
但它仍然會產生錯誤。
非常感謝對此的任何幫助。
謝謝!
至少應將%
添加到傳遞的值中,因為您使用查詢參數:
const getAuthors = await pool
.query("select json_object_data from \"jason-elwood/authors\".\"authors\" where json_object_data ->> 'name' ilike $1", [`${author_name}%`]);
res.json(getAuthors);
並且您可以使用ilike
而不是like
來避免在操作的兩邊都使用lower
。
因此,對於將來遇到此問題的任何其他人來說,問題在於我在將參數添加到查詢字符串時如何構建參數。 因為我使用的是 LIKE 運算符,所以需要像這樣格式化:
['%' + req.params.name + '%']
在我的頭撞在桌子上幾個小時后,現在對我來說顯而易見的原因。 (或者我最初擁有的是 ['%' + author_name + '%'])。 所以最終的查詢看起來像這樣:
const getAuthors = await pool.query("select json_object_data from \"jason-elwood/authors\".\"authors\" where lower(json_object_data ->> 'name') like lower($1)", ['%' + req.params.name + '%']);
我又可以呼吸了。 :)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.