First Steps with RedisGraph

RedisGraph is a super-fast graph database, and like others of its kind (such as Neo4j), it is useful to represent networks of entities and their relationships. Examples include social networks, family trees, and organisation charts.

Getting Started

The easiest way to try RedisGraph is using Docker. Use the following command, which is based on what the Quickstart recommends but instead uses the edge tag, which would have the latest features and fixes:

sudo docker run -p 6379:6379 -it --rm redislabs/redisgraph
Redis with RedisGraph running in Docker

You will also need the redis-cli tool to run the example queries. On Ubuntu (or similar), you can get this by installing the redis-tools package.

Tom Loves Judy

We’ll start by representing something really simple: Tom Loves Judy.

Tom Loves Judy.

We can create this graph using a single command:

GRAPH.QUERY TomLovesJudy "CREATE (tom:Person {name: 'Tom'})-[:loves]->(judy:Person {name: 'Judy'})"

When using redis-cli, queries will also follow the format of GRAPH.QUERY <key> "<cypher_query>". In RedisGraph, a graph is stored in a Redis key (in this case called “TomLovesJudy“) with the special type graphdata, thus this must always be specified in queries. The query itself is the part between double quotes, and uses a language called Cypher. Cypher is also used by Neo4j among other software, and RedisGraph implements a subset of it.

Cypher represents nodes and relationships using a sort of ASCII art. Nodes are represented by round brackets (parentheses), and relationships are represented by square brackets. The arrow indicates the direction of the relationship. RedisGraph at present does not support undirected relationships. When you run the above command, Redis should provide some output indicating what happened:

2 nodes and one relationship. Makes sense.

Since our graph has been created, we can start running queries against it. For this, we use the MATCH keyword:

GRAPH.QUERY TomLovesJudy "MATCH (x) RETURN x"

Since round brackets represent a node, here we’re saying that we want the query to match any node, which we’ll call x, and then return it. The output for this is quite verbose:

1) 1) "x"
2) 1) 1) 1) 1) "id"
            2) (integer) 0
         2) 1) "labels"
            2) 1) "Person"
         3) 1) "properties"
            2) 1) 1) "name"
                  2) "Tom"
   2) 1) 1) 1) "id"
            2) (integer) 1
         2) 1) "labels"
            2) 1) "Person"
         3) 1) "properties"
            2) 1) 1) "name"
                  2) "Judy"
3) 1) "Query internal execution time: 61.509847 milliseconds"

As you can see, this has given us the whole structure of each node. If we just want to get something specific, such as the name, then we can specify it in the RETURN clause:

GRAPH.QUERY TomLovesJudy "MATCH (x) RETURN x.name"
1) 1) "x.name"
2) 1) 1) "Tom"
   2) 1) "Judy"
3) 1) "Query internal execution time: 0.638126 milliseconds"

We can also query based on relationships. Let’s see who loves who:

GRAPH.QUERY TomLovesJudy "MATCH (x)-[:loves]->(y) RETURN x.name, y.name"
1) 1) "x.name"
   2) "y.name"
2) 1) 1) "Tom"
      2) "Judy"
3) 1) "Query internal execution time: 54.642536 milliseconds"

It seems like Tom Loves Judy. Unfortunately, Judy does not love Tom back.

Company Shareholding

Let’s take a look at a slightly more interesting example.

Company A is owned by individuals X (85%) and Y (15%). Company B is owned by individuals Y (55%) and Z (45%).

In this graph, we have companies (blue nodes) which are owned by multiple individuals (red nodes). We can’t create this as a single command as we did before. We also can’t simply issue a series of CREATE commands, because we may end up creating multiple nodes with the same name.

Instead, let’s create all the nodes separately first:

GRAPH.QUERY Companies "CREATE (:Individual {name: 'X'})"
GRAPH.QUERY Companies "CREATE (:Individual {name: 'Y'})"
GRAPH.QUERY Companies "CREATE (:Individual {name: 'Z'})"

GRAPH.QUERY Companies "CREATE (:Company {name: 'A'})"
GRAPH.QUERY Companies "CREATE (:Company {name: 'B'})"

You’ll notice here that the way we are defining nodes is a little different. A node follows the structure (alias:type {properties}). The alias is not much use in such CREATE commands, but on the other hand, the type now (unlike in the earlier example) gives us a way to distinguish between different kinds of nodes.

Now that we have the nodes, we can create the relationships:

GRAPH.QUERY Companies "MATCH (x:Individual { name : 'X' }), (c:Company { name : 'A' }) CREATE (x)-[:owns {percentage: 85}]->(c)"
GRAPH.QUERY Companies "MATCH (x:Individual { name : 'Y' }), (c:Company { name : 'A' }) CREATE (x)-[:owns {percentage: 15}]->(c)"
GRAPH.QUERY Companies "MATCH (x:Individual { name : 'Y' }), (c:Company { name : 'B' }) CREATE (x)-[:owns {percentage: 55}]->(c)"
GRAPH.QUERY Companies "MATCH (x:Individual { name : 'Z' }), (c:Company { name : 'B' }) CREATE (x)-[:owns {percentage: 45}]->(c)"

In order to make sure we apply the relationships to existing nodes (as opposed to creating new ones), we first find the nodes we want with a MATCH clause, and then CREATE the relationship between them. You’ll notice that our relationships now also have properties.

Now that our graph is set up, we can start querying it! Here are a few things we can do with it.

Return the names of all the nodes:

GRAPH.QUERY Companies "MATCH (x) RETURN x.name"
1) 1) "x.name"
2) 1) 1) "X"
   2) 1) "Y"
   3) 1) "Z"
   4) 1) "A"
   5) 1) "B"
3) 1) "Query internal execution time: 0.606600 milliseconds"

Return the names only of the companies:

GRAPH.QUERY Companies "MATCH (c:Company) RETURN c.name"
1) 1) "c.name"
2) 1) 1) "A"
   2) 1) "B"
3) 1) "Query internal execution time: 0.515959 milliseconds"

Return individual ownership in each company (separate fields):

GRAPH.QUERY Companies "MATCH (i)-[s]->(c) RETURN i.name, s.percentage, c.name"
1) 1) "i.name"
   2) "s.percentage"
   3) "c.name"
2) 1) 1) "X"
      2) (integer) 85
      3) "A"
   2) 1) "Y"
      2) (integer) 15
      3) "A"
   3) 1) "Y"
      2) (integer) 55
      3) "B"
   4) 1) "Z"
      2) (integer) 45
      3) "B"
3) 1) "Query internal execution time: 1.627741 milliseconds"

Return individual ownership in each company (concatenated strings):

GRAPH.QUERY Companies "MATCH (i)-[s]->(c) RETURN i.name + ' owns ' + round(s.percentage) + '% of ' + c.name"
1) 1) "i.name + ' owns ' + round(s.percentage) + '% of ' + c.name"
2) 1) 1) "X owns 85% of A"
   2) 1) "Y owns 15% of A"
   3) 1) "Y owns 55% of B"
   4) 1) "Z owns 45% of B"
3) 1) "Query internal execution time: 1.281184 milliseconds"

Find out who owns at least 50% of the shares in Company A:

GRAPH.QUERY Companies "MATCH (i)-[s]->(c) WHERE s.percentage >= 50 AND c.name = 'A' RETURN i.name"
1) 1) "i.name"
2) 1) 1) "X"
3) 1) "Query internal execution time: 1.321579 milliseconds"

Wrapping Up

In this article, we’ve seen how to:

  • get up and running with RedisGraph
  • create simple graphs
  • perform basic queries

We’ve obviously scratched the surface of RedisGraph and Cypher, but hopefully these examples will help others who, like me, are new to this space.

XAML Hot Reload

Having been away from WPF for a long time, it was a pleasant surprise for me to find this when building a small tool a few days ago:

The panel at the top says that Hot Reload is available.

XAML Hot Reload is a feature that causes changes in XAML to immediately be reflected in an application running in debug mode. It applies to WPF and UWP applications, and is currently in preview for Xamarin Forms apps.

Update 7th November 2019: thanks to the Twitter user who pointed out that this feature has been around for three years under the name “XAML Edit and Continue” for WPF and UWP apps. It recently got rebranded and extended to Xamarin Forms.

Basically, if I go and change the XAML for the above window (making it even uglier than it already is) while the application is still running, the changes are applied instantly as soon as I save the file:

Changes to styling from the XAML were instantly reflected in the window on save.

This kind of live-reload has existed in the web development space for a while thanks to technologies such as Browsersync. However, it is nice to see it finally arrive in the much-neglected realm of desktop application development, for those still stuck in it.

Running Legacy Windows Programs on Linux with WINE

I have a few really old Windows programs from the Windows 95 era that I never ended up replacing. Nowadays, these are really hard to run on Windows 10. Ironically, it is quite easy to run them on Linux, thanks to WINE:

“Wine (originally an acronym for “Wine Is Not an Emulator”) is a compatibility layer capable of running Windows applications on several POSIX-compliant operating systems, such as Linux, macOS, & BSD. Instead of simulating internal Windows logic like a virtual machine or emulator, Wine translates Windows API calls into POSIX calls on-the-fly, eliminating the performance and memory penalties of other methods and allowing you to cleanly integrate Windows applications into your desktop.”

One such program is this Family Tree software that came with the July 2001 issue of PC Format magazine.

To run this, we first need to install WINE, which on Ubuntu (or similar) would work something like this:

sudo apt-get install wine

After popping in the PC Format CD containing the software, simply locate the autorun executable. Then run the wine command, passing this executable (in this case PCF124.exe) as an argument:

After inserting the CD, locate the autorun executable, and run it using WINE. Although it’s a Windows program, it works just fine.

Selecting Family Tree 2 from the menu runs the corresponding installer. Although this expects a Windows-like filesystem and writes to a Windows registry, WINE has no problem mapping these out.

Select the install location on what looks like a Windows filesystem.
Doesn’t this make you feel nostalgic?

When this finishes, the program is actually installed, and can be found and run from the application menu of whatever desktop environment you’re using (in my case, Plasma by KDE):

Running Family Tree 2.0, we get an error that says “Please install default printer”.

For some bizarre reason, this particular family tree software requires a printer to be installed, and will not work without one. While you probably won’t have this problem, for me it was a tough one that left me wondering for a while. I managed to solve it only by asking for help on Ask Ubuntu and getting an extremely insightful answer:

“When you install printer-driver-cups-pdf (or cups-pdf for Ubuntu 15.10 and earlier) a PDF printer is added which saves the printed files in ~/PDF/. All the printers installed in your Ubuntu OS also work from WINE, you don’t need to do anything about it.
But:
“If you just normally installed CUPS on your 64-bit Ubuntu (uname -r gives x86_64 if it is 64-bit), this won’t work when you run a 32-bit software like yours from 1995 presumably is. The solution in this case is to install the 32-bit CUPS library, so that 32-bit WINE is also able to find your printers:”

sudo apt install libcups2:i386

Sure enough, that worked when I did this on a virtual machine on another laptop, but not on this one. This time, I simply needed to install cups-pdf, because the CPU architecture is different.

Family Tree 2.0 is running on Linux Kubuntu 19.10, thanks to WINE.

As you can see, this Windows-95-era piece of software is now working flawlessly on Linux. Once this is done, don’t forget to eject the CD (the eject command in the terminal has been a fun discovery for me) to unmount it from the filesystem. If you need to uninstall a Windows program you installed via WINE, you can do so directly from your desktop environment’s application menu. And if you need go deeper, WINE’s filesystem is located in the hidden .wine directory under your home folder.

Encrypting Strings in C# using Authenticated Encryption

Encryption is fundamental and ubiquitous. Whether it’s to prevent sensitive settings (such as passwords and API tokens) from falling into the wrong hands, or making sure no one listens in on confidential communications, encryption is extremely important. Many people do not even realise that they use it every day.

Encrypting data using the .NET Framework or .NET Core libraries, however, is not trivial. There are different ways to encrypt and decrypt data, and sometimes this requires some knowledge about the underlying algorithm.

To keep things really simple, we’ll use a third party library that provides a simple interface for encryption and decryption. Because this library uses strings and byte arrays, it is not suitable for encryption of large amounts of data, such as huge files, which would bloat the application’s memory. However, it is perfectly fine for small strings.

Later in the article, I also share a simple tool that I built to help generate keys and test encryption and decryption. You can find this tool under the AuthenticatedEncryptionTester folder in the Gigi Labs BitBucket repository.

Using AuthenticatedEncryption

AuthenticatedEncryption is a library that provides simple methods for encryption and decryption:

“The library consists of a single static class. This makes it very easy to use. It uses Authenticated Encryption with Associated Data (AEAD), using the approach called “Encrypt then MAC” (EtM). It uses one key for the encryption part (cryptkey) and another key for the MAC part (authkey).”

All we need to start using this is to install the corresponding NuGet package, either using the Package Manager Console:

Install-Package AuthenticatedEncryption

…or using the .NET Core command line tools:

dotnet add package AuthenticatedEncryption

The project’s readme file (which is the first thing you see in the GitHub repo) explains how it’s used, and it is really simple. First, you generate two keys, called the cryptkey and authkey respectively:

var cryptKey = AuthenticatedEncryption.AuthenticatedEncryption.NewKey();
var authKey = AuthenticatedEncryption.AuthenticatedEncryption.NewKey();

This is something you will typically do once, since you have to encrypt and decrypt using the same pair of keys.

Next, we need something to encrypt. We can get this from user input:

Console.Write("Enter something to encrypt: ");
string plainText = Console.ReadLine();

We can now encrypt the plain text by using the keys we generated earlier:

string encrypted = AuthenticatedEncryption.AuthenticatedEncryption
    .Encrypt(plainText, cryptKey, authKey);
Console.WriteLine($"Encrypted: {encrypted}");

And we can also decrypt the cipher text using a similar mechanism:

string decrypted = AuthenticatedEncryption.AuthenticatedEncryption
    .Decrypt(encrypted, cryptKey, authKey);
Console.WriteLine($"Decrypted: {decrypted}");

You will by now have noted the double AuthenticatedEncryption that is constantly repeated throughout the code. This is a result of the unfortunate choice of the library author to use the same for the class and namespace. There is already an open issue for this.

Let’s run this code and see what happens:

Simple encryption and decryption using the AuthenticatedEncryption library. Running on Kubuntu 19.10 using .NET Core.

As you can see, the input string was encrypted and the result was encoded in base64. This was later decrypted to produce the original input string once again.

Authenticated Encryption Tester

To facilitate key generation as well as experimentation, I wrote this small tool:

Authenticated Encryption Tester. A simple tool to quickly use the functions of the AuthenticatedEncryption library.

This lets you use the AuthenticatedEncryption library functionality that we have just seen in the previous section. It’s useful to initially generate your keys, and also to test that you are actually able to encrypt and decrypt your secrets successfully.

It is a WPF application running on .NET Core 3, so unlike the AuthenticatedEncryption library, unfortunately it only works on Windows. However, for those of you who, like me, have the misfortune of already using Windows, it can turn out to be a handy utility.

You can get the code from the AuthenticatedEncryptionTester folder in the Gigi Labs BitBucket repository. While I won’t go through all the code in the interest of brevity, I’d like to go through some parts and show that it’s doing pretty much what we’ve seen in the previous section.

        private void GenerateCryptKeyButton_Click(object sender, RoutedEventArgs e)
            => GenerateKeyInTextBox(this.CryptKeyField);

        private void GenerateAuthKeyButton_Click(object sender, RoutedEventArgs e)
            => GenerateKeyInTextBox(this.AuthKeyField);

// ...

        private void GenerateKeyInTextBox(TextBox textBox)
        {
            string key = AuthenticatedEncryption
                .AuthenticatedEncryption.NewKeyBase64Encoded();
            textBox.Text = key;
        }

The first two fields in the window expect to have the two keys in base64 format. You can either use keys you had generated earlier and stored, or you can hit the Generate buttons to create new ones. These buttons create new keys using the NewKeyBase64Encoded() method, which is just like NewKey() except that it returns a base64-encoded string instead of a byte array. This is handy in situations where you want a string representation, such as in a GUI like this.

Encryption and decryption also work just like in the previous section, and the implementation merely adds some extra code for validation and I/O. This is the method that runs when you click the Encrypt button:

        private void EncryptButton_Click(object sender, RoutedEventArgs e)
        {
            const string operation = "Encrypt";

            string cryptKeyBase64 = this.CryptKeyField.Text;
            string authKeyBase64 = this.AuthKeyField.Text;
            string plainText = this.PlainTextField.Text;

            try
            {
                if (string.IsNullOrWhiteSpace(cryptKeyBase64)
                    || string.IsNullOrWhiteSpace(authKeyBase64)
                    || string.IsNullOrWhiteSpace(plainText))
                {
                    ShowWarning("Both keys and the plain text must have a value.",
                        operation);
                }
                else
                {
                    byte[] cryptKey = Convert.FromBase64String(cryptKeyBase64);
                    byte[] authKey = Convert.FromBase64String(authKeyBase64);

                    string cipherText = AuthenticatedEncryption
                        .AuthenticatedEncryption.Encrypt(plainText, cryptKey, authKey);
                    this.CipherTextField.Text = cipherText;
                }
            }
            catch (Exception ex)
            {
                ShowError(ex, operation);
            }
        }

The Encrypt button takes what’s in the Plain Text field and puts an encrypted version in the Cipher Text field. The Decrypt button does the opposite, taking the Cipher Text and putting the decrypted version in the Pain Text field. The code for the Decrypt button is very similar to that of the Encrypt button so I won’t include it here.

One thing you’ll note as you experiment with this is that the encrypted output string changes every time. This is an expected behaviour that provides better security. By clearing the value in the Plain Text field before hitting Decrypt, you can verify that it is always decrypted correctly to the original input string, even with different encrypted values.

Summary

The AuthenticatedEncryption library is great for encryption and decryption of simple strings. For large amounts of data, you should instead use streams together with the cryptographic APIs available in the .NET Framework or .NET Core.

You can use my Authenticated Encryption Tester to generate keys or experiment with encryption and decryption using the AuthenticatedEncryption library. It is built on WPF so it only works on Windows.