Cette page explique comment gérer les données sans schéma dans Spanner Graph. Il fournit également des bonnes pratiques et des conseils de dépannage. Nous vous recommandons de vous familiariser avec le schéma et les requêtes Spanner Graph.
La gestion des données sans schéma vous permet de créer une définition de graphique flexible. Vous pouvez ajouter, modifier ou supprimer des définitions de types de nœuds et d'arêtes sans modifier le schéma. Cette approche prend en charge le développement itératif et réduit les frais généraux de gestion de schéma, tout en préservant l'expérience de requête de graphique familière.
La gestion des données sans schéma est utile dans les scénarios suivants :
Gérer des graphiques avec des modifications fréquentes, telles que les mises à jour et les ajouts de libellés et de propriétés d'éléments.
Graphiques comportant de nombreux types de nœuds et d'arêtes, ce qui rend la création et la gestion des tables d'entrée fastidieuses.
Pour savoir quand utiliser la gestion de données sans schéma, consultez Points à prendre en compte pour la gestion de données sans schéma.
Modéliser des données sans schéma
Spanner Graph vous permet de créer un graphique à partir de tables qui mappe les lignes aux nœuds et aux arêtes.
Au lieu d'utiliser des tables distinctes pour chaque type d'élément, la modélisation de données sans schéma utilise généralement une seule table de nœuds et une seule table d'arêtes avec une colonne STRING
pour le libellé et une colonne JSON
pour les propriétés.
Créer des tables d'entrée
Vous pouvez créer une seule table GraphNode
et une seule table GraphEdge
pour stocker des données sans schéma, comme illustré dans l'exemple suivant. Les noms de tables sont fournis à titre d'exemple. Vous pouvez choisir les vôtres.
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id);
CREATE TABLE GraphEdge (
id INT64 NOT NULL,
dest_id INT64 NOT NULL,
edge_id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id, dest_id, edge_id),
INTERLEAVE IN PARENT GraphNode;
Cet exemple effectue les actions suivantes :
Stocke tous les nœuds dans une seule table,
GraphNode
, identifiée par unid
unique.Stocke tous les nœuds dans une seule table,
GraphEdge
, identifiée par une combinaison unique de source (id
), de destination (dest_id
) et de son propre identifiant (edge_id
). Unedge_id
est inclus dans la clé primaire pour autoriser plusieurs nœuds d'une paireid
àdest_id
.
Les tables de nœuds et d'arêtes ont chacune leurs propres colonnes label
et properties
.
Ces colonnes sont respectivement de type STRING
et JSON
.
Pour en savoir plus sur les choix de clés pour la gestion des données sans schéma, consultez Définitions des clés primaires pour les nœuds et les arêtes.
Créer un graphique de propriétés
L'instruction CREATE PROPERTY GRAPH mappe les tables d'entrée de la section précédente en tant que nœuds et arêtes. Utilisez les clauses suivantes pour définir des libellés et des propriétés pour les données sans schéma :
DYNAMIC LABEL
: crée le libellé d'un nœud ou d'un bord à partir d'une colonneSTRING
de la table d'entrée.DYNAMIC PROPERTIES
: crée des propriétés d'un nœud ou d'un bord à partir d'une colonneJSON
de la table d'entrée.
L'exemple suivant montre comment créer un graphique à l'aide de ces clauses :
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
)
EDGE TABLES (
GraphEdge
SOURCE KEY (id) REFERENCES GraphNode(id)
DESTINATION KEY (dest_id) REFERENCES GraphNode(id)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
Définir des libellés dynamiques
La clause DYNAMIC LABEL
désigne une colonne de type de données STRING
pour stocker les valeurs de libellé.
Par exemple, dans une ligne GraphNode
, si la colonne label
a une valeur person
, elle est mappée à un nœud Person
dans le graphique. De même, dans une ligne GraphEdge
, si la colonne de libellé a une valeur de owns
, elle correspond à un bord Owns
dans le graphique.
Pour en savoir plus sur les limites d'utilisation des libellés dynamiques, consultez Limites.
Définir des propriétés dynamiques
La clause DYNAMIC PROPERTIES
désigne une colonne de type de données JSON
pour stocker les propriétés. Les clés JSON représentent les noms de propriétés et les valeurs JSON représentent les valeurs de propriétés.
Par exemple, lorsque la colonne properties
d'une ligne GraphNode
a la valeur JSON '{"name": "David", "age": 43}'
, Spanner la mappe à un nœud dont les propriétés age
et name
ont respectivement les valeurs 43
et "David"
.
Points à prendre en compte pour la gestion des données sans schéma
Vous ne souhaiterez peut-être pas utiliser la gestion des données sans schéma dans les scénarios suivants :
- Les types de nœuds et d'arêtes de vos données graphiques sont bien définis, ou leurs libellés et propriétés ne nécessitent pas de mises à jour fréquentes.
- Vos données sont déjà stockées dans Spanner et vous préférez créer des graphiques à partir de tables existantes plutôt que d'introduire de nouvelles tables de nœuds et d'arêtes dédiées.
- Les limites des données sans schéma empêchent leur adoption.
De plus, si votre charge de travail est très sensible aux performances d'écriture, en particulier lorsque les propriétés sont fréquemment mises à jour, il est plus efficace d'utiliser des propriétés définies par le schéma avec des types de données primitifs tels que STRING
ou INT64
plutôt que des propriétés dynamiques avec le type JSON
.
Pour savoir comment définir le schéma du graphique sans utiliser de libellés ni de propriétés de données dynamiques, consultez la présentation du schéma Spanner Graph.
Interroger des données de graphiques sans schéma
Vous pouvez interroger des données de graphe sans schéma à l'aide du langage GQL (Graph Query Language). Vous pouvez utiliser les exemples de requêtes dans l'aperçu des requêtes Spanner Graph et la documentation de référence sur GQL avec quelques modifications.
Faire correspondre des nœuds et des arêtes à l'aide de libellés
Vous pouvez faire correspondre des nœuds et des arêtes à l'aide de l'expression de libellé dans GQL.
La requête suivante correspond aux nœuds et aux arêtes connectés dont la colonne de libellé contient les valeurs account
et transfers
.
GRAPH FinGraph
MATCH (a:Account {id: 1})-[t:Transfers]->(d:Account)
RETURN COUNT(*) AS result_count;
Propriétés d'accès
Les modèles Spanner utilisent les clés et les valeurs de premier niveau du type de données JSON
comme propriétés, telles que age
et name
dans l'exemple suivant.
JSON document |
Properties |
|
|
|
L'exemple suivant montre comment accéder à la propriété name
à partir du nœud Person
.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN person.name;
La requête renvoie des résultats semblables à ce qui suit :
JSON"Tom"
Convertir les types de données des propriétés
Spanner traite les propriétés comme des valeurs du type de données JSON. Dans certains cas, comme pour les comparaisons avec des types SQL, vous devez d'abord convertir les propriétés en type SQL.
Dans l'exemple suivant, la requête effectue les conversions de type de données suivantes :
- Convertit la propriété
is_blocked
en type booléen pour évaluer l'expression. - Convertit la propriété
order_number_str
en type chaîne et la compare à la valeur littérale"302290001255747"
. - Utilise la fonction LAX_INT64 pour convertir
order_number_str
en entier de manière sécurisée en tant que type renvoyé.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->()
WHERE BOOL(a.is_blocked) AND STRING(t.order_number_str) = "302290001255747"
RETURN LAX_INT64(t.order_number_str) AS order_number_as_int64;
Elle renvoie des résultats semblables aux suivants :
+-----------------------+
| order_number_as_int64 |
+-----------------------+
| 302290001255747 |
+-----------------------+
Dans les clauses telles que GROUP BY
et ORDER BY
, vous devez également convertir le type de données JSON. L'exemple suivant convertit la propriété city
en type chaîne, ce qui vous permet de l'utiliser pour le regroupement.
GRAPH FinGraph
MATCH (person:Person {country: "South Korea"})
RETURN STRING(person.city) as person_city, COUNT(*) as cnt
LIMIT 10
Conseils pour convertir les types de données JSON en types de données SQL :
- Les convertisseurs stricts, tels que
INT64
, effectuent des vérifications rigoureuses du type et de la valeur. Utilisez des convertisseurs stricts lorsque le type de données JSON est connu et appliqué, par exemple en utilisant des contraintes de schéma pour appliquer le type de données de la propriété. - Les convertisseurs flexibles, tels que
LAX_INT64
, convertissent la valeur de manière sécurisée lorsque cela est possible et renvoientNULL
lorsque la conversion n'est pas possible. Utilisez des convertisseurs flexibles lorsqu'une vérification rigoureuse n'est pas requise ou que les types sont difficiles à appliquer.
Pour en savoir plus sur la conversion des données, consultez les conseils de dépannage.
Filtrer par valeurs de propriété
Dans les filtres de propriété, Spanner traite les paramètres de filtre comme des valeurs de type de données JSON
. Par exemple, dans la requête suivante, Spanner traiteis_blocked
comme un boolean
JSON et order_number_str
comme un string
JSON.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str:"302290001255747"}]->()
RETURN a.id AS account_id;
Elle renvoie des résultats semblables aux suivants :
+-----------------------+
| account_id |
+-----------------------+
| 7 |
+-----------------------+
Le paramètre de filtre doit correspondre au type et à la valeur de la propriété. Par exemple, lorsque le paramètre de filtre order_number_str
est un entier, Spanner ne trouve aucune correspondance, car la propriété est un string
JSON.
GRAPH FinGraph
MATCH (a:Account {is_blocked: false})-[t:Transfers {order_number_str: 302290001255747}]->()
RETURN t.order_number_str;
Accéder aux propriétés JSON imbriquées
Spanner ne modélise pas les clés et valeurs JSON imbriquées en tant que propriétés.
Dans l'exemple suivant, Spanner ne modélise pas les clés JSON city
, state
et country
en tant que propriétés, car elles sont imbriquées sous location
. Toutefois, vous pouvez y accéder à l'aide d'un opérateur d'accès aux champs JSON ou d'un opérateur d'indice JSON.
JSON document |
Properties |
|
|
|
|
|
L'exemple suivant montre comment accéder aux propriétés imbriquées avec l'opérateur d'accès au champ JSON.
GRAPH FinGraph
MATCH (person:Person {id: 1})
RETURN STRING(person.location.city);
Elle renvoie des résultats semblables aux suivants :
"New York"
Modifier des données sans schéma
Spanner Graph mappe les données des tables aux nœuds et aux arêtes du graphique. Lorsque vous modifiez les données d'une table d'entrée, cette modification entraîne directement des mutations dans les données graphiques correspondantes. Pour en savoir plus sur la mutation des données graphiques, consultez Insérer, mettre à jour ou supprimer des données Spanner Graph.
Exemples de requêtes
Cette section fournit des exemples montrant comment créer, mettre à jour et supprimer des données de graphique.
Insérer des données de graphique
L'exemple suivant insère un nœud person
. Les noms de libellés et de propriétés doivent être en minuscules.
INSERT INTO GraphNode (id, label, properties)
VALUES (4, "person", JSON'{"name": "David", "age": 43}');
Mettre à jour les données du graphique
L'exemple suivant met à jour un nœud Account
et utilise la fonction JSON_SET
pour définir sa propriété is_blocked
.
UPDATE GraphNode
SET properties = JSON_SET(
properties,
'$.is_blocked', false
)
WHERE label = "account" AND id = 16;
L'exemple suivant met à jour un nœud person
avec un nouvel ensemble de propriétés.
UPDATE GraphNode
SET properties = JSON'{"name": "David", "age": 43}'
WHERE label = "person" AND id = 4;
L'exemple suivant utilise la fonction JSON_REMOVE
pour supprimer la propriété is_blocked
d'un nœud Account
. Après l'exécution, toutes les autres propriétés existantes restent inchangées.
UPDATE GraphNode
SET properties = JSON_REMOVE(
properties,
'$.is_blocked'
)
WHERE label = "account" AND id = 16;
Supprimer les données du graphique
L'exemple suivant supprime l'arête Transfers
sur les nœuds Account
qui ont été transférés vers des comptes bloqués.
DELETE FROM GraphEdge
WHERE label = "transfers" AND id IN {
GRAPH FinGraph
MATCH (a:Account)-[:Transfers]->{1,2}(:Account {is_blocked: TRUE})
RETURN a.id
}
Limitations connues
Cette section liste les limites de la gestion des données sans schéma.
Exigence concernant les tableaux uniques pour les libellés dynamiques
Vous ne pouvez avoir qu'une seule table de nœuds si un libellé dynamique est utilisé dans sa définition. Cette restriction s'applique également à la table Edge. Spanner n'autorise pas les éléments suivants :
- Définition d'une table de nœuds avec un libellé dynamique à côté d'autres tables de nœuds.
- Définition d'une table d'arêtes avec un libellé dynamique à côté d'autres tables d'arêtes.
- Définir plusieurs tables de nœuds ou d'arêtes qui utilisent chacune un libellé dynamique.
Par exemple, le code suivant échoue lorsqu'il tente de créer plusieurs nœuds de graphique avec des libellés dynamiques.
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNodeOne
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties),
GraphNodeTwo
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties),
Account
LABEL Account PROPERTIES(create_time)
)
EDGE TABLES (
...
);
Les noms des libellés doivent être en minuscules.
Pour que la correspondance fonctionne, vous devez stocker les valeurs de chaîne des libellés en minuscules. Nous vous recommandons d'appliquer cette règle dans le code de l'application ou à l'aide de contraintes de schéma.
Bien que les valeurs de chaîne des libellés doivent être stockées en minuscules, elles ne sont pas sensibles à la casse lorsque vous les référencez dans une requête.
L'exemple suivant montre comment insérer des libellés en minuscules :
INSERT INTO GraphNode (id, label) VALUES (1, "account");
INSERT INTO GraphNode (id, label) VALUES (2, "account");
Vous pouvez utiliser des libellés non sensibles à la casse pour faire correspondre GraphNode
ou GraphEdge
.
GRAPH FinGraph
MATCH (accnt:Account {id: 1})-[:Transfers]->(dest_accnt:Account)
RETURN dest_accnt.id;
Les noms de propriétés doivent être en minuscules
Vous devez stocker les noms de propriétés en minuscules. Nous vous recommandons d'appliquer cette règle dans le code de l'application ou à l'aide de contraintes de schéma.
Bien que les noms de propriétés doivent être stockés en minuscules, ils ne sont pas sensibles à la casse lorsque vous les référencez dans votre requête.
L'exemple suivant insère les propriétés name
et age
en minuscules.
INSERT INTO GraphNode (id, label, properties)
VALUES (25, "person", JSON '{"name": "Kim", "age": 27}');
Dans le texte de la requête, les noms de propriétés ne sont pas sensibles à la casse. Par exemple, vous pouvez utiliser Age
ou age
pour accéder à la propriété.
GRAPH FinGraph
MATCH (n:Person {Age: 27})
RETURN n.id;
Autres limites
- Spanner ne modélise que les clés de premier niveau du type de données
JSON
en tant que propriétés. - Les types de données de propriété doivent être conformes aux spécifications du type JSON Spanner.
Bonnes pratiques pour les données sans schéma
Cette section décrit les bonnes pratiques qui vous aident à modéliser les données sans schéma.
Définir des clés primaires pour les nœuds et les arêtes
La clé d'un nœud doit être unique pour tous les nœuds du graphique. Par exemple, en tant que colonne INT64
ou chaîne UUID
.
Si plusieurs arêtes existent entre deux nœuds, ajoutez un identifiant unique pour l'arête. L'exemple de schéma utilise une colonne INT64
edge_id
de logique d'application.
Lorsque vous créez le schéma pour les tables de nœuds et d'arêtes, vous pouvez inclure la colonne label
en tant que colonne de clé primaire si la valeur est immuable. Dans ce cas, la clé composite formée par toutes les colonnes clés doit être unique pour tous les nœuds ou arêtes. Cette technique améliore les performances des requêtes qui ne sont filtrées que par libellé.
Pour en savoir plus sur le choix d'une clé primaire, consultez Choisir une clé primaire.
Créer un index secondaire pour une propriété fréquemment consultée
Pour améliorer les performances des requêtes pour une propriété fréquemment utilisée dans les filtres, créez un index secondaire par rapport à une colonne de propriété générée. Ensuite, utilisez-le dans un schéma de graphique et des requêtes.
L'exemple suivant montre comment ajouter une colonne age
générée à la table GraphNode
pour un nœud person
. La valeur est NULL
pour les nœuds sans libellé person
.
ALTER TABLE GraphNode
ADD COLUMN person_age INT64 AS
(IF (label = "person", LAX_INT64(properties.age), NULL));
L'instruction LDD suivante crée ensuite un NULL FILTERED INDEX
pour person_age
et l'entrelace dans la table GraphNode
pour un accès local.
CREATE NULL_FILTERED INDEX IdxPersonAge
ON GraphNode(id, label, person_age), INTERLEAVE IN GraphNode;
Le tableau GraphNode
inclut de nouvelles colonnes disponibles en tant que propriétés de nœud de graphique. Pour refléter cela dans la définition du graphique de propriétés, utilisez l'instruction CREATE OR
REPLACE PROPERTY GRAPH
. Cela recompile la définition et inclut la nouvelle colonne person_age
en tant que propriété.
Pour en savoir plus, consultez Mettre à jour des définitions de nœuds ou d'arêtes existantes.
L'instruction suivante recompile la définition et inclut la nouvelle colonne person_age
en tant que propriété.
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
)
EDGE TABLES (
GraphEdge
SOURCE KEY (id) REFERENCES GraphNode (id)
DESTINATION KEY (dest_id) REFERENCES GraphNode (id)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
L'exemple suivant exécute une requête avec la propriété indexée.
GRAPH FinGraph
MATCH (person:Person {person_age: 43})
RETURN person.id, person.name;
Vous pouvez éventuellement exécuter la commande ANALYZE
après la création de l'index afin que l'optimiseur de requêtes soit mis à jour avec les dernières statistiques de la base de données.
Utiliser des contraintes de vérification pour l'intégrité des données
Spanner accepte les objets de schéma tels que les contraintes de vérification pour assurer l'intégrité des données de libellé et de propriété. Cette section liste les recommandations concernant les contraintes de vérification que vous pouvez utiliser avec les données sans schéma.
Appliquer des valeurs de libellé
Nous vous recommandons d'utiliser NOT NULL
dans la définition de la colonne de libellés pour éviter les valeurs de libellés indéfinies.
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON,
) PRIMARY KEY (id);
Appliquer des valeurs de libellé et des noms de propriété en minuscules
Étant donné que les noms de libellés et de propriétés doivent être stockés en minuscules, procédez de l'une des manières suivantes :
- Appliquez la vérification dans la logique de l'application.
- Créez des contraintes de vérification dans le schéma.
Au moment de la requête, le nom du libellé et de la propriété ne sont pas sensibles à la casse.
L'exemple suivant montre comment ajouter une contrainte de libellé de nœud à la table GraphNode
pour s'assurer que le libellé est en minuscules.
ALTER TABLE GraphNode ADD CONSTRAINT NodeLabelLowerCaseCheck
CHECK(LOWER(label) = label);
L'exemple suivant montre comment ajouter une contrainte de vérification au nom de la propriété d'arête. La vérification utilise JSON_KEYS
pour accéder aux clés de premier niveau.
COALESCE
convertit la sortie en tableau vide si JSON_KEYS
renvoie NULL
, puis vérifie que chaque clé est en minuscules.
ALTER TABLE GraphEdge ADD CONSTRAINT EdgePropertiesLowerCaseCheck
CHECK(NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
Imposer l'existence des propriétés
Créez une contrainte qui vérifie si une propriété existe pour un libellé.
Dans l'exemple suivant, la contrainte vérifie si un nœud person
possède une propriété name
.
ALTER TABLE GraphNode
ADD CONSTRAINT NameMustExistForPersonConstraint
CHECK (IF(label = 'person', properties.name IS NOT NULL, TRUE));
Appliquer des propriétés uniques
Créez des contraintes basées sur les propriétés qui vérifient si la propriété d'un nœud ou d'un bord est unique parmi les nœuds ou les bords portant le même libellé. Pour ce faire, utilisez un INDEX UNIQUE sur les colonnes générées des propriétés.
Dans l'exemple suivant, l'index unique vérifie que les propriétés name
et country
combinées sont uniques pour tout nœud person
.
Ajoutez une colonne générée pour
PersonName
.ALTER TABLE GraphNode ADD COLUMN person_name STRING(MAX) AS (IF(label = 'person', STRING(properties.name), NULL)) Hidden;
Ajoutez une colonne générée pour
PersonCountry
.ALTER TABLE GraphNode ADD COLUMN person_country STRING(MAX) AS (IF(label = 'person', STRING(properties.country), NULL)) Hidden;
Créez un index unique
NULL_FILTERED
pour les propriétésPersonName
etPersonCountry
.CREATE UNIQUE NULL_FILTERED INDEX NameAndCountryMustBeUniqueForPerson ON GraphNode (person_name, person_country);
Appliquer le type de données de propriété
Appliquez un type de données de propriété à l'aide d'une contrainte de type de données sur une valeur de propriété pour un libellé, comme illustré dans l'exemple suivant. Cet exemple utilise la fonction JSON_TYPE
pour vérifier que la propriété name
du libellé person
utilise le type STRING
.
ALTER TABLE GraphNode
ADD CONSTRAINT PersonNameMustBeStringTypeConstraint
CHECK (IF(label = 'person', JSON_TYPE(properties.name) = 'string', TRUE));
Combiner des libellés définis et dynamiques
Spanner permet aux nœuds du graphique de propriétés d'avoir à la fois des libellés définis (dans le schéma) et des libellés dynamiques (dérivés des données). Personnalisez les libellés pour profiter de cette flexibilité.
Prenons l'exemple du schéma suivant, qui montre la création de la table GraphNode
:
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
LABEL Entity -- Defined label
DYNAMIC LABEL (label) -- Dynamic label from data column 'label'
DYNAMIC PROPERTIES (properties)
);
Ici, chaque nœud créé à partir de GraphNode
possède le libellé defined Entity
. De plus, chaque nœud possède un libellé dynamique déterminé par la valeur de sa colonne "Libellé".
Écrivez ensuite des requêtes qui correspondent aux nœuds en fonction du type de libellé. Par exemple, la requête suivante recherche des nœuds à l'aide du libellé Entity
défini :
GRAPH FinGraph
MATCH (node:Entity {id: 1}) -- Querying by the defined label
RETURN node.name;
Même si cette requête utilise le libellé défini Entity
, n'oubliez pas que le nœud correspondant comporte également un libellé dynamique basé sur ses données.
Exemples de schéma
Utilisez les exemples de schémas de cette section comme modèles pour créer vos propres schémas. Voici les principaux composants du schéma :
- Créer des tables d'entrée de graphiques
- Créer un graphique de propriété
- Facultatif : index d'inversion de la traversée des arêtes pour améliorer les performances de la traversée inverse
- Facultatif : index de libellés pour améliorer les performances des requêtes par libellé
- Facultatif : contraintes de schéma à appliquer aux libellés en minuscules et aux noms de propriétés
L'exemple suivant montre comment créer des tables d'entrée et un graphique de propriétés :
CREATE TABLE GraphNode (
id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON
) PRIMARY KEY (id);
CREATE TABLE GraphEdge (
id INT64 NOT NULL,
dest_id INT64 NOT NULL,
edge_id INT64 NOT NULL,
label STRING(MAX) NOT NULL,
properties JSON
) PRIMARY KEY (id, dest_id, edge_id),
INTERLEAVE IN PARENT GraphNode;
CREATE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
)
EDGE TABLES (
GraphEdge
SOURCE KEY (id) REFERENCES GraphNode(id)
DESTINATION KEY (dest_id) REFERENCES GraphNode(id)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
L'exemple suivant utilise un index pour améliorer la traversée des arêtes inversées. La clause STORING (properties)
inclut une copie des propriétés des arêtes, ce qui accélère les requêtes qui filtrent ces propriétés. Vous pouvez omettre la clause STORING (properties)
si vos requêtes n'en bénéficient pas.
CREATE INDEX R_EDGE ON GraphEdge (dest_id, id, edge_id) STORING (properties),
INTERLEAVE IN GraphNode;
L'exemple suivant utilise un index de libellés pour accélérer la mise en correspondance des nœuds par libellés.
CREATE INDEX IDX_NODE_LABEL ON GraphNode (label);
L'exemple suivant ajoute des contraintes qui imposent des libellés et des propriétés en minuscules. Les deux derniers exemples utilisent la fonction JSON_KEYS
. Si vous le souhaitez, vous pouvez appliquer la vérification des minuscules dans la logique de l'application.
ALTER TABLE GraphNode ADD CONSTRAINT node_label_lower_case
CHECK(LOWER(label) = label);
ALTER TABLE GraphEdge ADD CONSTRAINT edge_label_lower_case
CHECK(LOWER(label) = label);
ALTER TABLE GraphNode ADD CONSTRAINT node_property_keys_lower_case
CHECK(
NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
ALTER TABLE GraphEdge ADD CONSTRAINT edge_property_keys_lower_case
CHECK(
NOT array_includes(COALESCE(JSON_KEYS(properties, 1), []), key->key<>LOWER(key)));
Optimiser les mises à jour par lot des propriétés dynamiques avec le langage LMD
La modification des propriétés dynamiques à l'aide de fonctions telles que JSON_SET
et JSON_REMOVE
implique des opérations de lecture/modification/écriture. Cela peut entraîner des coûts plus élevés que la mise à jour des propriétés de type STRING
ou INT64
.
Si les charges de travail impliquent des mises à jour par lot des propriétés dynamiques à l'aide du langage LMD, suivez les recommandations ci-dessous pour améliorer les performances :
Mettez à jour plusieurs lignes dans une seule instruction LMD au lieu de les traiter individuellement.
Lorsque vous mettez à jour une large plage de clés, regroupez et triez les lignes concernées par clés primaires. La mise à jour de plages non chevauchantes avec chaque LMD réduit la contention des verrous.
Utilisez des paramètres de requête dans les instructions LMD au lieu de les coder en dur pour améliorer les performances.
En se basant sur ces suggestions, l'exemple suivant montre comment mettre à jour la propriété is_blocked
pour 100 nœuds dans une seule instruction LMD. Les paramètres de requête incluent les suivants :
@node_ids
: clés des lignesGraphNode
, stockées dans un paramètreARRAY
. Le cas échéant, le regroupement et le tri des DML permettent d'améliorer les performances.@is_blocked_values
: valeurs correspondantes à mettre à jour, stockées dans un paramètreARRAY
.
UPDATE GraphNode
SET properties = JSON_SET(
properties,
'$.is_blocked',
CASE id
WHEN @node_ids[OFFSET(0)] THEN @is_blocked_values[OFFSET(0)]
WHEN @node_ids[OFFSET(1)] THEN @is_blocked_values[OFFSET(1)]
...
WHEN @node_ids[OFFSET(99)] THEN @is_blocked_values[OFFSET(99)]
END,
create_if_missing => TRUE)
WHERE id IN UNNEST(@node_ids)
Résoudre les problèmes
Cette section explique comment résoudre les problèmes liés aux données sans schéma.
La propriété apparaît plusieurs fois dans le résultat TO_JSON
.
Problème
Le nœud suivant modélise les propriétés birthday
et name
en tant que propriétés dynamiques dans sa colonne JSON
. Les propriétés birthday
et name
en double apparaissent dans le résultat JSON de l'élément graphique.
GRAPH FinGraph
MATCH (n: Person {id: 14})
RETURN SAFE_TO_JSON(n) AS n;
Elle renvoie des résultats semblables aux suivants :
{
…,
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 14,
"label": "person",
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex"
}
}
…
}
Cause possible
Par défaut, toutes les colonnes de la table de base sont définies comme propriétés. L'utilisation de TO_JSON
ou SAFE_TO_JSON
pour renvoyer des éléments de graphique entraîne des propriétés en double. Cela se produit parce que la colonne JSON
(properties
) est une propriété définie par le schéma, tandis que les clés de premier niveau de JSON
sont modélisées en tant que propriétés dynamiques.
Solution recommandée
Pour éviter ce comportement, utilisez la clause PROPERTIES ALL COLUMNS EXCEPT
pour exclure la colonne properties
lorsque vous définissez des propriétés dans le schéma, comme illustré dans l'exemple suivant :
CREATE OR REPLACE PROPERTY GRAPH FinGraph
NODE TABLES (
GraphNode
PROPERTIES ALL COLUMNS EXCEPT (properties)
DYNAMIC LABEL (label)
DYNAMIC PROPERTIES (properties)
);
Après la modification du schéma, les éléments de graphique renvoyés du type de données JSON
ne comportent pas de doublons.
GRAPH FinGraph
MATCH (n: Person {id: 1})
RETURN TO_JSON(n) AS n;
Cette requête renvoie les résultats suivants :
{
…
"properties": {
"birthday": "1991-12-21 00:00:00",
"name": "Alex",
"id": 1,
"label": "person",
}
}
Problèmes courants lorsque les valeurs de propriété ne sont pas correctement converties
Pour résoudre les problèmes suivants, utilisez toujours les conversions de valeurs de propriété lorsque vous utilisez une propriété dans une expression de requête.
Comparaison des valeurs de propriété sans conversion
Problème
No matching signature for operator = for argument types: JSON, STRING
Cause possible
La requête ne convertit pas correctement les valeurs de propriété. Par exemple, la propriété name
n'est pas convertie en type STRING
dans la comparaison :
GRAPH FinGraph
MATCH (p:Person)
WHERE p.name = "Alex"
RETURN p.id;
Solution recommandée
Pour résoudre ce problème, utilisez une conversion de valeur avant la comparaison.
GRAPH FinGraph
MATCH (p:Person)
WHERE STRING(p.name) = "Alex"
RETURN p.id;
Elle renvoie des résultats semblables aux suivants :
+------+
| id |
+------+
| 1 |
+------+
Vous pouvez également utiliser un filtre de propriété pour simplifier les comparaisons d'égalité où la conversion de valeur se produit automatiquement. Notez que le type de la valeur ("Alex") doit correspondre exactement au type STRING
de la propriété dans JSON
.
GRAPH FinGraph
MATCH (p:Person {name: 'Alex'})
RETURN p.id;
Elle renvoie des résultats semblables aux suivants :
+------+
| id |
+------+
| 1 |
+------+
Utilisation de la valeur de la propriété RETURN DISTINCT
sans conversion
Problème
Column order_number_str of type JSON cannot be used in `RETURN DISTINCT
Cause possible
Dans l'exemple suivant, order_number_str
n'a pas été converti avant d'être utilisé dans l'instruction RETURN DISTINCT
:
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT t.order_number_str AS order_number_str;
Solution recommandée
Pour résoudre ce problème, utilisez une conversion de valeur avant RETURN DISTINCT
.
GRAPH FinGraph
MATCH -[t:Transfers]->
RETURN DISTINCT STRING(t.order_number_str) AS order_number_str;
Elle renvoie des résultats semblables aux suivants :
+-----------------+
| order_number_str|
+-----------------+
| 302290001255747 |
| 103650009791820 |
| 304330008004315 |
| 304120005529714 |
+-----------------+
Propriété utilisée comme clé de regroupement sans conversion
Problème
Grouping by expressions of type JSON is not allowed.
Cause possible
Dans l'exemple suivant, t.order_number_str
n'est pas converti avant d'être utilisé pour regrouper des objets JSON :
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN t.order_number_str, COUNT(*) AS total_transfers;
Solution recommandée
Pour résoudre ce problème, utilisez une conversion de valeur avant d'utiliser la propriété comme clé de regroupement.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN STRING(t.order_number_str) AS order_number_str, COUNT(*) AS total_transfers;
Elle renvoie des résultats semblables aux suivants :
+-----------------+------------------+
| order_number_str | total_transfers |
+-----------------+------------------+
| 302290001255747 | 1 |
| 103650009791820 | 1 |
| 304330008004315 | 1 |
| 304120005529714 | 2 |
+-----------------+------------------+
Propriété utilisée comme clé de tri sans conversion
Problème
ORDER BY does not support expressions of type JSON
Cause possible
Dans l'exemple suivant, t.amount
n'est pas converti avant d'être utilisé pour trier les résultats :
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN a.Id AS from_account, b.Id AS to_account, t.amount
ORDER BY t.amount DESC
LIMIT 1;
Solution recommandée
Pour résoudre ce problème, effectuez une conversion sur t.amount
dans la clause ORDER BY
.
GRAPH FinGraph
MATCH (a:Account)-[t:Transfers]->(b:Account)
RETURN a.Id AS from_account, b.Id AS to_account, t.amount
ORDER BY DOUBLE(t.amount) DESC
LIMIT 1;
Elle renvoie des résultats semblables aux suivants :
+--------------+------------+--------+
| from_account | to_account | amount |
+--------------+------------+--------+
| 20 | 7 | 500 |
+--------------+------------+--------+
Incompatibilité de type lors de la conversion
Problème
The provided JSON input is not an integer
Cause possible
Dans l'exemple suivant, la propriété order_number_str
est stockée en tant que type de données JSON STRING
. Si vous essayez d'effectuer une conversion vers INT64
, une erreur s'affiche.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Solution recommandée
Pour résoudre ce problème, utilisez le convertisseur de valeur exacte qui correspond au type de valeur.
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE STRING(e.order_number_str) = "302290001255747"
RETURN e.amount;
Elle renvoie des résultats semblables aux suivants :
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
Vous pouvez également utiliser un convertisseur flexible lorsque la valeur peut être convertie au type cible, comme indiqué dans l'exemple suivant :
GRAPH FinGraph
MATCH -[e:Transfers]->
WHERE LAX_INT64(e.order_number_str) = 302290001255747
RETURN e.amount;
Elle renvoie des résultats semblables aux suivants :
+-----------+
| amount |
+-----------+
| JSON"200" |
+-----------+
Étapes suivantes
- Pour en savoir plus sur JSON, consultez Modifier des données JSON et la liste des fonctions JSON.
- Comparer Spanner Graph et openCypher
- Migrez vers Spanner Graph.