type query postgres fields contrib arrayagg python django orm django-models django-jsonfield

python - query - Django db error: no se pudo identificar un operador de igualdad para el tipo json al intentar anotar un modelo con jsonfield



save json django (3)

Mi solución usa PL / Python, analiza y vuelve a volcar el json ordenando las claves, y luego produce el hash FNV1a del resultado: https://github.com/ifad/chronomodel/blob/master/sql/json_ops.sql .

No estoy usando hashtext () porque es solo para uso interno: http://www.postgresql.org/message-id/[email protected] .

No es una bala de plata, solo un truco crudo. La verdadera solución es esperar el soporte completo en Postgres.

Estoy trabajando en Django 1.5.4 y PostgreSQL 9.3, usando django-jsonfield para JSONField.

La siguiente consulta arroja un error de db (no se pudo identificar un operador de igualdad para el tipo json):

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

El field_to_count_by no es JSONField, campo int normal.

¿Alguna idea de cómo puedo resolver el problema y seguir usando anotaciones?

¿Qué anotación hace detrás del capó?


Pasé por el mismo problema, y ​​luego probé el código de Joachim Jablon, y aunque parecía funcionar bien, todavía tenía problemas. Voy a ir al grano aquí, la versión más larga está en mi blog .

  • SELECT ''{"a":1,"b":2}''::json = ''{"b":2,"a":1}''::json devolvió false , porque está basado en la representación de la cadena.
  • No se permite clasificar campos, porque la clase de operador es hash lugar de btree .

Luego, creé una función json_cmp() en PL / V8, que se puede usar para alimentar los operadores necesarios para un btree.

Aquí está el script SQL completo

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);

Esto tiende a ser mucho más lento que la simple comparación de cuerdas, por supuesto, pero tiene la ventaja de producir resultados más sólidos.

Tenga en cuenta que si usa South para sus migraciones, puede crear una migración vacía y ejecutar SQL desde el método forwards() . Esto instalará automáticamente las funciones cuando migres tu aplicación.


Me encontré con el mismo problema y finalmente (hoy) implementé un operador falso ejecutando esto como administrador en la consola 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);

(fuertemente inspirado de este hilo)

También hice referencia a su pregunta en el problema de GitHub de django-jsonfield.

Tenga en cuenta que :

  • Tengo una idea muy limitada sobre el impacto que esto tendrá. Tal vez no sea realmente una buena idea. Las implementaciones son ingenuas, pero deberían ser suficientes. O tal vez no.
  • En particular, el operador de igualdad verifica la igualdad del texto y no la igualdad semántica json. Pero en lo que se refiere a django-jsonField, creo que hay pocas posibilidades de que realmente necesitemos que los resultados sean correctos (puede ser un SELECTO FALSO incluso podría hacer el truco).