web-dev-qa-db-fra.com

Index PostgreSQL sur JSON

En utilisant Postgres 9.4, je veux créer un index sur une colonne json qui sera utilisé lors de la recherche sur des clés spécifiques dans la colonne.

Par exemple, j'ai une table "ferme" avec une colonne json "animaux".

La colonne des animaux contient des objets json de format général:

'{"cow": 2, "chicken": 11, "horse": 3}'

J'ai essayé un certain nombre d'index (séparément):

(1) create INDEX animal_index ON farm ((animal ->> 'cow'));
(2) create INDEX animal_index ON farm using gin ((animal ->> 'cow'));
(3) create INDEX animal_index ON farm using Gist ((animal ->> 'cow'));

Je veux exécuter des requêtes comme:

SELECT * FROM farm WHERE (animal ->> 'cow') > 3;

et que cette requête utilise l'index.

Lorsque j'exécute cette requête:

SELECT * FROM farm WHERE (animal ->> 'cow') is null;

alors l'index (1) fonctionne, mais je ne peux faire fonctionner aucun des index pour l'inégalité.

n tel index est-il possible?

La table de ferme ne contient que ~ 5000 fermes, mais certaines d'entre elles contiennent des centaines d'animaux et les requêtes prennent simplement trop de temps pour mon cas d'utilisation. Un index comme celui-ci est la seule méthode à laquelle je peux penser pour accélérer cette requête, mais il y a peut-être une autre option.

27
lnhubbell

Vos deux autres index ne fonctionneront pas simplement parce que ->> operator renvoie text, alors que vous avez évidemment à l'esprit les classes d'opérateurs jsonb gin. Notez que vous ne mentionnez que json, mais vous avez en fait besoin de jsonb pour une indexation avancée capacités.

Pour définir la meilleure stratégie d'indexation, vous devez définir plus précisément les requêtes à couvrir. Êtes-vous uniquement intéressé par les vaches? Ou tous les animaux/tous les tags? Quels opérateurs sont possibles? Votre document JSON comprend-il également des clés non animales? Que faire avec ça? Voulez-vous inclure des lignes dans l'index où les vaches (ou autre chose) n'apparaissent pas du tout dans le document JSON?

En supposant:

  • Nous ne nous intéressons qu'aux vaches au premier niveau de nidification.
  • La valeur est toujours un integer valide.
  • Nous ne sommes pas intéressés par les rangées sans vaches.

Je suggère un index btree fonctionnel, tout comme vous l'avez déjà fait, mais transformez la valeur en entier. Je ne suppose pas que vous souhaitiez que la comparaison soit évaluée comme text (où '2' est supérieur à '1111').

CREATE INDEX animal_index ON farm (((animal ->> 'cow')::int));  -- !

Le jeu de parenthèses supplémentaire est requis pour que le raccourci de transtypage rende la syntaxe de l'expression d'index sans ambiguïté.

Utilisez la même expression dans vos requêtes pour que Postgres réalise que l'index est applicable:

SELECT * FROM farm WHERE (animal ->> 'cow')::int > 3;

Si vous avez besoin d'un index jsonb plus générique, considérez:

Pour un connu, statique, trivial nombre d'animaux (comme vous l'avez commenté), je suggère des index partiels comme:

CREATE INDEX animal_index ON farm (((animal ->> 'cow')::int))
WHERE (animal ->> 'cow') IS NOT NULL;

CREATE INDEX animal_index ON farm (((animal ->> 'chicken')::int))
WHERE (animal ->> 'chicken') IS NOT NULL;

Etc.

Vous devrez peut-être ajouter la condition d'index à la requête:

SELECT * FROM farm
WHERE (animal ->> 'cow')::int > 3
AND   (animal ->> 'cow') IS NOT NULL; 

Peut sembler redondant, mais peut être nécessaire. Testez avec ANALYZE!

57
Erwin Brandstetter