我遇到了同样的问题,然后我尝试了 Joachim Jablon 的代码,虽然它看起来运行良好,但仍然存在问题。我会说到这里,最长的版本在我的博客上。
SELECT '{"a":1,"b":2}'::json = '{"b":2,"a":1}'::json
返回false
,因为它是基于字符串表示的。
- 不允许对字段进行排序,因为运算符类
hash
不是btree
.
然后,我json_cmp()
在 PL/V8 中创建了一个函数,可用于为 btree 所需的运算符提供动力。
这是完整的 SQL 脚本
CREATE OR REPLACE FUNCTION json_cmp(left json, right json)
RETURNS integer AS $$
function cleverType(obj) {
var type = typeof obj;
if (type === 'object') {
if (obj === null) {
type = 'null';
} else if (obj instanceof Array) {
type = 'array';
}
}
return type;
}
function cmp(left, right) {
var leftType = cleverType(left),
rightType = cleverType(right),
i,
buf,
leftKeys,
rightKeys,
output = 0;
if (leftType !== rightType) {
output = leftType.localeCompare(rightType);
} else if (leftType === 'number'
|| leftType === 'boolean'
|| leftType === 'string') {
if (left < right) {
output = -1;
} else if (left > right) {
output = 1;
} else {
output = 0;
}
} else if (leftType === 'array') {
if (left.length !== right.length) {
output = cmp(left.length, right.length);
} else {
for (i = 0; i < left.length; i += 1) {
buf = cmp(left[i], right[i]);
if (buf !== 0) {
output = buf;
break;
}
}
}
} else if (leftType === 'object') {
leftKeys = Object.keys(left);
rightKeys = Object.keys(right);
if (leftKeys.length !== rightKeys.length) {
leftKeys.sort();
rightKeys.sort();
buf = cmp(leftKeys, rightKeys);
} else {
buf = cmp(leftKeys.length, rightKeys.length);
}
if (buf !== 0) {
output = buf;
} else {
for (i = 0; i < leftKeys.length; i += 1) {
buf = cmp(left[leftKeys[i]], right[leftKeys[i]]);
if (buf !== 0) {
output = buf;
break;
}
}
}
}
return output;
}
return cmp(left, right);
$$ LANGUAGE plv8 IMMUTABLE STRICT;
CREATE OR REPLACE FUNCTION json_eq(json, json)
RETURNS BOOLEAN LANGUAGE SQL STRICT IMMUTABLE AS $$
SELECT json_cmp($1, $2) = 0;
$$;
CREATE OR REPLACE FUNCTION json_lt(json, json)
RETURNS BOOLEAN LANGUAGE SQL STRICT IMMUTABLE AS $$
SELECT json_cmp($1, $2) < 0;
$$;
CREATE OR REPLACE FUNCTION json_lte(json, json)
RETURNS BOOLEAN LANGUAGE SQL STRICT IMMUTABLE AS $$
SELECT json_cmp($1, $2) <= 0;
$$;
CREATE OR REPLACE FUNCTION json_gt(json, json)
RETURNS BOOLEAN LANGUAGE SQL STRICT IMMUTABLE AS $$
SELECT json_cmp($1, $2) > 0;
$$;
CREATE OR REPLACE FUNCTION json_gte(json, json)
RETURNS BOOLEAN LANGUAGE SQL STRICT IMMUTABLE AS $$
SELECT json_cmp($1, $2) >= 0;
$$;
CREATE OPERATOR = (LEFTARG = json, RIGHTARG = json, PROCEDURE = json_eq);
CREATE OPERATOR < (LEFTARG = json, RIGHTARG = json, PROCEDURE = json_lt);
CREATE OPERATOR <= (LEFTARG = json, RIGHTARG = json, PROCEDURE = json_lte);
CREATE OPERATOR > (LEFTARG = json, RIGHTARG = json, PROCEDURE = json_gt);
CREATE OPERATOR >= (LEFTARG = json, RIGHTARG = json, PROCEDURE = json_gte);
CREATE OPERATOR CLASS json_ops
DEFAULT FOR TYPE json USING btree AS
OPERATOR 1 <,
OPERATOR 2 <=,
OPERATOR 3 =,
OPERATOR 4 >=,
OPERATOR 5 >,
FUNCTION 1 json_cmp(json, json);
当然,这往往比简单的字符串比较慢很多,但具有产生更可靠结果的优势。
请注意,如果您使用 South 进行迁移,您可以创建一个空迁移并从该forwards()
方法执行 SQL。这将在您迁移应用程序时自动安装这些功能。