Files
NodeBB/src/database/postgres/hash.js
Barış Soner Uşaklı af1f7249a7 feat: test psql without defineProperty (#7815)
* feat: test psql without defineProperty

* feat: refactor psql

remove .bind calls, use module.pool.query directly
move requires to top of file
move promisify to bottom so .init etc are promisified

* feat: mongodb

move requires to bottom

* feat: redis
2019-08-05 09:20:00 -04:00

311 lines
7.7 KiB
JavaScript

'use strict';
module.exports = function (module) {
var helpers = require('./helpers');
module.setObject = async function (key, data) {
if (!key || !data) {
return;
}
if (data.hasOwnProperty('')) {
delete data[''];
}
await module.transaction(async function (client) {
const dataString = JSON.stringify(data);
async function setOne(key) {
await helpers.ensureLegacyObjectType(client, key, 'hash');
await client.query({
name: 'setObject',
text: `
INSERT INTO "legacy_hash" ("_key", "data")
VALUES ($1::TEXT, $2::TEXT::JSONB)
ON CONFLICT ("_key")
DO UPDATE SET "data" = "legacy_hash"."data" || $2::TEXT::JSONB`,
values: [key, dataString],
});
}
if (Array.isArray(key)) {
await Promise.all(key.map(k => setOne(k)));
} else {
await setOne(key);
}
});
};
module.setObjectField = async function (key, field, value) {
if (!field) {
return;
}
await module.transaction(async function (client) {
const valueString = JSON.stringify(value);
async function setOne(key) {
await helpers.ensureLegacyObjectType(client, key, 'hash');
await client.query({
name: 'setObjectField',
text: `
INSERT INTO "legacy_hash" ("_key", "data")
VALUES ($1::TEXT, jsonb_build_object($2::TEXT, $3::TEXT::JSONB))
ON CONFLICT ("_key")
DO UPDATE SET "data" = jsonb_set("legacy_hash"."data", ARRAY[$2::TEXT], $3::TEXT::JSONB)`,
values: [key, field, valueString],
});
}
if (Array.isArray(key)) {
await Promise.all(key.map(k => setOne(k)));
} else {
await setOne(key);
}
});
};
module.getObject = async function (key) {
if (!key) {
return null;
}
const res = await module.pool.query({
name: 'getObject',
text: `
SELECT h."data"
FROM "legacy_object_live" o
INNER JOIN "legacy_hash" h
ON o."_key" = h."_key"
AND o."type" = h."type"
WHERE o."_key" = $1::TEXT
LIMIT 1`,
values: [key],
});
return res.rows.length ? res.rows[0].data : null;
};
module.getObjects = async function (keys) {
if (!Array.isArray(keys) || !keys.length) {
return [];
}
const res = await module.pool.query({
name: 'getObjects',
text: `
SELECT h."data"
FROM UNNEST($1::TEXT[]) WITH ORDINALITY k("_key", i)
LEFT OUTER JOIN "legacy_object_live" o
ON o."_key" = k."_key"
LEFT OUTER JOIN "legacy_hash" h
ON o."_key" = h."_key"
AND o."type" = h."type"
ORDER BY k.i ASC`,
values: [keys],
});
return res.rows.map(row => row.data);
};
module.getObjectField = async function (key, field) {
if (!key) {
return null;
}
const res = await module.pool.query({
name: 'getObjectField',
text: `
SELECT h."data"->>$2::TEXT f
FROM "legacy_object_live" o
INNER JOIN "legacy_hash" h
ON o."_key" = h."_key"
AND o."type" = h."type"
WHERE o."_key" = $1::TEXT
LIMIT 1`,
values: [key, field],
});
return res.rows.length ? res.rows[0].f : null;
};
module.getObjectFields = async function (key, fields) {
if (!key) {
return null;
}
const res = await module.pool.query({
name: 'getObjectFields',
text: `
SELECT (SELECT jsonb_object_agg(f, d."value")
FROM UNNEST($2::TEXT[]) f
LEFT OUTER JOIN jsonb_each(h."data") d
ON d."key" = f) d
FROM "legacy_object_live" o
INNER JOIN "legacy_hash" h
ON o."_key" = h."_key"
AND o."type" = h."type"
WHERE o."_key" = $1::TEXT`,
values: [key, fields],
});
if (res.rows.length) {
return res.rows[0].d;
}
var obj = {};
fields.forEach(function (f) {
obj[f] = null;
});
return obj;
};
module.getObjectsFields = async function (keys, fields) {
if (!Array.isArray(keys) || !keys.length) {
return [];
}
const res = await module.pool.query({
name: 'getObjectsFields',
text: `
SELECT (SELECT jsonb_object_agg(f, d."value")
FROM UNNEST($2::TEXT[]) f
LEFT OUTER JOIN jsonb_each(h."data") d
ON d."key" = f) d
FROM UNNEST($1::text[]) WITH ORDINALITY k("_key", i)
LEFT OUTER JOIN "legacy_object_live" o
ON o."_key" = k."_key"
LEFT OUTER JOIN "legacy_hash" h
ON o."_key" = h."_key"
AND o."type" = h."type"
ORDER BY k.i ASC`,
values: [keys, fields],
});
return res.rows.map(row => row.d);
};
module.getObjectKeys = async function (key) {
if (!key) {
return;
}
const res = await module.pool.query({
name: 'getObjectKeys',
text: `
SELECT ARRAY(SELECT jsonb_object_keys(h."data")) k
FROM "legacy_object_live" o
INNER JOIN "legacy_hash" h
ON o."_key" = h."_key"
AND o."type" = h."type"
WHERE o."_key" = $1::TEXT
LIMIT 1`,
values: [key],
});
return res.rows.length ? res.rows[0].k : [];
};
module.getObjectValues = async function (key) {
const data = await module.getObject(key);
return data ? Object.values(data) : [];
};
module.isObjectField = async function (key, field) {
if (!key) {
return;
}
const res = await module.pool.query({
name: 'isObjectField',
text: `
SELECT (h."data" ? $2::TEXT AND h."data"->>$2::TEXT IS NOT NULL) b
FROM "legacy_object_live" o
INNER JOIN "legacy_hash" h
ON o."_key" = h."_key"
AND o."type" = h."type"
WHERE o."_key" = $1::TEXT
LIMIT 1`,
values: [key, field],
});
return res.rows.length ? res.rows[0].b : false;
};
module.isObjectFields = async function (key, fields) {
if (!key) {
return;
}
const data = await module.getObjectFields(key, fields);
if (!data) {
return fields.map(() => false);
}
return fields.map(field => data.hasOwnProperty(field) && data[field] !== null);
};
module.deleteObjectField = async function (key, field) {
await module.deleteObjectFields(key, [field]);
};
module.deleteObjectFields = async function (key, fields) {
if (!key || !Array.isArray(fields) || !fields.length) {
return;
}
await module.pool.query({
name: 'deleteObjectFields',
text: `
UPDATE "legacy_hash"
SET "data" = COALESCE((SELECT jsonb_object_agg("key", "value")
FROM jsonb_each("data")
WHERE "key" <> ALL ($2::TEXT[])), '{}')
WHERE "_key" = $1::TEXT`,
values: [key, fields],
});
};
module.incrObjectField = async function (key, field) {
return await module.incrObjectFieldBy(key, field, 1);
};
module.decrObjectField = async function (key, field) {
return await module.incrObjectFieldBy(key, field, -1);
};
module.incrObjectFieldBy = async function (key, field, value) {
value = parseInt(value, 10);
if (!key || isNaN(value)) {
return null;
}
return await module.transaction(async function (client) {
if (Array.isArray(key)) {
await helpers.ensureLegacyObjectsType(client, key, 'hash');
} else {
await helpers.ensureLegacyObjectType(client, key, 'hash');
}
const res = await client.query(Array.isArray(key) ? {
name: 'incrObjectFieldByMulti',
text: `
INSERT INTO "legacy_hash" ("_key", "data")
SELECT UNNEST($1::TEXT[]), jsonb_build_object($2::TEXT, $3::NUMERIC)
ON CONFLICT ("_key")
DO UPDATE SET "data" = jsonb_set("legacy_hash"."data", ARRAY[$2::TEXT], to_jsonb(COALESCE(("legacy_hash"."data"->>$2::TEXT)::NUMERIC, 0) + $3::NUMERIC))
RETURNING ("data"->>$2::TEXT)::NUMERIC v`,
values: [key, field, value],
} : {
name: 'incrObjectFieldBy',
text: `
INSERT INTO "legacy_hash" ("_key", "data")
VALUES ($1::TEXT, jsonb_build_object($2::TEXT, $3::NUMERIC))
ON CONFLICT ("_key")
DO UPDATE SET "data" = jsonb_set("legacy_hash"."data", ARRAY[$2::TEXT], to_jsonb(COALESCE(("legacy_hash"."data"->>$2::TEXT)::NUMERIC, 0) + $3::NUMERIC))
RETURNING ("data"->>$2::TEXT)::NUMERIC v`,
values: [key, field, value],
});
return Array.isArray(key) ? res.rows.map(r => parseFloat(r.v)) : parseFloat(res.rows[0].v);
});
};
};