2017-10-04 7 views
1

Je cette JSON sur ma base de données:PostgreSQL - idées de requête NoSQL pour sélectionner un nœud de JSON

{ 
     "Id": 1, 
     "Questions": [ 
     { 
      "QuestionId": 6, 
      "Description": "Question 1", 
      "Alternatives": [ 
      { 
       "Index": 1, 
       "CorrectAnswer": false, 
       "AlternativeId": 26, 
       "QuestionId": 6, 
       "Description": "Alternative one", 
       "Selected": false 
      }, 
      { 
       "Index": 2, 
       "CorrectAnswer": true, 
       "AlternativeId": 27, 
       "QuestionId": 6, 
       "Description": "Alternative two", 
       "Selected": false 
      } 
      ] 
     }, 
     { 
      "QuestionId": 7, 
      "Description": "Question 2", 
      "Alternatives": [ 
      { 
       "Index": 1, 
       "CorrectAnswer": false, 
       "AlternativeId": 26, 
       "QuestionId": 6, 
       "Description": "Alternative one", 
       "Selected": false 
      }, 
      { 
       "Index": 2, 
       "CorrectAnswer": true, 
       "AlternativeId": 27, 
       "QuestionId": 6, 
       "Description": "Alternative two", 
       "Selected": false 
      } 
      ] 
     } 
     ] 
    } 

Je ne peux pas juste une question dans ce document. J'ai essayé les questions suivantes:

select data#>'{Questions}' from db.my_table 
where (data #> '{Questions,0,QuestionId}')::numeric = 6; 

SELECT data ->> 'Questions' AS Questions FROM db.my_table 
WHERE (data -> 'Questions' ->> 'QuestionId')::numeric = 6; 

SELECT data ->> 'Questions' AS Questions FROM db.my_table 
WHERE data -> 'Questions' ->> 'QuestionId' = '6' 

Que puis-je faire mal? Je reçois toujours le retour de 0 lignes affectées.

Documentation:

https://www.postgresql.org/docs/current/static/datatype-json.html https://www.postgresql.org/docs/current/static/functions-json.html

Chaque aide est la bienvenue!

Répondre

2

Parlons de sur chacun de vos requêtes d'abord:

select data#>'{Questions}' from db.my_table 
where (data #> '{Questions,0,QuestionId}')::numeric = 6; 

Cela échoue en fait (ERROR: cannot cast type json to numeric). Vous pouvez modifier l'opérateur dans la clause where à #>>, et il n'y aura plus d'erreur - #>> renvoie le texte, qui peut être converti en numérique. À ce stade, la requête renvoie l'ensemble de votre tableau Questions, plutôt que la question spécifique que vous souhaitez.

Pourquoi? Parce que toutes les questions font partie de la même rangée.

SELECT renvoie les lignes et sa clause WHERE fonctionne sur les lignes. Puisque vous avez beaucoup de valeurs dans une seule ligne, vous allez avoir du mal à extraire conditionnellement un sous-objet json avec cette approche.

Les deux autres requêtes exécutées avec succès mais ne produisent aucun résultat:

SELECT data ->> 'Questions' AS Questions FROM db.my_table 
WHERE (data -> 'Questions' ->> 'QuestionId')::numeric = 6; 

SELECT data ->> 'Questions' AS Questions FROM db.my_table 
WHERE data -> 'Questions' ->> 'QuestionId' = '6' 

Dans les deux cas, vous l'indexation d'un champ inexistant dans votre blob JSON. Questions est un tableau, donc l'indexer avec une chaîne ne fonctionne pas, mais json est assez lâche pour vous permettre d'essayer. En d'autres termes, la clause WHERE data -> 'Questions' ->> 'QuestionId' = '6' ne correspond à aucune ligne.

L'astuce consiste à faire de chaque question une suite. C'est un peu compliqué, mais pas trop difficile à faire. Tout d'abord, regardez la fonction json_array_elements, qui semble fonctionner correctement - elle prend un tableau json et renvoie chaque élément en tant que ligne avec une seule colonne (appelée "valeur"). À première vue, il semble que vous devriez être en mesure de faire quelque chose de la forme:

SELECT value FROM json_array_elements(...) 
WHERE value ->> 'QuestionId'::numeric = 6; 

Malheureusement, ce n'est pas tout à fait aussi simple que cela. Voici une requête qui fonctionne:

SELECT datatable.question 
    FROM my_table, 
    json_array_elements(my_table.data -> 'Questions') AS datatable(question) 
WHERE (question->>'QuestionId')::numeric = 6; 

Bon, nous allons le décomposer un peu. En fin de compte, il renvoie le champ question du tableau datatable que nous créons à l'aide de l'appel json_array_elements. question est un objet json, et nous filtrons pour retourner uniquement les objets où QuestionID == 6. Notez cependant que nous sélectionnons également my_table, car c'est finalement de là que proviennent les données.Dans ce cas, nous effectuons une jointure cartésienne non contrainte entre my_table et datatable. Ce n'est pas bon, et ne sera probablement pas bien à l'échelle. Mais cela fonctionne pour notre situation actuelle de seulement quelques lignes.

L'espoir qui vous donne une place pour commencer.

+0

Hey @jmelesky merci pour cette réponse! Mais j'ai l'erreur suivante: 'ERREUR: la fonction json_array_elements (jsonb) n'existe pas LINE 1: sélectionnez json_array_elements (data -> 'Questions') de db.m ... ^ ASTUCE: Aucune fonction ne correspond à la donnée types de nom et d'argument. Vous devrez peut-être ajouter des conversions de types explicites. ********** Erreur ********** 'lorsque je cours la dernière requête (cartésienne). Ma colonne est un jsonb ... – IgoR

+1

Si votre colonne est jsonb, pas json, alors vous devriez utiliser la fonction jsonb au lieu de la fonction json. Comme le montre [documentation] (https://www.postgresql.org/docs/current/static/functions-json.html#FUNCTIONS-JSON-PROCESSING-TABLE), cela signifie simplement utiliser 'jsonb_array_elements', au lieu de' json_array_elements '. À l'avenir, fournir plus d'informations sur votre schéma dans votre question vous aidera. – jmelesky

+0

! Merci beaucoup, ça marche maintenant ... Je vais mettre quelques informations sur mon schéma afin que vous puissiez voir une "meilleure solution" possible. :) – IgoR