#Multi-model

0 Abonnés · 9 Publications

Une base de données multi-modèles est conçue pour prendre en charge plusieurs modèles de données à partir d'un back-end unique et intégré. Les modèles de documents, de graphes, relationnels et clés-valeurs sont des exemples de modèles de données qui peuvent être pris en charge par une base de données multi-modèles.

En savoir plus ici.

Article Guillaume Rongier · Oct 15, 2025 9m read

Vous êtes familier avec les bases de données SQL, mais vous ne connaissez pas IRIS ? Alors lisez la suite...

Il y a environ un an, j'ai rejoint InterSystems, et c'est ainsi que j'ai découvert IRIS.  J'utilise des bases de données depuis plus de 40 ans, la plupart du temps pour des fournisseurs de bases de données, et je pensais qu'IRIS serait similaire aux autres bases de données connues.  Cependant, j'ai été surpris de constater qu'IRIS est très différente par rapport aux autres bases de données, et souvent bien meilleure.  Avec mon premier article dans la communauté Dev, je vais présenter IRIS de manière générale aux personnes qui connaissent déjà d'autres bases de données telles qu'Oracle, SQL Server, Snowflake, PostgeSQL, etc.   J'espère vous rendre les choses plus claires et plus simples et vous faire gagner du temps pour vous lancer.

Tout d'abord, IRIS prend en charge les commandes et la syntaxe SQL de la norme ANSI. Il dispose de tables, de colonnes, de types de données, de procédures stockées, de fonctions...   bref, tout ce qui concerne les relations.  Et vous pouvez utiliser ODBC, JDBC, DBeaver ou tout autre navigateur de base de données que vous préférez.  Donc, oui, la plupart des connaissances et des opérations que vous maîtrisez avec d'autres bases de données fonctionneront très bien avec IRIS.  Youpi!  

Mais qu'en est-il de ces différences que j'ai mentionnées?  Bien, attachez vos ceintures:

Multi-Model: IRIS est une base de données relationnelle, mais c'est aussi une base de données orientée objet, un magasin de documents, et elle prend en charge les vecteurs, les cubes/MDX, et... vous comprenez où je veux en venir.  Ce qui est incroyable, c'est que vous pouvez profiter de tous ces modèles... dans la même instruction SQL!  Et bien souvent, les données peuvent être stockées sous plusieurs de ces structures de données — sans avoir à les sauvegarder à deux reprises — ni à utiliser plusieurs types de bases de données!  Lorsque vous accédez à des données identiques comme s'il s'agissait de modèles différents, InterSystems parle de CDP (Common Data Plane, ou plan de données commun).  C'est pour le moins rare, voire unique, dans le secteur des bases de données.  Personne ne s'intéressait vraiment au CDP jusqu'à ce que la révolution de l'IA rende tout à coup indispensable la prise en charge du multimodèle. Il ne s'agit pas d'une fonctionnalité que d'autres bases de données sont susceptibles d'implémenter, car elle est intégrée au cœur même du noyau.  IRIS facilite l'utilisation des modèles multiples ainsi que des technologies NoSQL et NewSQL pour les utilisateurs SQL:

Pour la base de données Object, vous extrayez une clé-valeur de l'arborescence JSON, qui correspond simplement à la valeur d'une table classique. 

-- exemple de requête dans une base de données ObjectSELECT JSON_OBJECT('product':Product,'sizes':PopularSizes) FROM Sample.Clothing

-- Cela renvoie une liste de paires clé-valeur. Si une paire manque, -- IRIS crée par défaut une paire avec une valeur nulle.

En ce qui concerne Vector, considérez-le simplement comme un autre type de données, mais avec certaines fonctions spéciales qui ne fonctionnent qu'avec le type de données en question 

-- exemple de création d'une table avec une colonne vectorielleCREATETABLE Sample.CustEventV1 (
  CustID INTEGER,
  EventDt DATE,
  Vtest VECTOR(integer,4),
  EventValue NUMERIC(12,2),  
  EventCD VARCHAR(8)) 

-- Vous pouvez utiliser des fonctions telles que VECTOR_DOT_PRODUCT ou VECTOR_COSINE sur Vtest

Taxonomie:  les différents fournisseurs de bases de données n'utilisent pas tels termes comme base de données, schéma, déploiement, instance, etc. exactement de la même manière. 

  • Instance: lorsque vous installez le logiciel de base de données, généralement appelé 'instance' par les éditeurs de bases de données. J'entends parfois ce terme chez InterSystems, mais plus souvent, j'entends le terme 'déploiement'.  Cela s'explique probablement par le fait que le terme 'instance' est déjà utilisé dans le monde orienté objet.  Quel que soit le terme utilisé, la hiérarchie pour les autres bases de données est généralement la suivante:
    • instance/déploiement
      • base de deonnées
        • schéma
          • tables, vues, etc.

            .. ou bien:

  • instance/déploiement (il *s'agit* de la base de données)
    • schéma
      • tables, vues, etc.

            .. mais IRIS est un peu différent dans la mesure où il comporte une couche supplémentaire appelée 'espace de nom':

  • instance/déploiement
    • espace de nom
      • base de données
        • schéma
          • tables, viues, etc.

Un espace de noms est une entité logique qui contient des bases de données. Cependant, plusieurs espaces de noms peuvent contenir la même base de données, il ne s'agit donc peut-être pas d'une hiérarchie.  Il est principalement utilisé pour le contrôle d'accès. Et il peut contenir des bases de données provenant d'autres instances/déploiements!

HA: La haute disponibilité (High Availability) est obtenue grâce à une technique appelée mise en miroir ( mirroring ).  Il s'agit d'un type de réplication où l'intégralité de la base de données est répliquée, y compris le code.  Vous pensez peut-être que vous ne souhaitez pas répliquer l'intégralité de la base de données.  Mais grâce aux espaces de noms, vous pouvez considérer une base de données comme une sorte de schéma et diviser vos données de manière à ce que celles que vous souhaitez mettre en miroir et celles que vous ne souhaitez pas mettre en miroir se trouvent dans des bases de données distinctes. 

Stockage du code: Oui, vous avez parfaitement compris: lorsque vous mettez une base de données en miroir, le code est également transféré!  Il s'agit d'une fonctionnalité très récente pour certaines bases de données à la mode, mais IRIS l'offre depuis toujours. Vous pouvez stocker à la fois le code et les données dans la même base de données, mais le plus souvent, les utilisateurs préfèrent les séparer.

ECP: Bien; c'est le protocole Enterprise Cache Protocol qui rend IRIS vraiment intéressant.  Je ne savais même pas que cela était possible, mais j'ai récemment découvert qu'il existe quelques bases de données NoSQL peu connues qui le permettent.  Avec le protocole ECP vous pouvez configurer le système de manière à ce que différents déploiements puissent partager leurs caches!  Oui, je veux bien dire leurs caches memory réels.. et non pas le partage des données des tables. Pour ce faire, le cache d'un déploiement est automatiquement synchronisé avec celui d'un autre déploiement.  C'est ce qu'on appelle être synchronisé!  C'est très facile à configurer, même si cela doit être compliqué en coulisses. Il s'agit d'un tout autre type de mise à l'échelle horizontale qui peut rendre les applications ultra-rapides.

Translytique: Ce terme, translytique, est utilisé pour décrire une base de données qui est à la fois OLTP et OLAP. Il peut également être appelé HTAP ou HOAP. Parfois, on emploie le terme hybride mais ce terme est trop utilisé dans le monde technologique, je vais donc m'en tenir au terme commençant par T.  Au début, toutes les bases de données étaient translytiques.  Mais avec l'avènement des structures en colonnes et d'autres structures, ainsi que de nouveaux types de stockage (par exemple le stockage en blocs par opposition au stockage en blobs) elles ont été séparées en OLTP et OLAP. Aujourd'hui, les fournisseurs tentent de les réunir à nouveau.   Il est beaucoup plus facile d'ajouter OLAP à un noyau OLTP que de faire l'inverse. Bien sûr, les fournisseurs de solutions DW peuvent ajouter quelques indexations pour les recherches sur une seule ligne, mais je doute qu'ils ajoutent rapidement la prise en charge de fonctionnalités complexes telles que les déclencheurs et les insertions/mises à jour rapides. Le fait est que l'OLTP rapide est plus compliqué à construire que l'OLAP... c'est une technologie beaucoup plus mature. IRIS est une excellente base de données translytique (voir les évaluations des analystes pour comprendre pourquoi). Par exemple, certaines bases de données prennent en charge à la fois le stockage en lignes et en colonnes, mais dans des tables différentes.  IRIS peut disposer de colonnes de stockage en lignes dans la même table que les colonnes de stockage en colonnes.

/* Exemple de combinaison entre stockage en lignes et stockage en colonnes. 
   Toutes les colonnes sont stockées en lignes (par défaut), à l'exception de EventValue.
   EventValue est explicitement définie comme stockage en colonnes. 
   Si vous interrogiez la valeur moyenne de EventValue pour l'ensemble de la table, la réponse serait RAPIDE! */CREATETABLE Sample.CustEvent (
  CustID INTEGER,
  EventDt DATE,
  EventValue NUMERIC(12,2) WITH STORAGETYPE = COLUMNAR,
  EventCD VARCHAR(8))

Installation: Avec d'autres bases de données, vous devez généralement les installer quelque part (sur site ou dans le cloud), comme vous le faites avec Postgres ou SQL Server, ou bien recourir à un SAAS cloud tel que RedShift ou Snowflake. Avec IRIS, cela dépend. Il y a trois moyens d'obtenir IRIS : via une licence, via un service géré ou via Cloud SQL. 

  1. Grâce à une licence, vous pouvez l'installer, le configurer et le maintenir de manière indépendante. Cela peut se faire sur site ou sur le cloud de votre choix. J'ai surtout entendu parler de son utilisation sur AWS, Azure, GCP et TenCent.
  2. Grâce à un service géré, InterSystems installe, configure et assure la maintenance d'IRIS pour vous via un cloud public. 
  3. Grâce à Cloud SQL, il est possible de bénéficier d'un service SAAS (ou devrais-je dire PAAS ? DBAAS ?).  Vous n'avez rien à installer.  Il est conçu pour s'intégrer dans des systèmes plus vastes en tant que module composable, n'offrant qu'un sous-ensemble des fonctionnalités IRIS, telles que SQL et les fonctions d'apprentissage automatique (ML).  La suite de cet article concerne IRIS sous licence ou IRIS géré, et ne concerne pas Cloud SQL.

Langages intégrés: Outre SQL, IRIS a toujours pris en charge un langage orienté objet appelé ObjectScript, qui est un dérivé du langage médical MUMPS. Il s'agit d'un langage très puissant, mais peu connu. Ne vous inquiétez pas, IRIS prend également en charge Python intégré. 

Documentation: Comme IRIS a toujours été étroitement lié à ObjectScript, la documentation a tendance à utiliser une terminologie orientée objet.  Vous trouverez donc des termes simples tels que tables désignés par 'classes persistentes'.  Mais cela semble disparaître de la documentation au fil du temps, et vous pouvez tout simplement ignorer ces termes, sauf si vous souhaitez devenir programmeur IRIS.

IRIS prend donc en charge le langage SQL que vous connaissez et appréciez, ainsi que Python, il est translytique, fonctionne sur site ou dans le cloud, est multimodèle et dispose de fonctionnalités futuristes telles que l'ECP.  Il y a bien d'autres choses encore mais ce sont celles-ci qui m'ont paru les plus importantes et interessantes.  Je pense qu'elles pourraient être utiles à d'autres développeurs SQL et administrateurs de bases de données provenant d'autres produits.   Si c'est votre cas et que vous essayez IRIS, je souhaiterais connaître votre avis sur votre expérience.

0
0 27
Article Iryna Mykhailova · Août 18, 2025 4m read

Récompense d’août pour les articles sur Global Masters a retenu mon attention, et l'un des sujets proposés m'a semblé très intéressant quant à son utilisation future dans mon enseignement. Voici donc ce que j'aimerais dire à mes étudiants à propos des tables dans IRIS et de leur corrélation avec le modèle objet.

Tout d'abord, InterSystems IRIS dispose d'un modèle de données unifié. Cela signifie que lorsque vous travaillez avec des données, vous n'êtes pas enfermé dans un paradigme unique. Les mêmes données sont accessibles et manipulables comme une table SQL traditionnelle, comme un objet natif, ou même comme un tableau multidimensionnel (global). Cela signifie que lorsque vous créez une table SQL, IRIS crée automatiquement une classe d'objet correspondante. Lorsque vous définissez une classe d'objet, IRIS la rend automatiquement disponible sous forme de table SQL. Les données elles-mêmes ne sont stockées qu'une seule fois dans le moteur de stockage multidimensionnel performant d'IRIS. Le moteur SQL et le moteur objet sont simplement des « optiques » différentes pour visualiser et travailler avec les mêmes données.

Commençons par examiner la corrélation entre le modèle relationnel et le modèle objet :

Relationnel Objet
Table Classe
Colonne Propriété
Ligne Objet
Clé primaire Identifiant d'objet

La corrélation n'est pas toujours exacte, car plusieurs tables peuvent représenter une même classe, par exemple. Mais c'est une règle générale.

0
0 23
Article Sylvain Guilbaud · Fév 21, 2025 6m read

InterSystems est à l'avant-garde de la technologie des bases de données depuis sa création, en étant à l'origine d'innovations qui surpassent régulièrement ses concurrents comme Oracle, IBM et Microsoft. En se concentrant sur une conception de noyau efficace et en adoptant une approche sans compromis des performances des données, InterSystems s'est taillé une place de choix dans les applications critiques, garantissant fiabilité, rapidité et évolutivité.

Une histoire d'excellence technique

0
0 66
Article Iryna Mykhailova · Jan 10, 2025 7m read


 

Traçage des données gnSSLocation de mon véhicule Rivian R1S à travers le Michigan avec InterSystems Cloud Document et Databricks

Si vous cherchez un cas d'utilisation pour une Document Database, j'ai compris que mon cas préféré, très simple, est la possibilité d'interroger un paquet de JSON, juste à côté de mes autres données avec sql sans vraiment faire grand-chose. C'est un rêve réalisé à partir de la puissante plateforme de données multi-modèles d'InterSystems, et montré ici dans un simple carnet pour visualiser mes données de localisation géographique que mon Rivian R1S émet pour DeezWatts ( Une aventure de données Rivian ).

Voici donc l'approche en 2 étapes, à savoir l'ingestion à et la visualisation de InterSystems Cloud Document, à l'aide du pilote document JDBC.

Déploiement de documents dans InterSystems Cloud

Pour commencer, j'ai lancé un petit déploiement de Cloud Document sur le portail InterSystems Cloud Services Portal, avec un listener activé.

J'ai téléchargé le certificat ssl, et j'ai récupéré les drivers pour JDBC ainsi que le pilote de document qui l'accompagne.

Ingestion

Pour l'ingestion, j'ai voulu comprendre comment extraire un document JSON du système de fichiers et le faire persister en tant que collection dans la base de données de documents via le listener, pour cela j'ai écrit une application Java autonome. Cette approche était plus utilitaire, car tout ce qui était amusant se passait dans le carnet une fois que les données y étaient placées.
 

 
RivianDocDB.java

Ce qui précède est assez proche de JAVA trash, mais cela fonctionne, nous pouvons voir la collection dans le navigateur de collection lors du déploiement.

Databricks

Cela demande un peu d'installation de Databricks, mais cela vaut la peine de travailler avec pyspark la partie amusante.

J'ai ajouté les deux pilotes InterSystems au cluster, et j'ai placé le certificat dans le script d'initialisation du cluster import_cloudsql_certficiate.sh afin qu'il soit ajouté au keystore.

Pour être complet, le cluster utilise Databricks 16, Spark 3.5.0 et Scala 2.12

Visualization

Nous devrions donc être prêts à exécuter une tâche PySpark et à tracer l'endroit où mon fouet a été dans le sous-ensemble de données que je traîne.

Nous utilisons l'ensemble de données de base géo geopandas and geodatasets pour une approche directe de la représentation graphique.

import geopandas as gpd
import geodatasets
from shapely.geometry import Polygon

Il faut un peu de temps pour s'y habituer, mais voici la requête adressée à InterSystems Cloud Document en utilisant la syntaxe des chemins JSON et JSON_TABLE.

dbtablequery = f"(SELECT TOP 1000 lat,longitude FROM JSON_TABLE(deezwatts2 FORMAT COLLECTION, '$' COLUMNS (lat VARCHAR(20) path '$.whip2.data.vehicleState.gnssLocation.latitude', longitude VARCHAR(20) path '$.whip2.data.vehicleState.gnssLocation.longitude' ))) AS temp_table;"

 

J'ai réussi à trouver un site qui facilite grandement la création du chemin json path @ jsonpath.com.

Ensuite, nous établissons la connexion avec le déploiement de la base de données documentaire IRIS Document Database et nous lisons les données dans un cadre de données.

# Read data from InterSystems Lecture des données de la base de données documentaire InterSystems Document Database via la requête ci-dessus
df = (spark.read.format("jdbc") \
  .option("url", "jdbc:IRIS://k8s-05868f04-a88b7ecb-5c5e41660d-404345a22ba1370c.elb.us-east-1.amazonaws.com:443/USER") \
  .option("jars", "/Volumes/cloudsql/iris/irisvolume/intersystems-document-1.0.1.jar") \
  .option("driver", "com.intersystems.jdbc.IRISDriver") \
  .option("dbtable", dbtablequery) \
  .option("sql", "SELECT * FROM temp_table;") \
  .option("user", "SQLAdmin") \
  .option("password", "REDACTED") \
  .option("connection security level","10") \
  .option("sslConnection","true") \
  .load())


Ensuite, nous prenons une carte disponible à partir de jeux de données géographiques, le jeu sdoh est parfait pour une utilisation générique des États-Unis.
 

# La carte sdoh est fantastique grâce aux boîtes de délimitation
michigan = gpd.read_file(geodatasets.get_path("geoda.us_sdoh"))

gdf = gpd.GeoDataFrame( df.toPandas(), geometry=gpd.points_from_xy(df.toPandas()['longitude'].astype(float), df.toPandas()['lat'].astype(float)), crs=michigan.crs #"EPSG:4326" )

Maintenant, la partie la plus intéressante, nous voulons zoomer sur l'endroit où nous voulons placer les points de géolocalisation de l'endroit où le R1S a roulé, pour cela nous avons besoin d'une boîte de délimitation pour l'état du Michigan.

J'ai utilisé un outil très astucieux de Keene pour dessiner la boîte de délimitation de la géo clôture et cela me donne le tableau de coordonnées!

Maintenant le tableau de coordonnées de la boîte de délimitation est en notre possession, il nous faut l'insérer dans un objet Polygone.

polygon = Polygon([
      (
        -87.286377,
        45.9664245
      ),
      (
        -81.6503906,
        45.8134865
      ),
      (
        -82.3864746,
        42.1063737
      ),
      (
        -84.7814941,
        41.3520721
      ),
      (
        -87.253418,
        42.5045029
      ),
      (
        -87.5610352,
        45.8823607
      )
    ])

 

Maintenant, traçons la piste du Rivian R1S! Il s'agit d'environ 10 000 enregistrements (j'ai utilisé l'instruction top ci-dessus pour limiter les résultats)
 

ax = michigan.clip(polygon).plot(color="lightblue", alpha=0.5,linewidth=0.8, edgecolor='gray')
ax.axis('off')
ax.annotate("Data: Rivian R1S Telemetry Data via InterSystems Document Database", xy=(0.01, .085), xycoords='figure fraction', fontsize=14, color='#555555')

gdf.plot(ax=ax, color="red", markersize=1.50, alpha=0.5, figsize=(200,200))

Et voilà, nous l'avons... Détroit, Traverse City, Silver Lake Sand Dunes, Holland, Mullet Lake, Interlachen... Le Michigan à l'état pur, à la Rivian.


 

0
0 30
Article Pierre LaFay · Fév 7, 2024 8m read

Introduction

Il n'y a pas si longtemps, j'ai vu l'idée de using Python Class Definition Syntax to create IRIS classes

sur le portail d'idées d'InterSystems. Elle a attiré mon attention car l'intégration d'un maximum de syntaxes donne de la visibilité aux produits d'InterSystems pour les programmeurs ayant de l'expérience dans de nombreux langages.

0
0 65
Article Iryna Mykhailova · Juil 19, 2022 9m read

Pour parler des différentes bases de données et des différents modèles de données qui existent, on doit premièrement comprendre ce qui est une base de données et comment les utiliser.

Une base de données est une collection organisée de données stockées et accessibles par voie électronique. Elle permet de stocker et de retrouver des données structurées, semi-structurées ou des données brutes souvent en rapport avec un thème ou une activité.

Au cœur de chaque base de données se trouve au moins un modèle utilisé pour décrire ses données. Et selon le modèle sur lequel elle est basée, elle peut avoir des caractéristiques un peu différentes et stocker différents types de données.

Pour inscrire, retrouver, modifier, trier, transformer ou imprimer les informations de la base de données on utilise un logiciel qui s’appelle système de gestion de base de données (SGBD, en anglais DBMS pour Database management system).

La taille, les capacités et les performances des bases de données et de leurs SGBD respectifs ont augmenté de plusieurs ordres de grandeur. Ces augmentations de performances ont été rendues possibles par les progrès technologiques dans différents domaines, tels que les domaines des processeurs, de la mémoire informatique, du stockage informatique et des réseaux informatiques. Le développement ultérieur de la technologie des bases de données peut être divisé en quatre générations basées sur le modèle ou la structure des données : navigation, relationnel, objet et post-relationnel.

4
0 642
Article Lorenzo Scalese · Mai 18, 2022 14m read

La recherche d'images comme celle de Google est une fonctionnalité intéressante qui m'émerveille - comme presque tout ce qui est lié au traitement des images.

Il y a quelques mois, InterSystems a publié un aperçu de Python Embedded. Comme Python dispose de nombreuses librairies pour le traitement d'images, j'ai décidé de lancer ma propre tentative pour jouer avec une sorte de recherche d'images - une version beaucoup plus modeste en fait :-)


--- #### Un peu de théorie 🤓

Pour réaliser un système de recherche d'images, il faut d'abord sélectionner un ensemble de caractéristiques à extraire des images - ces caractéristiques sont également appelées descripteurs. L'étendue de chaque composante de ces descripteurs crée ce qu'on appelle un espace de caractéristiques, et chaque instance de cet espace est appelée un vecteur. Le nombre d de composantes nécessaires pour décrire les vecteurs, définit la dimension de l'espace des caractéristiques et des vecteurs, appelé d-dimensionnel.


Figure 1 - Un espace caractéristique tridimensionnel et un vecteur descripteur dans cet espace.
Credits: https://tinyurl.com/ddd76dln
---

Une fois l'ensemble des descripteurs définis, tout ce que vous avez à faire pour rechercher une image dans la base de données est d'extraire les mêmes descripteurs d'une image à rechercher et de les comparer aux descripteurs des images de la base de données - qui ont été précédemment extraits.

Dans ce travail, on a simplement utilisé la couleur dominante de l'image comme descripteur (j'ai dit que c'était une version modeste...). Comme une représentation RVB des couleurs a été utilisée, l'espace caractéristique est un espace tridimensionnel - 3d en abrégé. Chaque vecteur dans un tel espace a 3 composantes - (r,g,b), dans la gamme [0, 255].


Figure 2 - L'espace tridimensionnel des caractéristiques RVB
Credits: https://www.baslerweb.com/fp-1485687434/media/editorial/content_images/faqs/faq_RGB_1.gif
---

En traitement du signal, il est très fréquent d'avoir des espaces à n dimensions avec des valeurs de n bien supérieures à 3. En fait, vous pouvez combiner un grand nombre de descripteurs dans un même vecteur afin d'obtenir une meilleure précision. C'est ce qu'on appelle la sélection de caractéristiques et c'est une étape très importante dans les tâches de classification/reconnaissance.

Il est également courant de normaliser la plage de dimensions en [0, 1], mais pour des raisons de simplicité, ce travail utilise la plage par défaut [0, 255].

L'avantage de modéliser des caractéristiques sous forme de vecteurs est la possibilité de les comparer à travers des métriques de distance. Il existe de nombreuses distances, chacune ayant ses avantages et ses inconvénients, selon que l'on recherche la performance ou la précision. Dans ce travail, j'ai choisi des distances faciles à calculer - manhattan et chebyshev, qui sont essentiellement des différences absolues avec une précision raisonnable.


Figure 3 - Représentation de certains paramètres de distance
Credits: https://i0.wp.com/dataaspirant.com/wp-content/uploads/2015/04/cover_post_final.png
---

Index fonctionnel

Mais il ne s'agit que des outils nécessaires pour comparer les images en fonction de leur contenu. Si vous ne disposez pas d'un langage de requête comme SQL, vous vous retrouverez avec des méthodes et des paramètres de recherche fastidieux... De plus, en utilisant SQL, vous pouvez combiner cet index avec d'autres opérateurs bien connus, créant ainsi des requêtes complexes.

C'est ici où Functional Index d'InterSystems est très utile.

Un index fonctionnel est une classe qui implémente la classe abstraite %Library.FunctionalIndex qui implémente certaines méthodes afin de gérer la tâche d'indexation dans une instruction SQL. Ces méthodes traitent essentiellement les insertions, les suppressions et les mises à jour.

/// Indexation fonctionnelle permettant d'optimiser les requêtes sur les données d'image
Class dc.multimodel.ImageIndex.Index Extends %Library.FunctionalIndex [ System = 3 ]
{

/// Cardinalité de l'espace des caractéristiques
/// Comme cette classe est destinée à indexer l'image dans l'espace RVB, sa cardinalité est de 3
Paramètre Cardinalité = 3 ;

/// Cette méthode est invoquée lorsqu'une instance existante d'une classe est supprimée.
ClassMethod DeleteIndex(pID As %CacheString, pArg... As %Binary) [ CodeMode = generator, ServerOnly = 1 ]
{
	If (%mode '= "method") {
		$$$GENERATE("Set indexer = ##class(dc.multimodel.ImageIndex.Indexer).GetInstance("""_%class_""", """_%property_""")")
		$$$GENERATE("Set indexer.Cardinality = "_..#Cardinality)
		$$$GENERATE("Do indexer.Delete(pID, pArg...)")
	}
	Return $$$OK
}

ClassMethod Find(pSearch As %Binary) As %Library.Binary [ CodeMode = generator, ServerOnly = 1, SqlProc ]
{
	If (%mode '= "method") {
		$$$GENERATE("Set result = """"")
		$$$GENERATE("Set result = ##class(dc.multimodel.ImageIndex.SQLFind).%New()")
		$$$GENERATE("Set indexer = ##class(dc.multimodel.ImageIndex.Indexer).GetInstance("""_%class_""", """_%property_""")")
		$$$GENERATE("Set indexer.Cardinality = "_..#Cardinality)
		$$$GENERATE("Set result.Indexer = indexer")
		$$$GENERATE("Do result.PrepareFind(pSearch)")
		$$$GENERATE("Return result")
	}
	Return $$$OK
}

/// Cette méthode est invoquée lorsqu'une nouvelle instance d'une classe est insérée dans la base de données.
ClassMethod InsertIndex(pID As %CacheString, pArg... As %Binary) [ CodeMode = generator, ServerOnly = 1 ]
{
	If (%mode '= "method") {
		$$$GENERATE("Set indexer = ##class(dc.multimodel.ImageIndex.Indexer).GetInstance("""_%class_""", """_%property_""")")
		$$$GENERATE("Set indexer.Cardinality = "_..#Cardinality)
		$$$GENERATE("Do indexer.Insert(pID, pArg...)")
	}
	Return $$$OK
}

ClassMethod PurgeIndex() [ CodeMode = generator, ServerOnly = 1 ]
{
	If (%mode '= "method") {
		$$$GENERATE("Set indexer = ##class(dc.multimodel.ImageIndex.Indexer).GetInstance("""_%class_""", """_%property_""")")
		$$$GENERATE("Set indexer.Cardinality = "_..#Cardinality)
		$$$GENERATE("Set indexGbl = indexer.GetIndexLocation()")
		$$$GENERATE("Do indexer.Purge()")
	}
	Return $$$OK
}

/// Cette méthode est invoquée lorsqu'une instance existante d'une classe est mise à jour.
ClassMethod UpdateIndex(pID As %CacheString, pArg... As %Binary) [ CodeMode = generator, ServerOnly = 1 ]
{
	If (%mode '= "method") {
		$$$GENERATE("Set indexer = ##class(dc.multimodel.ImageIndex.Indexer).GetInstance("""_%class_""", """_%property_""")")
		$$$GENERATE("Set indexer.Cardinality = "_..#Cardinality)
		$$$GENERATE("Do indexer.Update(pID, pArg...)")
	}
	Return $$$OK
}

}

J'ai caché une partie du code d'implémentation pour des raisons de lisibilité ; vous pouvez consulter le code dans le lien OpenExchange.

Une autre classe abstraite doit être implémentée, c'est %SQL.AbstractFind, afin de rendre disponible l'utilisation de l'opérateur %FIND pour demander au moteur SQL d'utiliser votre index personnalisé.

Une explication beaucoup plus détaillée et conviviale des index fonctionnels est donnée par @alexander-koblov qui constitue également un excellent exemple d'index fonctionnel. Je vous recommande vivement de le lire.

Si vous souhaitez aller plus loin, vous pouvez jouer avec le code source des index %iFind et %UIMA index.

Dans ce travail, j'ai configuré une classe de test de persistance simple, où le chemin des images est stocké, et un index personnalisé pour la recherche d'images est défini pour ce champ.

Class dc.multimodel.ImageIndex.Test Extends %Persistent
{

Property Name As %String;

Property ImageFile As %String(MAXLEN = 1024);

Index idxName On Name [ Type = bitmap ];

Index idxImageFile On (ImageFile) As dc.multimodel.ImageIndex.Index;

Notez que idxImageFile est un index personnalisé (dc.multimodel.ImageIndex.Index) pour le champ Image (qui stocke le chemin de l'image).

Le tour de Python (et COS) !

Ainsi, les classes abstraites d'index fonctionnel vous donneront les points d'entrée où vous pourrez effectuer l'extraction de caractéristiques et la recherche lors de l'exécution des instructions SQL. Maintenant, c'est au tour de Python !

Vous pouvez importer et exécuter le code Python dans un contexte COS en utilisant Python intégré. Par exemple, pour extraire la couleur dominante d'une image :

Method GetDominantColorRGB(pFile As %String, ByRef pVector) As %Status
{
  Set sc = $$$OK
  Try {
    Set json = ##class(%SYS.Python).Import("json")
    Set fastcolorthief = ##class(%SYS.Python).Import("fast_colorthief")
    Set imagepath = pFile
    Set dominantcolor = fastcolorthief."get_dominant_color"(imagepath, 1)
    Set vector = {}.%FromJSON(json.dumps(dominantcolor))
    Set n = ..Cardinality - 1
    For i = 0:1:n {
      Set pVector(i) = vector.%Get(i)
    }
  } Catch(e) {
    Set sc = e.AsStatus()
  }
  Return sc
}

Dans cette méthode, deux librairies Python sont importées (json et fast_colorthief). La librairie fast_colorthief renvoie une représentation Python de type tableau 3-d avec les valeurs de RGB ; l'autre librairie - json, sérialise ce tableau dans un %DynamicArray.

La couleur dominante est extraite pour chaque enregistrement qui est inséré ou mis à jour - une fois que l'index fonctionnel lève les appels aux méthodes InsertIndex et UpdateIndex en réponse aux insertions et mises à jour dans le tableau. Ces caractéristiques sont stockées dans l'index global du tableau :

Method Insert(pID As %CacheString, pArgs... As %Binary)
{
	// pArgs(1) has the image path
	$$$ThrowOnError(..GetDominantColor(pArgs(1), .rgb))
	Set idxGbl = ..GetIndexLocation()
	Set @idxGbl@("model", pID) = ""
  	Merge @idxGbl@("model", pID, "rgb") = rgb
  	Set @idxGbl@("last-modification") = $ZTIMESTAMP
}

Method Update(pID As %CacheString, pArg... As %Binary)
{
	// pArgs(1) has the image path
  	Set idxGbl = ..GetIndexLocation()
  	Do ..GetDominantColor(pArg(1), .rgb)
  	Kill @idxGbl@("model", pID)
  	Set @idxGbl@("model", pID) = ""
  	Merge @idxGbl@("model", pID, "rgb") = rgb
  	Set @idxGbl@("last-modification") = $ZTIMESTAMP
}

De la même manière, lorsque des enregistrements sont supprimés, l'index fonctionnel lance des appels aux méthodes DeleteIndex et PurgeIndex. À leur tour, les fonctionnalités doivent être supprimées de l'index global du tableau :

Method Delete(pID As %CacheString, pArg... As %Binary)
{
  	Set idxGbl = ..GetIndexLocation()
  	Kill @idxGbl@("model", pID)
  	Set @idxGbl@("last-modification") = $ZTIMESTAMP
}

Method Purge(pID As %CacheString, pArg... As %Binary)
{
  	Set idxGbl = ..GetIndexLocation()
  	Kill @idxGbl
  	Set @idxGbl@("last-modification") = $ZTIMESTAMP
}

L'index global est récupéré par introspection dans la classe persistante :

Method GetIndexLocation() As %String
{
	Set storage = ##class(%Dictionary.ClassDefinition).%OpenId(..ClassName).Storages.GetAt(1).IndexLocation
	Return $NAME(@storage@(..IndexName))
}

Lorsque les utilisateurs utilisent l'index dans les clauses WHERE, la méthode Find() est activée par l'index de la fonction. Les instructions de la requête sont transmises afin que vous puissiez les analyser et décider de ce qu'il faut faire. Dans ce travail, les paramètres sont sérialisés en JSON afin de faciliter leur analyse. Les paramètres de la requête ont la structure suivante :

SELECT ImageFile
FROM dc_multimodel_ImageIndex.Test
WHERE ID %FIND search_index(idxImageFile, '{"color_similarity":{"image":"/data/img/test/161074693598711.jpg","first":5,"strategy":"knn"}}')

Dans cette instruction, vous pouvez voir l'utilisation de l'opérateur %FIND et de la fonction search_index. C'est ainsi que SQL accède à notre index personnalisé.

Les paramètres de search_index définissent l'index à rechercher - idxImageFile, dans ce cas ; et la valeur à envoyer à l'index. Ici, l'index attend un objet JSON, avec une configuration d'objet définissant : (i) le chemin de l'image, (ii) une limite pour les résultats, et (iii) une stratégie de recherche.

Une stratégie de recherche est simplement l'algorithme à utiliser pour effectuer la recherche. Actuellement, deux stratégies sont mises en œuvre : (i) fullscan et (ii) knn, qui correspond à k-proches voisins.

La stratégie fullscan consiste simplement en une recherche exhaustive mesurant la distance entre l'image recherchée et chaque image stockée dans la base de données.

Method FullScanFindStrategy(ByRef pSearchVector, ByRef pResult) As %Status
{
	Set sc = $$$OK
	Try {
		Set idxGbl = ..Indexer.GetIndexLocation()
		Set rankGbl = ..Indexer.GetRankLocation()

		Set id = $ORDER(@idxGbl@("model", ""))
		While (id '= "") {
			If ($ISVALIDNUM(id)) {
				Merge vector = @idxGbl@("model", id, "rgb")
				Set distance = ..Indexer.GetL1Distance(.pSearchVector, .vector)
				Set result(distance, id) = ""
			}
			Set id = $ORDER(@idxGbl@("model", id))
		}

		Kill @rankGbl@(..ImagePath, ..FindStrategy)
		If (..First '= "") {
			Set c = 0
			Set distance = $ORDER(result(""))
			While (distance '= "") && (c < ..First) {
				Merge resultTmp(distance) = result(distance)

				Set id = $ORDER(result(distance, ""))
				While (id '= "") {
					Set @rankGbl@(..ImagePath, ..FindStrategy, id) = distance
					Set id = $ORDER(result(distance, id))
				}

				Set c = c + 1
				Set distance = $ORDER(result(distance))
			}
			Kill result
			Merge result = resultTmp
		}

		Merge pResult = result
	}
	Catch ex {
		Set sc = ex.AsStatus()
	}
	Return sc
}

La stratégie KNN utilise une approche plus sophistiquée. Elle utilise une librairie Python pour créer une structure arborescente appelée Ball Tree. Une telle arborescence convient à une recherche efficace dans un espace à n dimensions.

Method KNNFindStrategy(ByRef pSearchVector, ByRef pResult) As %Status
{
	Do ..Log(" ------ KNNFindStrategy ------ ")
	Set sc = $$$OK
	Try {
		Set idxGbl = ..Indexer.GetIndexLocation()
		Set rankGbl = ..Indexer.GetRankLocation()

		Set json = ##class(%SYS.Python).Import("json")
		Set knn = ##class(%SYS.Python).Import("knn")

		Set first = ..First
		Set k = $GET(first, 5)

		Set n = ..Indexer.Cardinality - 1
		Set x = ""
		For i = 0:1:n {
			Set $LIST(x, * + 1) = pSearchVector(i)
		}
		Set x = "[["_$LISTTOSTRING(x, ",")_"]]"

		$$$ThrowOnError(..CreateOrUpdateKNNIndex())
		Set ind = knn.query(x, k, idxGbl)
		Set ind = {}.%FromJSON(json.dumps(ind.tolist()))
		Set ind = ind.%Get(0)

		Kill result
		Kill @rankGbl@(..ImagePath, ..FindStrategy)
		Set n = k - 1
		For i=0:1:n {
			Set id = ind.%Get(i)
			Set result(i, id) = ""
			Set @rankGbl@(..ImagePath, ..FindStrategy, id) = i
		}
		Merge pResult = result
	}
	Catch ex {
		Set sc = ex.AsStatus()
	}
	Return sc
}

Le code Python pour générer une arborescence Ball Tree est présenté ci-dessous :

from sklearn.neighbors import BallTree
import numpy as np
import pickle
import base64
import irisnative

def get_iris():
  ip = "127.0.0.1"
  port = 1972
  namespace = "USER"
  username = "superuser"
  password = "SYS"

  connection = irisnative.createConnection(ip,port,namespace,username,password)
  dbnative = irisnative.createIris(connection)

  return (connection, dbnative)

def release_iris(connection):
  connection.close()

def normalize_filename(filename):
  filename = filename.encode('UTF-8')
  return base64.urlsafe_b64encode(filename).decode('UTF-8')

def create_index(index_global, cardinality):
  connection, dbnative = get_iris()
  X = get_data(dbnative, index_global, cardinality)
  tree = BallTree(X, metric = "chebyshev")
  filename = f"/tmp/${normalize_filename(index_global)}.p"
  pickle.dump(tree, open(filename, "wb"))
  release_iris(connection)
  return tree

def get_data(dbnative, index_global, cardinality):
  X = []
  iter_ = dbnative.iterator(index_global, "model")
  for subscript, value in iter_.items():
    id_ = subscript
    v = []
    for i in range(cardinality):
      v.append(
        dbnative.get(index_global, "model", id_, "rgb", i) / 255
      )
    X.append(v)
  return X

def query(x, k, index_global):
  filename = f"/tmp/${normalize_filename(index_global)}.p"
  tree = pickle.load(open(filename, "rb"))
  x = eval(x)
  x_ = [xi / 255 for xi in x[0]]
  dist, ind = tree.query([x_], k)
  return ind

Lorsqu'une image est recherchée, l'index personnalisé appelle la méthode de requête de l'objet Ball Tree en Python. Vous pouvez également noter l'utilisation de l'API native d'IRIS afin d'accéder aux valeurs RVB globales de l'index pour la construction de l'arborescence Ball Tree.

Pour ordonner les images par similarité, il a été développé une procédure SQL qui traverse une globale stockant les distances précédemment calculées pour chaque image recherchée :

Method DiffRank(pSearch As %Binary, pId As %String) As %Float
{
	Set search = {}.%FromJSON(pSearch)
	If (search.%IsDefined("color_similarity")) {
		Set config = search.%Get("color_similarity")
		Set imagePath = config.%Get("image")
		If (config.%IsDefined("strategy")) {
			Set findStrategy = config.%Get("strategy")
		}
		Set rankGbl = ..Indexer.GetRankLocation()
		Set rank = $GET(@rankGbl@(imagePath, findStrategy, pId))
		Return rank
	}
	Return ""
}

Vous pouvez donc modifier l'instruction SQL pour classer les résultats par similarité :

SELECT ImageFile, dc_multimodel_ImageIndex.Test_idxImageFileDiffRank('{"color_similarity":{"image":"/data/img/test/161074693598711.jpg","first":5,"strategy":"knn"}}', id) AS DiffRank
FROM dc_multimodel_ImageIndex.Test
WHERE ID %FIND search_index(idxImageFile, '{"color_similarity":{"image":"/data/img/test/161074693598711.jpg","first":5,"strategy":"knn"}}')
ORDER BY DiffRank

Conclusion

L'objectif de ce travail était de montrer comment combiner la définition d'index fonctionnels dans COS avec des appels au code Python utilisant leurs étonnantes bibliothèques. De plus, en utilisant cette technique, vous pouvez accéder à des fonctionnalités complexes fournies par les librairies Python dans des instructions SQL, ce qui vous permet d'ajouter de nouvelles fonctionnalités à vos applications.

0
0 93
Article Irène Mykhailova · Avr 18, 2022 7m read

Vous avez probablement entendu parler des bases de données NoSQL. Il existe plusieurs définitions, mais pour simplifier, ce terme est couramment utilisé pour désigner les bases de données qui n'utilisent littéralement pas le langage SQL, c'est-à-dire les bases de données autres que les bases de données relationnelles (RDB).

InterSystems IRIS Data Platform vous permet de définir des tableaux et d'accéder aux données en SQL. Par conséquent, InterSystems IRIS Data Platform n'est pas strictement une base de données NoSQL. Cependant, les " Globales " à l'origine des hautes performances d'InterSystems IRIS sont une technologie de base d'InterSystems depuis 40 ans, fournissant ce que nous appellerions aujourd'hui une base de données NoSQL. Cet article montre comment créer des structures graphiques dans les "Globales" InterSystems IRIS et y accéder en Python.

NoSQL

Il y a des bases de données classées comme NoSQL qui traitent une variété de modèles de données. Des exemples typiques sont énumérés ci-dessous.

  • Key-Value: Maintient la correspondance entre les clés et les valeurs. Une base de données typique : Redis
  • Document: JSON ou XML sont utilisés comme modèle de données. Une base de données typique : MongoDB
  • Graph: Un modèle de structure de graphe comprenant des sections et des nœuds. Une base de données typique : Neo4j

Il existe une multitude d'autres produits et modèles de données. 

Historique de NoSQL

Pourquoi NoSQL est-il apparu et pourquoi est-il devenu si populaire ? Dans cet article, nous souhaiterions citer les raisons suivantes.

  • Les structures de données qui ne peuvent pas être représentées naturellement au moyen de tableaux: Théoriquement, toute structure de données peut être représentée dans un tableau et accessible par SQL, mais il peut y avoir des problèmes tels que la complexité SQL et des performances médiocres. Dans de tels cas, il est préférable d'utiliser un modèle de données adapté à la structure plutôt qu'un tableau.
  • Optimisation pour les environnements distribués:L'un des domaines où l'utilisation de NoSQL a pris de l'ampleur est celui de l'infrastructure des services de réseaux sociaux tels que Facebook. Pour prendre en charge de grandes quantités de données, de nombreux utilisateurs et une haute disponibilité, il est essentiel de distribuer la base de données. Les RDB ne fonctionnent pas toujours de manière optimale dans un environnement distribué. L'approche transactionnelle et sous-transactionnelle de la RDB, décrite ci-dessous, est également un facteur qui rend difficile sa mise en œuvre dans un environnement distribué.
  • Demande de traitement d'une transaction:Le traitement des transactions et l'intégrité des données sont des fonctions dans lesquelles les RDBs possèdent des points forts. Il va sans dire qu'un support strict du traitement des transactions et de l'intégrité des données est essentiel pour les systèmes qui gèrent les comptes bancaires, par exemple. Cependant, le maintien de l'intégrité des données dans le traitement des transactions est très difficile à mettre en œuvre dans un environnement distribué, notamment en ce qui concerne la compatibilité avec les performances. Par conséquent, si vous souhaitez donner la priorité à l'amélioration des performances et de la disponibilité dans un environnement distribué plutôt qu'au maintien d'une cohérence stricte, NoSQL pourrait mieux répondre à vos besoins que RDB.

Globales

InterSystems IRIS utilise une structure de données appelée Globales au cœur de la base de données. Les Globales sont modélisées comme des variables de tableau, comme le montre la figure suivante.

Les Globales n'ont pas besoin d'être définies préalablement (sans schéma), ce qui permet une conception très souple des structures de données. En outre, les données sont toujours triées par ordre d'indice (clé), tant sur le disque que dans le cache mémoire, ce qui localise et accélère l'accès aux données et évite la fragmentation.

Bien que les Globales aient la forme de variables de tableau, ils peuvent en fait être utilisés dans IRIS ObjectScript, qui est un langage de script pour IRIS, avec presque la même syntaxe que les variables en mémoire, ce qui améliore les perspectives de développement de programmes. Pour plus d'informations sur les Globales, voir la section Global Usage Documentation.

Accès en utilisant Python

Voici un exemple de manipulation de Globales en utiisant Python à l'aide de Python Native API.

Nous utiliserons les données "WikiSpeedia" de SNAP à l'Université de Standford comme point de départ. WikiSpeedia est un jeu dans lequel les joueurs s'affrontent pour voir à quelle vitesse ils peuvent atteindre un mot cible à partir d'un mot donné en suivant uniquement les liens Wikipédia. SNAP compte environ 110 000 liens suivis par des utilisateurs réels, qui sont stockés globalement dans InterSystems IRIS.

Structure du graphe

Cette section décrit la structure du graphe.

La figure suivante présente les principes de base de la structure d'un graphe. Un graphe est constitué de nœuds et d'arêtes. Une arête relie deux nœuds.

Par exemple, une relation d'amitié sur Facebook peut être représentée par nœud = utilisateur, arête = relation d'amitié. Une structure de données pouvant être utilisée pour la recherche d'itinéraires est aussi réalisable en définissant le nœud = station, l'arête = station voisine de la précédente station, et la caractéristique de l'arête = distance entre les stations.

Dans l'exemple de ce document, la structure du graphe est utilisée comme suit : nœud = article Wikipédia (mot-clé) et arête = relation entre l'article original et l'article lié vers lequel l'utilisateur de WikiSpeedia a cliqué.

Structure des Globales

Examinons la structure des Globales.

^Links("Tokyo", "18th_century")=""
^Links("Tokyo", "London")=""
^Links("Osaka", "Aquarium")=""

Par exemple, la ligne supérieure indique que l'utilisateur a cliqué sur un lien à partir de l'article "Tokyo" vers l'article "18th_century". La troisième ligne indique que l'utilisateur a cliqué à partir de "Osaka" vers "Aquarium".

Les Globales sont optimisées pour un accès de gauche à droite aux souscripteurs (clés). Ainsi, le

^Links("Article original", "Cliquer sur l'article")=""

est optimale. En outre, dans ce cas, la valeur est "", mais si vous voulez que les arêtes aient un certain attribut, vous pouvez le définir sur une valeur globale.

Code Python

Cette section décrit le code Python. D'abord, c'est import.

import irisnative
import networkx as nx

Pour utiliser l'API native Python, importez le module irisnative. Ce module est un fichier .whl placé dans le répertoire dev/python lors de l'installation d'IRIS et installé dans l'environnement python avec la commande pip. De plus, comme NetworkX est utilisé pour maintenir la structure du graphe, importez-le également.

connection = irisnative.createConnection("localhost", 9091, "user", "horita","horita")
iris_native = irisnative.createIris(connection)

Dans ces deux lignes, createConnection() établit la connexion à IRIS et createIris() crée l'objet d'interface pour les opérations globales.

def addNodes(key, g, d):
    g.add_node(key)
    if d > 3:
        return
    
    iter = iris_native.iterator("Links", key)
    nodelist = [k for k,v in iter.items()]

    Limité à 3 liens à suivre pour des raisons de démonstration
    random.shuffle(nodelist)
    nodelist = nodelist[0:3]

    edgelist = [(key, n) for n in nodelist]
    g.add_nodes_from(nodelist)
    g.add_edges_from(edgelist)
    
    for subk in nodelist:
        addNodes(subk, g, d + 1)

Définition de la fonction addNodes(). Cette fonction construit une structure de graphe en tant qu'objet NetworkX en suivant les liens des noeuds dans un graphe donné. Des appels récursifs sont utilisés pour traverser le troisième niveau de la hiérarchie. Le point principal ici est

    iter = iris_native.iterator("Links", key)
    nodelist = [k for k,v in iter.items()]

Dans iris_native.iterator("Links", key), l'API native Python crée un itérateur pour l'indice. Par exemple, key='Tokyo' serait un itérateur qui itère sur la chaîne dans la partie * de ^Links("Tokyo", *).

L'itérateur est ensuite répété dans Python pour créer une liste. Comme vous pouvez le constater, l'itérateur de la globale s'intègre parfaitement au modèle itératif de Python, ce qui permet d'obtenir un code simple.

curkey = 'Tokyo'
G = nx.Graph()
addNodes(curkey, G, 1)

Ensuite, addNodes() est appelé en prenant le mot "Tokyo" comme clé. Cela nous donnera l'enchaînement des liens tracés à partir du mot "Tokyo". Il est montré dans la figure suivante.

Le réseau des mots cliqués à partir du mot "Tokyo" est ainsi représenté.

Conclusion

Les globales d'InterSystems IRIS sont une force avec laquelle il faut compter dans les cas où une base de données NoSQL est la base de données de choix. En outre, l'API native Python vous permet de manipuler les globales de manière naturelle à partir de vos programmes Python.

Nous vous encourageons à l'essayer. Par ailleurs, nous serions très heureux si vous pouviez écrire vos questions dans les commentaires de cet article.

0
0 191