Aller au contenu

Collections et UDT


Objectifs

  • Manipuler les Collections
  • Manipuler les UDT
  • Utiliser une modélisation spécifique NOSQL (dénormalisé)

Les collections

Les collections permettent une représentation dénormalisée des données. C'est une solution pour contourner l'absence d'opération de jointure.

Cassandra définit 3 types de collections :

  • MAP : représentant un dictionnaire
  • SET : représentant un ensemble non ordonnées
  • LIST : représentant un liste ordonnée de valeurs

Syntaxe

  • Déclaration :
    MAP '<' cql_type',' cql_type'>'
    SET '<' cql_type '>'
    LIST '<' cql_type'>'
    
  • Litéraux :
    map_literal::= '\{' [ term ':' term (',' term : term)* ] '}'
    set_literal::= '\{' [ term (',' term)* ] '}'
    list_literal::= '[' [ term (',' term)* ] ']'
    
  • Accès :
    • MAP : map_name['key']
    • LIST : list_name[index]
    • SET : pas d'accès aux éléments
  • Opérations :
    • + : Ajout d'éléments
    • - : Supression d'éléments
    • = : Affectation
    • CONTAINS : tester l'appartenance d'un élément à une collection ❗ Nécessite un index sur la colonne
    • CONTAINS KEY : tester la présence d'une clé pour le type MAP ❗ Nécessite un index sur la colonne
  • Index
    • LIST et SET :
      CREATE INDEX index_name ON table_name(column);
      
    • MAP :
      CREATE INDEX index_name ON table_name(KEYS (column));
      CREATE INDEX index_name ON table_name( VALUES (column));
      

Exemples

1⃣ Créer une table contenant des collections

CREATE TABLE personne (
    id INT,
    nom VARCHAR,
    prenom VARCHAR,
    evenements MAP <text, date>,
    emails SET <text>,
    enfants LIST <text>,
    PRIMARY KEY (id)
);

2⃣ Création d'index

CREATE INDEX idx_emails ON personne(emails);
CREATE INDEX idx_enfants ON personne(enfants);
CREATE INDEX idx_k_evenement ON personne(KEYS(evenements));
CREATE INDEX idx_v_evenement ON personne(VALUES(evenements));
3⃣ Insertion de données

INSERT INTO personne(id, nom, prenom, evenements, emails, enfants)
VALUES (1, 'DELON','Alain',
    {'naissance': '1935-11-08'},
    {'alain@laposte.fr','alain.delon@laposte.fr'},
    ['Anthony','Anouchka','Alain']);
4⃣ Mise à jour

-- Ajouter la date du mariage dans les événements et supprimer un email et modifier le prénom d'un enfant
UPDATE personne SET evenements = evenements + {'mariage':'1964-08-13'},
emails = emails - {'alain@laposte.fr'}, enfants[2] = 'Alain-Fabien' 
WHERE id = 1;

-- Supprimer le premier enfant
DELETE enfants[0] FROM personne WHERE id = 1;
5⃣ Recherche

-- Chercher la date de naissance des personnes ayant un email alain.delon@laposte.fr
SELECT evenements['naissance'] FROM personne WHERE emails CONTAINS 'alain.delon@laposte.fr';

-- Chercher les noms et prénoms des personnes ayant un événement mariage
SELECT nom, prenom FROM personne WHERE evenements CONTAINS KEY 'mariage';

Les types utilisateurs (UDT)

Les types définis par l'utilisateur sont semblables aux enregistrements dans les langages de programmation. Il se composnt de champs.

Syntaxe

La manipulation des UDT dans CQL est similaire à celle des tables.

  • Définition
    CREATE TYPE [ IF NOT EXISTS ] <udt_name>(<champ> <cql_type>, ...);
    
  • Modification
    ALTER TYPE <udt_name> ADD <champ> <cql_type>;
    ALTER TYPE <udt_name> RENAME <ancien_nom> TO <nouveau_nom>;
    
  • Suppression
    DROP TYPE <udt_name>;
    
  • Litéraux
    {champ1:valeur, ...}
    
  • UDT et Collections
    CREATE TABLE T(collection MAP<text frozen<udt>>...);
    

Exemple

Exemple 1

CREATE TYPE address (
    street text,
    city text,
    zip text
);

CREATE TABLE user (
    name text PRIMARY KEY,
    home address
);

INSERT INTO user (name, home) 
VALUES (
    'z3 Pr3z1den7',
    { 
        street: '1600 Pennsylvania Ave NW',
        city: 'Washington',
        zip: '20500' 
    }
);

Exemple 2

CREATE TYPE phone (
    country_code int,
    number text,
);

CREATE TYPE address (
    street text,
    city text,
    zip text,
    phones map<text, phone>
);

CREATE TABLE user (
    name text PRIMARY KEY,
    addresses map<text, frozen<address>>
);

INSERT INTO user (name, addresses)
VALUES (
    'z3 Pr3z1den7', 
    {
        'home' : {
            street: '1600 Pennsylvania Ave NW',
            city: 'Washington',
            zip: '20500',
            phones: { 
                'cell' : { country_code: 1, number: '202 456‐1111' },
                'landline' : { country_code: 1, number: '...' }
            }
        },
        'work' : {
            street: '1600 Pennsylvania Ave NW',
            city: 'Washington',
            zip: '20500',
            phones: { 'fax' : { country_code: 1, number: '...' } }
        }
    }
);

Exercice

Dans l’archive fournie avec la section interrogation, nous allons utiliser le fichier InspectionsRestaurant.json dont la structure JSON est comme suit :

{
    "idRestaurant": 40373938,
    "restaurant": {
        "name": "IHOP",
        "borough": "BRONX",
        "buildingnum": "5655",
        "street": "BROADWAY",
        "zipcode": "10463",
        "phone": "7185494565",
        "cuisineType": "American"
    },
    "inspectionDate": "2016-08-16",
    "violationCode": "04L",
    "violationDescription": "Evidence of mice or live mice present in facility's food and/or non-food areas.",
    "criticalFlag": "Critical",
    "score": 15,
    "grade": ""
}

Partie 1

  1. Définir le modèle de document associant les restaurants et leurs inspections, en utilisant les types imbriqués, et créer la table InspectionRestaurant selon le format JSON précédent. Dans cette modélisation, qui est une solution au problème de manque de jointure, les informations du restaurant sont imbriquées dans chacune de ses inspections.
  2. Insérer un document dans la table.
  3. Faire l’import avec l’utilitaire d’insertion de documents JSON.

    1. copier le fichier JSON et JAR dans le container
    2. importer avec l'utilitaire selon la syntaxe suivante

    java -jar JSonFile2Cassandra [-host <host>] [-port <port>] [-keyspace <keyspace>] [-columnFamily <columnFamily>] [file]
    
    4. Créer un index sur le Grade de la table InspectionRestaurant, puis trouver les restaurants ayant reçu le grade “A” au moins une fois.

Partie 2

Maintenant, on veut pouvoir rechercher les restaurants par leur quartier (borough).

  1. Est-ce possible sur le schéma précédent ?
  2. Proposer une modélisation adaptée, et créer la table. La solution consiste à ajouter une collection de type map, avec la date d’insertion comme clé, dans chaque restaurant représentant la relation un-à-plusieurs qui lie un restaurant à ses inspections.
  3. Insérer des données dans la nouvelle table, soit directement, soit avec l’utilitaire d’import.
  4. Trouver tous les restaurants du Bronx.
  5. Maintenant, on veut, sur cette seconde table, trouver tous les restaurants ayant reçu une note "A". Est-ce possible ? Chercher une solution permise par le fait que nous avons utilisé le type map.