7

JSONFieldにdjango-jsonfieldを使用して、Django 1.5.4とPostgreSQL 9.3で作業しています。

次のクエリは、db エラーをスローします (タイプ json の等値演算子を識別できませんでした):

ModelWithJsonField.objects.annotate(count=Count('field_to_count_by'))

JSONFieldfield_to_count_byではなく、通常の int フィールドです。

問題を解決し、注釈を使用する方法はありますか?

ボンネットの後ろにある注釈は何ですか?

4

3 に答える 3

6

私は同じ問題に遭遇し、最終的に (今日) psql コンソールでこれを管理者として実行することにより、偽のオペレーターを実装しました。

-- This creates a function named hashjson that transforms the
-- json to texts and generates a hash
CREATE OR REPLACE FUNCTION hashjson(            
    json          
) RETURNS INTEGER LANGUAGE SQL STRICT IMMUTABLE AS $$ 
    SELECT hashtext($1::text); 
$$; 

-- This creates a function named json_eq that checks equality (as text)
CREATE OR REPLACE FUNCTION json_eq(            
    json, 
    json              
) RETURNS BOOLEAN LANGUAGE SQL STRICT IMMUTABLE AS $$ 
    SELECT bttextcmp($1::text, $2::text) = 0; 
$$; 

-- This creates an operator from the equality function
CREATE OPERATOR = (            
    LEFTARG   = json, 
    RIGHTARG  = json, 
    PROCEDURE = json_eq 
); 

-- Finaly, this defines a new default JSON operator family with the
-- operators and functions we just defined.
CREATE OPERATOR CLASS json_ops
   DEFAULT FOR TYPE json USING hash AS
   OPERATOR 1  =,
   FUNCTION 1  hashjson(json);

(このスレッドから多くのインスピレーションを得ています)

また、django-jsonfield GitHub issueであなたの質問を参照しました。

ご了承ください :

  • これが与える影響について、私は非常に限られた考えしか持っていません。たぶん、それは本当に良い考えではありません。実装は素朴ですが、十分なはずです。またはそうでないかもしれません。
  • 特に、equality 演算子は、セマンティック json の等価性ではなく、テキストの等価性をチェックします。しかし、django-jsonField に関する限り、結果が正しい必要がある可能性はほとんどないと思います (SELECT FALSE でうまくいくかもしれません)。
于 2013-12-06T13:31:26.200 に答える
4

私は同じ問題を経験し、次に Joachim Jablon のコードを試してみました。うまく機能しているように見えましたが、まだ問題がありました。ここで要点を説明します。最長バージョンは私のブログにあります。

  • SELECT '{"a":1,"b":2}'::json = '{"b":2,"a":1}'::jsonfalse文字列表現に基づいているため、 が返されます。
  • 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 を実行できることに注意してください。これにより、アプリを移行するときに関数が自動的にインストールされます。

于 2014-03-15T04:25:24.543 に答える