StackExchange.Redis: Usando estruturas de dados Redis Sets
Tags: programming, redis, dotnet, braziliandevs
Published: 2023-02-19 17:15:15

## Introdução

Redis Sets são uma implementação de um conjunto matemático. E tem as seguintes características:

- Eles são desordenados.
- Eles não permitem duplicação, portanto não há membros repetidos.
- Eles podem ser combinados para criar novos conjuntos.

## Redis Sets na prática

Imaginemos que estejamos criando um MMORPG e temos esses 5 conjuntos de usuários 

- ActiveUsers
- InactiveUsers
- OnlineUsers
- OfflineUsers
- AllUsers

Agora vamos preencher nossos conjuntos de usuários ativos, inativos e offline. Para fazer isso, usaremos o método `SetAddAsync`. Este método é variável, então podemos fazer isso em um comando para cada conjunto.

```csharp
var activeUsers = new string[] { "User:1", "User:2" };
var inactiveUsers = new string[] { "User:3", "User:4" };
var offlineUsers = new string[] { "User:5", "User:6", "User:7" };
await database.SetAddAsync(activeUsersKey, activeUsers.Select(u => (RedisValue)u).ToArray());
await database.SetAddAsync(inactiveUsersKey, inactiveUsers.Select(u => (RedisValue)u).ToArray());
await database.SetAddAsync(offlineUsersKey, offlineUsers.Select(u => (RedisValue)u).ToArray());
```

### Combinando conjuntos para obter todos os usuários

Não preenchemos nosso conjunto `allUsersKey`. Se considerarmos active, inactive, offline, podemos usar as operações de combinação de conjuntos para obter um conjunto com todos os nossos usuários.

```csharp
await database.SetCombineAndStoreAsync(SetOperation.Union, allUsersKey, new RedisKey[] { activeUsersKey, inactiveUsersKey, offlineUsersKey });
```

### Verificar associação

Os conjuntos não permitem a duplicação, mas permitem verificações de associação O(1) muito rápidas. Portanto, se quiséssemos verificar se User:6 está offline, poderíamos fazê-lo facilmente:

```csharp
var user6Offline = await database.SetContainsAsync(offlineUsersKey, "User:6");
Console.WriteLine($"User:6 offline: {user6Offline}");
```

```csharp
// output 
User:6 offline: True
```

### Enumerar Conjunto

Quando você deseja enumerar os membros de um conjunto, tem duas opções: pode enumerar todos de uma vez ou pode examinar o conjunto e enumerar tudo.

**Enumerar todo o conjunto**

Se você quiser garantir que está enumerando todo o conjunto em uma viagem de ida e volta, poderá fazê-lo usando o método `SetMembersAsync`. Isso usará o comando `SMEMBERS` no Redis. Se o seu conjunto for relativamente compacto (abaixo de 1.000 membros), essa é uma maneira perfeitamente válida de retirar todos os membros do conjunto.

```csharp
Console.WriteLine($"All Users In one shot: {string.Join(", ", await database.SetMembersAsync(allUsersKey))}");
```

```csharp
// output
All Users In one shot: User:3, User:1, User:4, User:6, User:7, User:2, User:5
```

**Enumerar Conjunto em Blocos**

A maneira alternativa de enumerar um conjunto é enumerar com `SetScanAsync`, que criará um Set Enumerator e usará o comando `SSCAN` para varrer todo o conjunto até que o conjunto se esgote.

```csharp
Console.Write("All Users with scan: ");
await foreach (var user in database.SetScanAsync(allUsersKey))
{
    Console.Write($"{user}, ");
}
```

```csharp
// output
All Users with scan: User:1, User:2, User:3, User:6, User:7, User:5, User:4,
```

**Mover elementos entre conjuntos**

ma operação muito normal que você pode precisar executar com conjuntos é mover elementos entre eles. Por exemplo, se User:1 fosse mover offline, você pode usar `SetMoveAsync`para movê-los do conjunto de usuários ativos para o conjunto de usuários offline.

```csharp
Console.WriteLine($"Moving User:1 from active to offline");
var moved = await database.SetMoveAsync(activeUsersKey, offlineUsersKey, "User:1");
Console.WriteLine($"Move successful: {moved}");
```

```csharp
// output
Move successful: True
```

## Conclusão

O Redis Sets é uma implementação de um conjunto matemático com recursos avançados. Eles são desordenados, não permitem duplicação e permitem a combinação de conjuntos para criar novos conjuntos. A combinação de conjuntos é uma funcionalidade que permite a criação de novos conjuntos por meio da união, interseção ou diferença entre conjuntos. Os conjuntos Redis são particularmente úteis para operações que exigem associação O(1), como verificar se um elemento pertence a um conjunto, e também para a manipulação de membros de conjuntos comuns, como adicionar ou remover um membro. O Redis Sets fornece muitas outras operações úteis, como enumerar conjuntos em blocos e mover elementos entre conjuntos. Em resumo, o Redis Sets é uma ferramenta poderosa e flexível para lidar com conjuntos de dados e é amplamente utilizado em muitas aplicações.

## 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 allUsersKey = "users";
var activeUsersKey = "users:state:active";
var inactiveUsersKey = "users:state:inactive";
var offlineUsersKey = "users:state:offline";
await database.KeyDeleteAsync(new RedisKey[] { allUsersKey, activeUsersKey, inactiveUsersKey, offlineUsersKey });

var activeUsers = new string[] { "User:1", "User:2" };
var inactiveUsers = new string[] { "User:3", "User:4" };
var offlineUsers = new string[] { "User:5", "User:6", "User:7" };
await database.SetAddAsync(activeUsersKey, activeUsers.Select(u => (RedisValue)u).ToArray());
await database.SetAddAsync(inactiveUsersKey, inactiveUsers.Select(u => (RedisValue)u).ToArray());
await database.SetAddAsync(offlineUsersKey, offlineUsers.Select(u => (RedisValue)u).ToArray());

await database.SetCombineAndStoreAsync(SetOperation.Union, allUsersKey, new RedisKey[] { activeUsersKey, inactiveUsersKey, offlineUsersKey });

var user6Offline = await database.SetContainsAsync(offlineUsersKey, "User:6");
Console.WriteLine($"User:6 offline: {user6Offline}");

Console.WriteLine($"All Users In one shot: {string.Join(", ", await database.SetMembersAsync(allUsersKey))}");

Console.Write("All Users with scan: ");
await foreach (var user in database.SetScanAsync(allUsersKey))
{
    Console.Write($"{user}, ");
}

Console.WriteLine($"Moving User:1 from active to offline");
var moved = await database.SetMoveAsync(activeUsersKey, offlineUsersKey, "User:1");
Console.WriteLine($"Move successful: {moved}");
```