StackExchange.Redis: Usando estruturas de dados Redis Lists
Tags: redis, dotnet, programming, braziliandevs
Published: 2023-02-19 07:11:10

## Introdução

Redis Lists são listas duplamente vinculadas(linked list) que permitem que você empurre e pop da frente(`head`) e da cauda(`tail`). Isso permite que você crie o que são essencialmente `filas` e `pilhas` de Redis Strings. A biblioteca `StackExchange.Redis` fornece uma interface intuitiva para trabalhar com listas no `IDatabase`.

## Redis List na prática

As listas são listas duplamente vinculadas (linked list), o que significa que você pode empurrar ou pular de qualquer lado da lista. No entanto, qualquer tipo de acesso indexado torna-se uma operação O(N), onde N é o número de elementos que você precisa percorrer para alcançar o índice.

### Adicionando itens na lista

Vamos adicionar algumas linguagens de programação para o lado esquerdo da lista(head).

```csharp
var programmingLanguages = new string[] { "C#", "F#", "Elixir", "Java", "JavaScript", "Pascal" };
await database.ListLeftPushAsync(programmingLanguagesKey, programmingLanguages.Select(p => (RedisValue)p).ToArray());
```

Agora vamos tentar imprimir o primeiro elemento desta lista. As listas são indexadas da esquerda para a direita, consequentemente a última linguagem de programação que adicionamos para a esquerda, será o 0º índice "Pascal”

```csharp
var lastProgramingLanguage = await database.ListGetByIndexAsync(programmingLanguagesKey, 0);
Console.WriteLine($"The last programming language in the list is: {lastProgramingLanguage}");
```

```bash
// output
The last programming language in the list is: Pascal
```

E se acessássemos o final da lista, com o índice -1, obteríamos o primeiro elemento que colocamos no lado esquerdo da lista, "C#":

```csharp
var firstProgramingLanguage = await database.ListGetByIndexAsync(programmingLanguagesKey, -1);
Console.WriteLine($"The first programming language in the list is: {firstProgramingLanguage}");
```

```bash
The first programming language in the list is: C#
```

Em nossa lista de empresas de tecnologias, agora vamos adicionar a direita. Como estamos pressionando para a direita, o item em questão é adicionado ao final da lista, tornando-o o último elemento da lista se iterarmos na lista.

```csharp
var technologyCompanies = new string[] { "Microsoft", "Oracle", "Apple", "IBM" };
await database.ListRightPushAsync(technologyCompaniesKey, technologyCompanies.Select(p => (RedisValue)p).ToArray());
```

Agora, se tentarmos acessar o primeiro elemento da lista, obteremos o primeiro elemento que empurramos para a direita, "Microsoft".

```csharp
var firstTechnologyCompany = await database.ListGetByIndexAsync(technologyCompaniesKey, 0);
Console.WriteLine($"The first technology in the list is: {firstTechnologyCompany}");
```

```csharp
// output
The first technology in the list is: Microsoft
```

Por outro lado, se acessarmos o último elemento, obteremos "IBM"

```csharp
var lastTechnologyCompany = await database.ListGetByIndexAsync(technologyCompaniesKey, -1);
Console.WriteLine($"The last technology in the list is: {lastTechnologyCompany}");
```

```csharp
// output
The last technology in the list is: IBM
```

### Enumerar uma Lista

Para enumerar uma lista, você pode usar o método `ListRange`. Se você passar um índice de início, o intervalo começará a partir daí, e se você passar um índice de parada, ele parará ali, se você não passar nem um início nem um fim, ele irá puxar para trás toda a lista.

```csharp
Console.WriteLine($"Programming language index 0 to -1: {string.Join(", ", await database.ListRangeAsync(programmingLanguagesKey))}");
Console.WriteLine($"Technology companies index 0 to -2: {string.Join(", ", await database.ListRangeAsync(technologyCompaniesKey, 0, -2))}");
```

```csharp
// output
Programming language index 0 to -1: Pascal, JavaScript, Java, Elixir, F#, C#
Technology companies index 0 to -2: Microsoft, Oracle, Apple
```

### Mover elementos entre listas

Você também pode transferir elementos entre listas usando o método `ListMove`. Isso aceita duas chaves de lista, bem como o lado de origem e o lado de destino. Estes são os lados da lista de onde você irá pular e empurrar, respectivamente.

```csharp
await database.ListMoveAsync(programmingLanguagesKey, technologyCompaniesKey, ListSide.Right, ListSide.Left);
```

```csharp
127.0.0.1:6379> lrange programming-languages 0 -1
1) "Pascal"
2) "JavaScript"
3) "Java"
4) "Elixir"
5) "F#"
127.0.0.1:6379> lrange technology-companies 0 -1
1) "C#"
2) "Microsoft"
3) "Oracle"
4) "Apple"
5) "IBM"
```

### Lista como uma fila

Você pode usar uma lista Redis como uma fila FIFO de fato pressionando e abrindo de diferentes lados. Convencionalmente, você adicionar para a esquerda, removeria para a direita:

```bash
await database.ListLeftPushAsync(programmingLanguagesKey, "Rust");
Console.WriteLine($"Dequeued: {await database.ListRightPopAsync(programmingLanguagesKey)}");
```

```bash
// output
Dequeued: F#

127.0.0.1:6379> lrange programming-languages 0 -1
1) "Rust"
2) "Pascal"
3) "JavaScript"
4) "Java"
5) "Elixir"
```

### Lista como uma pilha

Você também pode fazer com que suas listas atuem como pilhas LIFO, pressionando e retirando das mesmas, por convenção, você normalmente usaria o lado esquerdo.

```csharp
await database.ListLeftPushAsync(technologyCompaniesKey, "JetBrains");
Console.WriteLine($"Popping: {await database.ListLeftPopAsync(technologyCompaniesKey)}");
```

```csharp
// output
Popping: JetBrains
```

### Pesquisando na lista

As listas Redis também permitem encontrar o índice de um item específico usando o método `ListPosition`.

```csharp
Console.WriteLine($"Position of Java: {await database.ListPositionAsync(programmingLanguagesKey, "Java")}");
```

```csharp
// ouput
Position of Java: 3
```

### Tamanho da Lista

Você usa o método `ListLength` para determinar o tamanho de uma determinada lista.

```csharp
Console.WriteLine($"There are {await database.ListLengthAsync(technologyCompaniesKey)} technology companies in our list");
```

```csharp
// output
There are 5 technology companies in our list
```

## Código completo

```csharp
using StackExchange.Redis;
using System.Diagnostics;

var options = new ConfigurationOptions
{
    EndPoints = new EndPointCollection { "localhost:6379" }
};

var conn = ConnectionMultiplexer.Connect(options);
var database = conn.GetDatabase();

var programmingLanguagesKey = "programming-languages";
var technologyCompaniesKey = "technology-companies";
var keysToDelete = new string[] { programmingLanguagesKey, technologyCompaniesKey };
await database.KeyDeleteAsync(keysToDelete.Select(k => (RedisKey)k).ToArray());

var programmingLanguages = new string[] { "C#", "F#", "Elixir", "Java", "JavaScript", "Pascal" };
await database.ListLeftPushAsync(programmingLanguagesKey, programmingLanguages.Select(p => (RedisValue)p).ToArray());

var lastProgramingLanguage = await database.ListGetByIndexAsync(programmingLanguagesKey, 0);
Console.WriteLine($"The last programming language in the list is: {lastProgramingLanguage}");

var firstProgramingLanguage = await database.ListGetByIndexAsync(programmingLanguagesKey, -1);
Console.WriteLine($"The first programming language in the list is: {firstProgramingLanguage}");

var technologyCompanies = new string[] { "Microsoft", "Oracle", "Apple", "IBM" };
await database.ListRightPushAsync(technologyCompaniesKey, technologyCompanies.Select(p => (RedisValue)p).ToArray());

var firstTechnologyCompany = await database.ListGetByIndexAsync(technologyCompaniesKey, 0);
Console.WriteLine($"The first technology in the list is: {firstTechnologyCompany}");

var lastTechnologyCompany = await database.ListGetByIndexAsync(technologyCompaniesKey, -1);
Console.WriteLine($"The last technology in the list is: {lastTechnologyCompany}");

Console.WriteLine($"Programming language index 0 to -1: {string.Join(", ", await database.ListRangeAsync(programmingLanguagesKey))}");
Console.WriteLine($"Technology companies index 0 to -2: {string.Join(", ", await database.ListRangeAsync(technologyCompaniesKey, 0, -2))}");

await database.ListMoveAsync(programmingLanguagesKey, technologyCompaniesKey, ListSide.Right, ListSide.Left);

await database.ListLeftPushAsync(programmingLanguagesKey, "Rust");
Console.WriteLine($"Dequeued: {await database.ListRightPopAsync(programmingLanguagesKey)}");

await database.ListLeftPushAsync(technologyCompaniesKey, "JetBrains");
Console.WriteLine($"Popping: {await database.ListLeftPopAsync(technologyCompaniesKey)}");

Console.WriteLine($"Position of Java: {await database.ListPositionAsync(programmingLanguagesKey, "Java")}");

Console.WriteLine($"There are {await database.ListLengthAsync(technologyCompaniesKey)} technology companies in our list");
```