Упражнение. Реализация службы Azure Cosmos DB для NoSQL

Завершено

Служба Azure Cosmos DB (CosmosDbService) управляет запросами, созданием, удалением и обновлением сеансов и сообщений в приложении помощник ИИ. Для управления всеми этими операциями служба требуется для реализации нескольких методов для каждой потенциальной операции с помощью различных функций пакета SDK для .NET.

В этом упражнении существует несколько ключевых требований.

  • Реализация операций для создания сеанса или сообщения
  • Реализация запросов для получения нескольких сеансов или сообщений
  • Реализация операции обновления одного сеанса или пакетного обновления нескольких сообщений
  • Реализация операции для запроса и удаления нескольких связанных сеансов и сообщений

Создание сеанса или сообщения

Azure Cosmos DB для NoSQL сохраняет данные в формате JSON, что позволяет хранить множество типов данных в одном контейнере. Это приложение хранит как сеанс чата с помощник ИИ, так и отдельными "сообщениями" в каждом сеансе. С помощью API для NoSQL приложение может хранить оба типа данных в одном контейнере, а затем различать эти типы с помощью простого type поля.

  1. Откройте файл Services/CosmosDbService.cs.

  2. В методе удалите любой существующий InsertSessionAsync код заполнителя.

    public async Task<Session> InsertSessionAsync(Session session)
    {
    }
    
  3. Создайте переменную с именем partitionKey типа PartitionKey , используя свойство текущего сеанса SessionId в качестве параметра.

    PartitionKey partitionKey = new(session.SessionId);
    
  4. CreateItemAsync Вызов метода контейнера, передавающего session параметр и partitionKey переменную. Возвращает ответ в результате InsertSessionAsync метода.

    return await _container.CreateItemAsync<Session>(
        item: session,
        partitionKey: partitionKey
    ); 
    
  5. В методе удалите любой существующий InsertMessageAsync код заполнителя.

    public async Task<Message> InsertMessageAsync(Message message)
    {
    }
    
  6. Создайте переменную, используемую PartitionKeysession.SessionId в качестве значения ключа секции.

    PartitionKey partitionKey = new(message.SessionId);
    
  7. Создайте новую переменную сообщения с именем newMessage свойства Timestamp , обновленного до текущей метки времени в формате UTC.

    Message newMessage = message with { TimeStamp = DateTime.UtcNow };
    
  8. Вызывает передачу CreateItemAsync как нового сообщения, так и переменных ключа секции. Возвращает ответ в результате InsertMessageAsync.

    return await _container.CreateItemAsync<Message>(
        item: newMessage,
        partitionKey: partitionKey
    );
    
  9. Сохраните файл Services/CosmosDbService.cs .

Получение нескольких сеансов или сообщений

Существует два основных варианта использования, когда приложению необходимо извлечь несколько элементов из нашего контейнера. Во-первых, приложение извлекает все сеансы для текущего пользователя, отфильтровав элементы в том месте type = Session. Во-вторых, приложение извлекает все сообщения для сеанса, выполняя аналогичный фильтр, где type = Session & sessionId = <current-session-id>. Реализуйте оба запроса здесь с помощью пакета SDK для .NET и итератора веб-канала.

  1. В методе удалите любой существующий GetSessionsAsync код заполнителя.

    public async Task<List<Session>> GetSessionsAsync()
    {
    }
    
  2. Создайте новую переменную с именем query типа QueryDefinition с помощью SQL-запроса SELECT DISTINCT * FROM c WHERE c.type = @type. Используйте метод fluent WithParameter , чтобы назначить имя Session класса в качестве значения параметра.

    QueryDefinition query = new QueryDefinition("SELECT DISTINCT * FROM c WHERE c.type = @type")
        .WithParameter("@type", nameof(Session));
    
  3. Вызов универсального GetItemQueryIterator<> метода _container переменной, передаваемой универсальным типом Session и query переменной в качестве параметра. Сохраните результат в переменной типа FeedIterator<Session> с именем response.

    FeedIterator<Session> response = _container.GetItemQueryIterator<Session>(query);
    
  4. Создайте новую переменную универсального списка с именем output.

    List<Session> output = new();
    
  5. Создайте цикл времени, который выполняется до тех пор, пока response.HasMoreResults не будет выполнено значение true.

    while (response.HasMoreResults)
    {
    }
    

    Примечание.

    Использование цикла в то время здесь будет эффективно прокрутить все страницы ответа, пока не осталось страниц.

  6. В цикле в то время асинхронно получите следующую страницу результатов путем вызова ReadNextAsyncresponse переменной, а затем добавьте эти результаты в переменную списка с именем output.

    FeedResponse<Session> results = await response.ReadNextAsync();
    output.AddRange(results);
    
  7. За пределами цикла в то время возвращайте output переменную со списком сеансов в результате GetSessionsAsync метода.

    return output;
    
  8. В методе удалите любой существующий GetSessionMessagesAsync код заполнителя.

    public async Task<List<Message>> GetSessionMessagesAsync(string sessionId)
    {
    }
    
  9. Создайте переменную query типа QueryDefinition. Используйте SQL-запрос SELECT * FROM c WHERE c.sessionId = @sessionId AND c.type = @type. Используйте метод fluent WithParameter , чтобы назначить @sessionId параметр идентификатору сеанса, переданного в качестве параметра, и @type параметру имя Message класса.

    QueryDefinition query = new QueryDefinition("SELECT * FROM c WHERE c.sessionId = @sessionId AND c.type = @type")
        .WithParameter("@sessionId", sessionId)
        .WithParameter("@type", nameof(Message));
    
  10. FeedIterator<Message>query Создайте переменную и GetItemQueryIterator<> метод.

    FeedIterator<Message> response = _container.GetItemQueryIterator<Message>(query);
    
  11. Используйте цикл времени для итерации всех страниц результатов и хранения результатов в одной List<Message> переменной с именемoutput.

    List<Message> output = new();
    while (response.HasMoreResults)
    {
        FeedResponse<Message> results = await response.ReadNextAsync();
        output.AddRange(results);
    }
    
  12. output Возвращает переменную в результате GetSessionMessagesAsync метода.

    return output;
    
  13. Сохраните файл Services/CosmosDbService.cs .

Обновление одного или нескольких сеансов или сообщений

Существуют сценарии, когда для одного сеанса требуется обновление или несколько сообщений, для которых требуется обновление. В первом сценарии используйте ReplaceItemAsync метод пакета SDK для замены существующего элемента измененной версией. Во втором сценарии используйте пакетную функцию пакета транзакций пакета SDK для изменения нескольких сообщений в одном пакете.

  1. В методе удалите любой существующий UpdateSessionAsync код заполнителя.

    public async Task<Session> UpdateSessionAsync(Session session)
    {
    }
    
  2. Создайте переменную, используемую PartitionKeysession.SessionId в качестве значения ключа секции.

    PartitionKey partitionKey = new(session.SessionId);
    
  3. ReplaceItemAsync Вызовите передачу уникального идентификатора и ключа секции нового сообщения. Возвращает ответ в результате UpdateSessionAsync.

    return await _container.ReplaceItemAsync(
        item: session,
        id: session.Id,
        partitionKey: partitionKey
    );
    
  4. В методе удалите любой существующий UpsertSessionBatchAsync код заполнителя.

    public async Task UpsertSessionBatchAsync(params dynamic[] messages)
    {
    }
    
  5. Убедитесь, что все сообщения содержат один идентификатор сеанса (SessionId) с помощью языкового интегрированного запроса (LINQ). Если любое из сообщений содержит другое значение, вызовите ArgumentExceptionисключение.

    if (messages.Select(m => m.SessionId).Distinct().Count() > 1)
    {
        throw new ArgumentException("All items must have the same partition key.");
    }
    
  6. Создайте новую PartitionKey переменную с помощью SessionId свойства первого сообщения.

    PartitionKey partitionKey = new(messages.First().SessionId);
    

    Примечание.

    Помните, что все сообщения имеют один и тот же идентификатор сеанса, если приложение перемещено на эту точку в коде метода.

  7. Создайте новую переменную с именем batch типа, вызвав CreateTransactionalBatch метод переменной _containerTransactionalBatch. Используйте текущую переменную ключа секции для пакетных операций.

    TransactionalBatch batch = _container.CreateTransactionalBatch(partitionKey);
    

    Внимание

    Помните, что все транзакции в этом пакете должны находиться в одной логической секции.

  8. Выполните итерацию по каждому сообщению в массиве messagesс помощью цикла foreach.

    foreach (var message in messages)
    {
    }
    
  9. В цикле foreach добавьте каждое сообщение в качестве операции upsert в пакет.

    batch.UpsertItem(
        item: message
    );
    

    Примечание.

    Upsert сообщает Azure Cosmos DB определить, следует ли заменить или обновить элемент на стороне сервера. Azure Cosmos DB сделает это определение ключом и ключом id секции каждого элемента.

  10. Вне цикла foreach асинхронно вызывает ExecuteAsync метод пакета для выполнения всех операций в пакете.

    await batch.ExecuteAsync();
    
  11. Сохраните файл Services/CosmosDbService.cs .

Наконец, объедините функциональные возможности запроса и пакетной службы транзакций, чтобы удалить несколько элементов. В этом примере получите элемент сеанса и все связанные сообщения, запрашивая все элементы с определенным идентификатором сеанса независимо от типа. Затем создайте пакет транзакций для удаления всех сопоставленных элементов в виде одной транзакции.

  1. В методе удалите любой существующий DeleteSessionAndMessagesAsync код заполнителя.

    public async Task DeleteSessionAndMessagesAsync(string sessionId)
    {
    }
    
  2. Создайте переменную с именем partitionKey типа PartitionKey , используя sesionId строковое значение, переданное в качестве параметра в этот метод.

    PartitionKey partitionKey = new(sessionId);
    
  3. Используя один и тот же sessionId параметр метода, создайте QueryDefinition объект, который находит все элементы, соответствующие идентификатору сеанса. Используйте параметр запроса для sessionId и убедитесь, что запрос не фильтруем по типу элемента.

    QueryDefinition query = new QueryDefinition("SELECT VALUE c.id FROM c WHERE c.sessionId = @sessionId")
        .WithParameter("@sessionId", sessionId);
    

    Примечание.

    При применении type фильтра в этом запросе может непреднамеренно пропустить связанные сообщения или сеансы, которые должны быть удалены в рамках этой операции.

  4. Создайте новое FeedIterator<string> использование GetItemQueryIterator и созданный запрос.

    FeedIterator<string> response = _container.GetItemQueryIterator<string>(query);
    
  5. Создайте именованную TransactionalBatchbatch переменную с помощью CreateTransactionalBatch и переменную ключа секции.

    TransactionalBatch batch = _container.CreateTransactionalBatch(partitionKey);
    
  6. Создайте цикл времени для итерации всех страниц результатов. В цикле "Время" получите следующую страницу результатов и используйте цикл foreach для итерации всех идентификаторов элементов на страницу. В цикле foreach добавьте пакетную операцию для удаления элемента с помощью batch.DeleteItem.

    while (response.HasMoreResults)
    {
        FeedResponse<string> results = await response.ReadNextAsync();
        foreach (var itemId in results)
        {
            batch.DeleteItem(
                id: itemId
            );
        }
    }
    
  7. После цикла в то время выполните пакет с помощью batch.ExecuteAsync.

    await batch.ExecuteAsync();
    
  8. Сохраните файл Services/CosmosDbService.cs .

Проверьте свою работу

Теперь приложение имеет полную реализацию Azure OpenAI и Azure Cosmos DB. Вы можете протестировать комплексное приложение, отладив решение.

  1. Откройте новый терминал.

  2. Запустите приложение с включенной горячей перезагрузкой.dotnet watch

    dotnet watch run --non-interactive
    
  3. Visual Studio Code снова запускает простой браузер с веб-приложением, запущенным. В веб-приложении создайте новый сеанс чата и попросите ИИ помощник вопрос. Затем закройте работающее веб-приложение.

  4. Закройте терминал. Теперь откройте новый терминал.

  5. Запустите приложение еще раз с включенной горячей перезагрузкой.dotnet watch

    dotnet watch run --non-interactive
    
  6. Visual Studio Code снова запускает простой браузер с помощью веб-приложения, запущенного в приложении. Для этого итерации обратите внимание, что сеансы чата сохраняются между сеансами отладки.

    Screenshot of the application with both Azure Cosmos DB and Azure OpenAI services implemented.

  7. Закройте терминал.