Avoir accès à un moteur de base de données natif dans un environnement Windows 8 Modern UI en Javascript, en C# en VB et en C++/CX : Part II

Téléchargement des binaires.

 

Prérequis, pour comprendre les exemples dans ce billet, il est préférable de lire mon précédant billet ou j’explique les bases de la création d’une base de données, à l’aide du Wrapper Windows Runtime JetBlue.WinRT.

Dans ce billet nous allons voir :

  • La création d’une table avec des colonnes de multi-valeur, et la manière de récupérer les données de cette colonne
  • L’utilisation des champs de type DateTime
  • La sauvegarde d’images sous formes de BLOB.
  • La création de table avec des indexes
  • L’utilisation des indexes pour une recherche multicritères

 

Utilisation d’une colonne multi-valeur

Une colonne multi-valeur, peux servir à stocker dans une colonne plusieurs valeurs. Pour des raisons de performances, ce type de colonne, peut éviter d’aller chercher dans une autre table les valeurs associées.

Pour créer cette colonne il suffit de préciser le flags ColumnFlags.MultiValued lors de la création de la table. Ici nous créons une colonne de Type Texte, qui pourra être nulle ou contenir plusieurs valeurs. Tous les types sont supportés pour une colonne MultiValued, a l’exception pour l’instant du type LongBinary.

Code Javascript

listcolumns.push(new JetBlue.WinRT.JetBlueColumn("TechnoEtLangages", JetBlue.WinRT.Enum.ColumnType.text, JetBlue.WinRT.Enum.ColumnFlags.multiValued | JetBlue.WinRT.Enum.ColumnFlags.maybeNull));              
                var table = db.createTable("TETable", listcolumns);
              

 

Code C#

listcolumns.Add(new JetBlueColumn("TechnoEtLangages", ColumnType.Text, ColumnFlags.MultiValued | ColumnFlags.MaybeNull));
         var table = db.CreateTable("TETable", listcolumns);

 

Code C++/CX

listcolumns->Append(ref new JetBlueColumn("TechnoEtLangages", ColumnType::Text,ColumnFlags::MultiValued | ColumnFlags::MaybeNull));       
                     auto table = db->CreateTable("TETable", listcolumns);

 

Pour insérer des données dans la table, nous n’allons pas utiliser la propriétés Value de la colonne, mais sa propriété Values, qui est typée en une collection d’objets.

Code Javascript

listcolumns[6].values.append("C++");
                listcolumns[6].values.append("DirectX");
                listcolumns[6].values.append("Javascript");
                table.records.add(listcolumns);
                listcolumns[6].values.clear();

 

Code C#

listcolumns[6].Values.Add("Javascript");
                 listcolumns[6].Values.Add("C#");
                 listcolumns[6].Values.Add("C++");
                 listcolumns[6].Values.Add("DirectX");                                 
                 table.Records.Add(listcolumns);                
                 listcolumns[6].Values.Clear();

Code C++/CX

listcolumns->GetAt(6)->Values->Append("C++");
                     listcolumns->GetAt(6)->Values->Append("DirectX");
                     table->Records->Add(listcolumns);
                     listcolumns->GetAt(6)->Values->Clear();

Dans notre exemple nous ajoutons une collection de chaine de caractères, car le type de notre colonne est ColumnType.Text, toute fois le compilateur ne vous dira rien si vous ajoutez un type entier par exemple listcolumns.Values.Add(100) , par contre à l’exécution, l’exception “Invalid Column Type” est levée. C’est un peu plus épineux à déboguer parfois, il est donc important de bien faire attention à ce que l’on fait.

Ensuite, il ne faut pas oublier de vider la collection pour l’insertion de l’enregistrement suivant.

Pour récupérer les données c’est l’opération inverse, il suffit de parcourir la collection d’objets et la caster en type String pour les langages autres que Javascript, comme dans les exemples suivants :

Code Javascript

columns.push(new JetBlue.WinRT.JetBlueColumn("TechnoEtLangages"));

                db.openTableAsync("TETable", JetBlue.WinRT.Enum.OpenTableFlags.readOnly).then(function (t1) {
                    //Navigation en mode sequentiel
                    do {
                        var record = t1.records.get(columns);
                        var jetbluecolumn = record[6];
                        var values = [];
                        if (jetbluecolumn.values !== null) {
                            jetbluecolumn.values.forEach(function(item)
                            {
                                values.push(item);
                             });
                        }

 

Code C#

  1. var t1 = db.OpenTable("TETable", OpenTableFlags.ReadOnly);
  2.                  uint count = t1.Records.Count;
  3.                  columns.Add(new JetBlueColumn("TechnoEtLangages"));               
  4.                  do
  5.                  {
  6.                      var record = t1.Records.Get(columns);
  7.                      var name = record[1].Value + " " + record[0].Value;
  8.                     
  9.                      List<String> values = new List<string>();
  10.                      foreach (var item in record[6].Values)
  11.                      {
  12.                          values.Add(item.ToString());
  13.                      }

 

Utilisation d’une colonne de type DateTime.

Pour créer une colonne de ce type nous allons utiliser le type ColumnType.DateTime.

Code Javascript

  1. listcolumns.push(new JetBlue.WinRT.JetBlueColumn("DateNaissance", JetBlue.WinRT.Enum.ColumnType.dateTime, JetBlue.WinRT.Enum.ColumnFlags.maybeNull));

Code C#

listcolumns.Add(new JetBlueColumn("DateNaissance", ColumnType.DateTime, ColumnFlags.MaybeNull));

Code C++/CX

listcolumns->Append(ref new JetBlueColumn("DateNaissance",ColumnType::DateTime,ColumnFlags::MaybeNull));

Pour créer une date en Javascript, c’est relativement simple, il suffit de créer un objet Date() en lui passant une chaine de caractères et l’affecter à la propriété value de la colonne.

Code Javascript

listcolumns[7].value = new Date("12/16/1964");

En C# il faut utiliser l’objet DateTimeOffset 

Code C#

listcolumns[7].Value = new DateTimeOffset(new DateTime(1964, 12, 16));

En C++, c’est légèrement différent, car nous allons utiliser le type WinRT Windows.Foundation.DateTime (non disponible dans les autres langages) et qui possède une propriété UniversalTime de type long long codé sur 8 octets. D’après la documentation cette propriété, à la même granularité que la structure FILETIME, qui possède elle même, deux membres de type DWORD dwHigDateTime, et dwLowDateTime.

Mais le type DateTime, ne nous permet pas d’exprimer une date sous forme Année/Mois/jour comme dans les autres langages.

  • Pour ce faire, nous allons utiliser la structure Windows SYSTEMETIME, pour stocker la date sous la forme désirée.
  • Ensuite, l’API Windows SystemTimeToFileTime, nous permettra de la convertir en une structure FILETIME et remplir les deux membres dwHigDateTime, et dwLowDateTime.
  • La moitié du travail est fait, car maintenant il faut construire à partir de ces deux membres de type DWORD un long long (INT64) pour le stocker dans la propriété UniversalTime. Pour cela il faut caster la partie haute dwHigDateTime en INT64, que nous décalons vers la gauche de 4 octets, ce qui donne un nombre sur 8 octets auquel on applique un masque de la partie basse dwLowDateTime et le tour est joué. Bon ok je comprend l’engouement des langages de plus haut niveau, merci à la projection Javascript et .NET

Code C++/CX

Windows::Foundation::DateTime dt;                    
                     _SYSTEMTIME sysTime={0};
                    
                     sysTime.wYear=1964;
                     sysTime.wMonth=12;
                     sysTime.wDay=16;
                    
                     FILETIME fileTime;
                     ::SystemTimeToFileTime(&sysTime,&fileTime);
                     INT64 universal=static_cast<INT64>(fileTime.dwHighDateTime) << 32 | fileTime.dwLowDateTime;
                     dt.UniversalTime=universal;
                     listcolumns->GetAt(7)->Value=dt;

Pour récupérer la valeur de notre colonne, c’est très simple.

La projection Javascript permet de manipuler directement la valeur retrouvée en un type Date.

Code Javascript

columns.push(new JetBlue.WinRT.JetBlueColumn("DateNaissance"));
                db.openTableAsync("TETable", JetBlue.WinRT.Enum.OpenTableFlags.readOnly).then(function (t1) {
                    //Navigation en mode sequentiel
                    do {
                        var record = t1.records.get(columns);
                        var Madate = record[0].value;
               

En .NET la projection se fait via un DateTimeOffset

Code C#

columns.Add(new JetBlueColumn("DateNaissance"));
                 do
                 {
                     var record = t1.Records.Get(columns);
                    
                     DateTimeOffset date =(DateTimeOffset)record[7].Value;

En C++, il suffit de suivre ce que la documentation MSDN nous indique, je site :

To convert the UniversalTime to SYSTEMTIME, use ULARGE_INTEGER to convert the int64 value to FILETIME, then use FileTimeToSystemTime to get SYSTEMTIME.

  • Tout d’abord on cast notre Valeur en type DateTime, car rappelez vous la propriété Value est de type Object^
  • Puis nous affectons au membre Quadpart de la structure ULARGE_INTEGER, la valeur UniversalTime de notre DateTime. Ce qui a pour effet de remplir automatiquement les deux membres HighPart et Lowpart que nous pouvons affecter aux membres de la structure FILETIME.
  • Nous convertissons cette dernière en SYSTEMTIME à l’aide de l’API Windows FileTimeToSystemTime
  • Ensuite, il suffit de formater la date en une chaine de caractères qui soit affichable à l’aide de l’API GetDateFormatEx(). Le 1er appel à GetDateFormatEx sert à obtenir la longueur exacte de la chaine de caractères.
  • La chaine ainsi retournée est de type WCHAR, que nous allons convertir en une chaine de caractère String^.

Code C++/CX

DateTime date=static_cast<DateTime>(record->GetAt(7)->Value);
    ULARGE_INTEGER l={0};
    l.QuadPart=date.UniversalTime;                                                        
    FILETIME ft={0};
    ft.dwHighDateTime=l.HighPart;
    ft.dwLowDateTime=l.LowPart;
    SYSTEMTIME st={0};
    ::FileTimeToSystemTime(&ft,&st);                            
    int cb=::GetDateFormatEx(LOCALE_NAME_SYSTEM_DEFAULT,LOCALE_USE_CP_ACP | DATE_LONGDATE,&st,NULL,NULL,0,NULL);                            
    std::unique_ptr<WCHAR> s(new WCHAR[cb]);
    cb=::GetDateFormatEx(LOCALE_NAME_SYSTEM_DEFAULT,LOCALE_USE_CP_ACP | DATE_LONGDATE,&st,NULL,s.get(),cb,NULL);                            
    Platform::StringReference sr(s.get());
    return sr.GetString();

 

Utilisation d’un BLOB

Le BLOB (Binary Large Object), va nous permettre de sauvegarder dans la base de données des objets sous forme Binaire, que ce soit, des Images, des vidéos, et même des objets .NET sérialisé en Binaire.

Tout d’abord, il faut créer la colonne de type LongBinary

Code Javascript

  1. listcolumns.push(new JetBlue.WinRT.JetBlueColumn("Picture", JetBlue.WinRT.Enum.ColumnType.longBinary, JetBlue.WinRT.Enum.ColumnFlags.maybeNull));

Code C#

listcolumns.Add(new JetBlueColumn("Picture",ColumnType.LongBinary,ColumnFlags.MaybeNull));

Code C++/CX

listcolumns->Append(ref new JetBlueColumn("Picture",ColumnType::LongBinary,ColumnFlags::MaybeNull));

 

Ensuite pour sauvegarder l’image dans la base, dans notre exemple, nous allons lire une image qui se trouve dans le projet et l’affecté à la propriété LongBinary de notre colonne.

En Javascript, nous utilisons la notion de Promise pour une exécution Asynchrone, comme illustré dans la méthode getBufferFromPictureAsync()

Code Javasctipt

  1. getBufferFromPictureAsync(new Windows.Foundation.Uri("ms-appx:///images/DavidC.jpg")).then(function (buffer) {
  2.         listcolumns[8].binaryValue = buffer;
  3.         //code omis pour plus de clart
  4.     }

getBufferFromPictureAsync()

function getBufferFromPictureAsync(uri)
    {
        var streams = Windows.Storage.Streams;
        var storage = Windows.Storage
        return new WinJS.Promise(function (complete)
        {
            setTimeout(function ()
            {
                storage.StorageFile.getFileFromApplicationUriAsync(uri).done(function (file)
                {                    
                    file.openAsync(storage.FileAccessMode.read).then(function (stream)
                    {                                                                                          
                        var reader = new streams.DataReader(stream);
                        reader.loadAsync(stream.size).then(function (cb) {
                            stream.seek(0);                            
                            var bytesArray=new Uint8Array(cb);
                            reader.readBytes(bytesArray);
                            reader.close();
                            stream.close();
                            complete(bytesArray);
                        });
                                                
                    });
                });                                                     
            }, 1000);
        });
    }

 

En c# nous pouvons dans notre exemple utiliser la notion de Task comme illustré dans la méthode GetBufferFromPictureAsync()

Code C#

listcolumns[8].BinaryValue = GetBufferFromPictureAsync(new System.Uri("ms-appx:///Assets/DavidC.jpg")).Result;

        Task<Byte[]> GetBufferFromPictureAsync(Uri uri)
        {
            return Task.Run<Byte[]>(async () =>
                {
                    Byte[] buffer = null;                   
                    var file = await Windows.Storage.StorageFile.GetFileFromApplicationUriAsync(uri);
                    var stream = await file.OpenReadAsync();
                    buffer = new Byte[stream.Size];
                    var ibuf =await  stream.ReadAsync(buffer.AsBuffer(), (uint)stream.Size, InputStreamOptions.None);
                    return buffer;
                });
           
        }

 

En C++/CX nous utiliserons directement l’interface WinRT IAsyncOperation<T> comme illustré dans la méthode GetBufferFromPictureAsync() . Vous noterez que comme en Javascript,

nous utilisons la notion de continuation avec la méthode then

Code C++/CX

task<Platform::WriteOnlyArray<BYTE>^> taskOnPicture(GetBufferFromPictureAsync(ref new Windows::Foundation::Uri(L"ms-appx:///DavidC.jpg")));
                     taskOnPicture.then([this,listcolumns,m_engine,table](Platform::WriteOnlyArray<BYTE>^ buffer)
                     {
                         listcolumns->GetAt(8)->BinaryValue=static_cast<Platform::Array<BYTE>^>(buffer);
                         //code omis pour plus de clart
                     }

GetBufferFromPictureAsync()

IAsyncOperation<Platform::WriteOnlyArray<BYTE>^>^ SampleDataSource::GetBufferFromPictureAsync(Windows::Foundation::Uri^ uri)
{

return create_async([uri]()
    {
        DataReader^ reader=nullptr;        
        auto taskfile=create_task(StorageFile::GetFileFromApplicationUriAsync(uri));
        return taskfile.then([&reader](StorageFile^ file)
        {
            return file->OpenAsync(FileAccessMode::Read);
        },task_continuation_context::use_arbitrary())
            .then([&reader](IRandomAccessStream^ stream)
            {
                reader=ref new DataReader(stream);                
                return reader->LoadAsync(static_cast<unsigned int>(stream->Size));                
        },task_continuation_context::use_arbitrary())
            .then([&reader](unsigned int cb)
            {
                Platform::WriteOnlyArray<BYTE>^ buffer=ref new Platform::Array<BYTE>(cb);
                
                reader->ReadBytes(buffer);
                
                return buffer;
        }).get();                
    });
}

Pour relire la valeur de la colonne, il suffit de l’indiquer dans la requête et de relire la propriété BinaryValue.

Code Snippet

columns.Add(new JetBlueColumn("Picture"));
                 var t1 = db.OpenTable("TETable", OpenTableFlags.ReadOnly);
                              
                 do
                 {
                     var record = t1.Records.Get(columns);
                     var buffer = record[8].BinaryValue;

Notez, qu’il peut être judicieux de tester si il est plus performant de charger une image à partir d’un blob ou la charger à partir du disque.

Création de tables avec des indexes.

Pour illustrer la création d’index, nous allons partir d’un modèle de données différents, qui se base sur le modèle des cartes magiques de David Catuhe.

Ce modèle est constitué pour simplifier, de trois entités, Blocks, Expansions, et Cards

Pour créer une table avec des indexes, nous avons plusieurs possibilités, en fonction de la complexité des indexes.

En effet un index peut-être défini sur plusieurs colonnes, dans ce cas la il faudra utiliser directement la classe JetBlueIndex.

Néanmoins, pour éviter de rentrer trop dans le détail et simplifier l’exercice, nous allons dans ce billet, manipuler des indexes simples.

La création d’une table ce fait toujours par l’intermédiaire d’une collection de la classe JetBlueColumn (reportez vous à mon précédant billet si vous souhaitez revoir ce point).

Table Blocks

static async Task<JetBlueTable> CreateTableBlockAsyncSimple(String tablename, JetBlueDatabase db)
        {
            List<JetBlueColumn> Columns = new List<JetBlueColumn>();
            Columns.Add(new JetBlueColumn("BlockName",ColumnType.Text,ColumnFlags.NotNull,"idxBlockName",IndexFlags.Unique));
            Columns.Add(new JetBlueColumn("BlockId",ColumnType.Int32,ColumnFlags.NotNull,"idxBlockId",IndexFlags.Primary));
            Columns.Add(new JetBlueColumn("BlockPicturePath",ColumnType.Text,ColumnFlags.MaybeNull));
            Columns.Add(new JetBlueColumn("BlockIndex",ColumnType.Int32,ColumnFlags.NotNull));
            Columns.Add(new JetBlueColumn("Picture", ColumnType.LongBinary, ColumnFlags.MaybeNull | ColumnFlags.Compressed));

            var table = await db.CreateTableAsync(tablename, Columns);

            return table;
        }

Table Cards

private static async Task<JetBlueTable> CreateTableCardsAsyncSimple(String tablename, JetBlueDatabase db)
        {
            List<JetBlueColumn> Columns = new List<JetBlueColumn>();
            Columns.Add(new JetBlueColumn("CardPicturePath", ColumnType.LongText, ColumnFlags.MaybeNull));
            Columns.Add(new JetBlueColumn("Type", ColumnType.LongText, ColumnFlags.MaybeNull, "idxType", IndexFlags.Tuples));
            Columns.Add(new JetBlueColumn("Text", ColumnType.LongText, ColumnFlags.MaybeNull, "idxText", IndexFlags.Tuples));
            Columns.Add(new JetBlueColumn("Flavor", ColumnType.LongText, ColumnFlags.MaybeNull, "idxFlavor", IndexFlags.Tuples));
            Columns.Add(new JetBlueColumn("Name", ColumnType.LongText, ColumnFlags.MaybeNull, "idxName", IndexFlags.Tuples));
            Columns.Add(new JetBlueColumn("CardId", ColumnType.Int32, ColumnFlags.NotNull, "ixdCardId", IndexFlags.Unique));
            Columns.Add(new JetBlueColumn("ExpansionId", ColumnType.Int32, ColumnFlags.NotNull, "idxExpansionId", IndexFlags.None));
            Columns.Add(new JetBlueColumn("Picture", ColumnType.LongBinary, ColumnFlags.MaybeNull | ColumnFlags.Compressed));
            Columns.Add(new JetBlueColumn("Author", ColumnType.Text, ColumnFlags.NotNull));
            Columns.Add(new JetBlueColumn("Power", ColumnType.Text, ColumnFlags.MaybeNull));
            Columns.Add(new JetBlueColumn("Rarity", ColumnType.Text, ColumnFlags.MaybeNull));
            Columns.Add(new JetBlueColumn("Price", ColumnType.IEEESingle, ColumnFlags.MaybeNull));
            Columns.Add(new JetBlueColumn("Number", ColumnType.Int32, ColumnFlags.MaybeNull));
            Columns.Add(new JetBlueColumn("Defense", ColumnType.Int32, ColumnFlags.MaybeNull));
            Columns.Add(new JetBlueColumn("Force", ColumnType.Int32, ColumnFlags.MaybeNull));
            Columns.Add(new JetBlueColumn("ColorId", ColumnType.Int32, ColumnFlags.NotNull));
            var table = await db.CreateTableAsync(tablename, Columns);
            return table;
        }

Table Expansions

static async Task<JetBlueTable> CreateTableExpansionAsyncSimple(String tablename, JetBlueDatabase db)
        {
            List<JetBlueColumn> Columns = new List<JetBlueColumn>();
            Columns.Add(new JetBlueColumn("ExpansionName", ColumnType.Text, ColumnFlags.NotNull, "idxExpansionName", IndexFlags.Unique));
            Columns.Add(new JetBlueColumn("ExpansionId", ColumnType.Int32, ColumnFlags.NotNull, "idxExpansionId", IndexFlags.Unique));
            Columns.Add(new JetBlueColumn("BlockId", ColumnType.Int32, ColumnFlags.NotNull, "idxBlockId", IndexFlags.None));
            Columns.Add(new JetBlueColumn("ExpansionPicturePath", ColumnType.Text, ColumnFlags.MaybeNull));
            Columns.Add(new JetBlueColumn("Picture", ColumnType.LongBinary, ColumnFlags.MaybeNull | ColumnFlags.Compressed));           
            var table = await db.CreateTableAsync(tablename, Columns);
            return table;
        }

 

Lors de la création d’une colonne, il est possible de lui définir un index, en précisant dans son constructeur, le nom de l’index, suivie du type de l’index.

Ici pour la table Blocks, nous définissons un index “idxBlockName”, de type IndexFlags.Unique. Si la valeur insérée n’est pas unique, lors de l’insertion de données, l’erreur JET_errKeyDuplicate sera retournée,

ainsi qu’un index primaire idxBlockId sur la colonne BlockId.

Par défaut, les indexes sont crée dans un ordre croissant, et dans notre exemple, triés basé sur la clé primaire.

Si on souhaite par exemple que la table soit triée par ordre croissant en fonction de l’index idxBlockName (donc par le nom du block), il faudra le préciser avec la méthode SetIndex() de l’objet Indexes, comme illustré dans l’extrait de code suivant :

Trie par ordre croissant

var table = await  m_db.OpenTableAsync("Blocks", OpenTableFlags.ReadOnly);
                List<JetBlueColumn> columns = new List<JetBlueColumn>();
                columns.Add(new JetBlueColumn("BlockName"));
                columns.Add(new JetBlueColumn("BlockId"));
                table.Indexes.SetIndex("idxBlockName");
                
                var cursor = await table.Records.GetAsync(columns, 100);

 

Remarque : La méthode table.Indexes.Get(“BlockName”) permet de retrouver le nom de l’index si vous ne connaissez pas son nom

Ensuite, il est possible de rechercher un enregistrement dans une table en fonction de critères.

Par exemple si nous souhaitons retrouver la valeur de la colonne BlockId en fonction du nom du block comme dans une requête SQL classique

Select blockId from Blocks where BlockName=”Dual Deck”.

Nous utiliserons à cet effet la classe JetBlueKey, qui permettra de préciser les critères de recherche.

Dans cet exemple, nous utilisons la méthode SetKey de l’objet Indexes qui prend comme paramètre une collection de JetBlueKey.

Lors de la construction d’un critère de recherche, nous précisons le nom de la colonne “BlockName”, la borne que nous souhaitons “Equal”, ainsi que le critère “Duel Decks”

Critère de recherche Méthode 1

  1. var tableBlock = await m_db.OpenTableAsync("Blocks", OpenTableFlags.ReadOnly);
  2.                 var indexName = tableBlock.Indexes.Get("BlockName");
  3.                 columns.Add(new JetBlueColumn("BlockId"));
  4.                 
  5.                 List<JetBlueKey> keys = new List<JetBlueKey>();
  6.                 keys.Add(new JetBlueKey("BlockName", SeekFlags.Equal, "Duel Decks"));
  7.                 tableBlock.Indexes.SetKey(keys) ;                               
  8.                 var cursor1=await tableBlock.Records.GetAsync(columns);               
  9.                 DisplayColumnsOnOutput(columns);
  10.                 tableBlock.Close();

Une autre manière de faire, et d’utiliser directement une des méthodes surchargées GetAsync() qui prend comme paramètres une collection de JetBlueKey.

Critère de recheche Méthode 2

var cursor=await m_db.OpenTableAsync("Blocks", OpenTableFlags.ReadOnly);
                List<JetBlueKey> criterias = new List<JetBlueKey>();
                JetBlueKey key2 = new JetBlueKey();
                key2.ColumnName = "BlockName";
                key2.SearchCriteria.StartSearchKey = "Duel Decks";
                key2.SearchCriteria.SeekStart = SeekFlags.Equal;
                criterias.Add(key2);
                
                var results = await cursor.Records.GetAsync(criterias, columns, 100);
                foreach (var items in results)
                {
                    DisplayColumnsOnOutput(items);
                }

 

Allons un peu plus loin. Imaginons maintenant que nous souhaitons récupérer des enregistrements d’une table, mais qui répondent à plusieurs critères.

Par exemple nous souhaitons obtenir les enregistrements de la table Cards, selon la requête SQL suivante :

Select Name from Cards Where ExpansionID=174 And CardId >=26832 and CardId <=26837

Recherche multi-criteres

var columns = new List<JetBlueColumn>();
                columns.Add(new JetBlueColumn("Name"));
                var cursor = await m_db.OpenTableAsync("Cards", OpenTableFlags.ReadOnly);
                List<JetBlueKey> criterias = new List<JetBlueKey>();
                
                var criteria1 = new JetBlueKey();
                criteria1.ColumnName = "ExpansionId";
                criteria1.SearchCriteria.StartSearchKey = 174;

                var criteria2 = new JetBlueKey();
                criteria2.ColumnName = "CardId";
                criteria2.SearchCriteria.StartSearchKey = 26832;
                criteria2.SearchCriteria.SeekStart = SeekFlags.GreaterOrEqual;
                criteria2.SearchCriteria.EndSearchKey = 26837;
                criteria2.SearchCriteria.SeekEnd = SeekFlags.LessOrEqual;
                criterias.Add(criteria1);
                criterias.Add(criteria2);
                var results = await cursor.Records.GetAsync(criterias, columns, 100);

Encore une fois nous utilisons une collection de JetBlueKey, mais cette fois-ci, pour le second critère nous précisons un champ d’action entre une borne inférieure et une bonne supérieure.

L’énumération SeekFlags, peut prendre les valeurs Equal , LessThan , LessOrEqual , GreaterOrEqual, GreaterThan, vous permettant de combiner tout type de recherche.

Finissons maintenant ce tour d’horizon des indexes, sur la recherche d’une chaine de caractères sur plusieurs colonnes de type Text ou LongText.

Pour ce faire, il faut impérativement, que votre Index soit de type IndexFlags.Tuples comme définie plus haut lors de la création de la table Cards, sur les colonnes Type, Name, Flavor et Text.

Imaginons que nous souhaitons rechercher la chaine “red” dans les colonnes Name et Text, soit la requête SQL :

Select Name from Cards where Name LIKE ‘red’ And Text LIKE ‘red’

Recherche sur colonne Text

                List<JetBlueKey> keys = new List<JetBlueKey>();
                var columns = new List<JetBlueColumn>();
                columns.Add(new JetBlueColumn("Name"));
                keys.Add(new JetBlueKey("Name", SeekFlags.Equal, "red"));
                keys.Add(new JetBlueKey("Text",SeekFlags.Equal,"red"));
               
                var m_globalCursor = m_db.OpenTable("Cards", OpenTableFlags.ReadOnly);                
                m_globalCursor.Indexes.SetKey(keys);
                do
                {
                    
                    var result=await m_globalCursor.Records.GetAsync(columns);                    
                    DisplayColumnsOnOutput(result);

                }
                while (m_globalCursor.Records.Move(MoveDirection.Next) != MoveResult.NoCurrentRecord);

L’utilisation des clés de recherches est identique à celle des nombres et le moteur recherche, dans les colonnes Name et Text, la chaine “red”.

Le moteur retournera aussi bien une la colonne Name ayant comme valeur “Red Ward” aussi bien que “Merciless Predator”

Il est également possible de rechercher sur de multi-critères.

Select Name from Cards where Text LIKE “shade” And Name LIKE “shade” And Flavor LIKE “necromancer” And Type LIKE “creature”

Recherche multi-crières

            var cursor = await m_db.OpenTableAsync("Cards", OpenTableFlags.ReadOnly);
                List<JetBlueKey> criterias = new List<JetBlueKey>();
                JetBlueKey crit1 = new JetBlueKey();
                
                crit1.ColumnName = "Text";                
                crit1.SearchCriteria.StartSearchKey = "shade";

                JetBlueKey crit11 = new JetBlueKey();
                crit11.ColumnName = "Name";

                crit11.SearchCriteria.StartSearchKey = "shade";

                JetBlueKey crit3 = new JetBlueKey();
                crit3.ColumnName = "Flavor";

                crit3.SearchCriteria.StartSearchKey = "necromancer";

                JetBlueKey crit2 = new JetBlueKey();
                crit2.ColumnName = "Type";

                crit2.SearchCriteria.StartSearchKey = "creature";

                criterias.Add(crit1);
                criterias.Add(crit11);
                criterias.Add(crit2);
                criterias.Add(crit3);

                var results = await cursor.Records.GetAsync(criterias, columns, 100);

 

Dans ces deux billets, nous avons abordé les basiques de l’utilisation de ce wrapper au dessus de ESE, il reste encore pas mal de travail avant de pouvoir le mettre en production, néanmoins, je suis preneur de vos feedbacks si vous souhaitez l’utiliser.

Si vous souhaitez connaitre mieux les arcanes de ESE vous pouvez allez directement à la documentation https://msdn.microsoft.com/en-us/library/gg269259(v=exchg.10).aspx

Téléchargement des binaires

 

Eric