Упражнение. Реализация службы Azure Cosmos DB для NoSQL
Служба Azure Cosmos DB (CosmosDbService
) управляет запросами, созданием, удалением и обновлением сеансов и сообщений в приложении помощник ИИ. Для управления всеми этими операциями служба требуется для реализации нескольких методов для каждой потенциальной операции с помощью различных функций пакета SDK для .NET.
В этом упражнении существует несколько ключевых требований.
- Реализация операций для создания сеанса или сообщения
- Реализация запросов для получения нескольких сеансов или сообщений
- Реализация операции обновления одного сеанса или пакетного обновления нескольких сообщений
- Реализация операции для запроса и удаления нескольких связанных сеансов и сообщений
Создание сеанса или сообщения
Azure Cosmos DB для NoSQL сохраняет данные в формате JSON, что позволяет хранить множество типов данных в одном контейнере. Это приложение хранит как сеанс чата с помощник ИИ, так и отдельными "сообщениями" в каждом сеансе. С помощью API для NoSQL приложение может хранить оба типа данных в одном контейнере, а затем различать эти типы с помощью простого type
поля.
Откройте файл Services/CosmosDbService.cs.
В методе удалите любой существующий
InsertSessionAsync
код заполнителя.public async Task<Session> InsertSessionAsync(Session session) { }
Создайте переменную с именем
partitionKey
типаPartitionKey
, используя свойство текущего сеансаSessionId
в качестве параметра.PartitionKey partitionKey = new(session.SessionId);
CreateItemAsync
Вызов метода контейнера, передавающегоsession
параметр иpartitionKey
переменную. Возвращает ответ в результатеInsertSessionAsync
метода.return await _container.CreateItemAsync<Session>( item: session, partitionKey: partitionKey );
В методе удалите любой существующий
InsertMessageAsync
код заполнителя.public async Task<Message> InsertMessageAsync(Message message) { }
Создайте переменную, используемую
PartitionKey
session.SessionId
в качестве значения ключа секции.PartitionKey partitionKey = new(message.SessionId);
Создайте новую переменную сообщения с именем
newMessage
свойстваTimestamp
, обновленного до текущей метки времени в формате UTC.Message newMessage = message with { TimeStamp = DateTime.UtcNow };
Вызывает передачу
CreateItemAsync
как нового сообщения, так и переменных ключа секции. Возвращает ответ в результатеInsertMessageAsync
.return await _container.CreateItemAsync<Message>( item: newMessage, partitionKey: partitionKey );
Сохраните файл Services/CosmosDbService.cs .
Получение нескольких сеансов или сообщений
Существует два основных варианта использования, когда приложению необходимо извлечь несколько элементов из нашего контейнера. Во-первых, приложение извлекает все сеансы для текущего пользователя, отфильтровав элементы в том месте type = Session
. Во-вторых, приложение извлекает все сообщения для сеанса, выполняя аналогичный фильтр, где type = Session & sessionId = <current-session-id>
. Реализуйте оба запроса здесь с помощью пакета SDK для .NET и итератора веб-канала.
В методе удалите любой существующий
GetSessionsAsync
код заполнителя.public async Task<List<Session>> GetSessionsAsync() { }
Создайте новую переменную с именем
query
типаQueryDefinition
с помощью SQL-запросаSELECT DISTINCT * FROM c WHERE c.type = @type
. Используйте метод fluentWithParameter
, чтобы назначить имяSession
класса в качестве значения параметра.QueryDefinition query = new QueryDefinition("SELECT DISTINCT * FROM c WHERE c.type = @type") .WithParameter("@type", nameof(Session));
Вызов универсального
GetItemQueryIterator<>
метода_container
переменной, передаваемой универсальным типомSession
иquery
переменной в качестве параметра. Сохраните результат в переменной типаFeedIterator<Session>
с именемresponse
.FeedIterator<Session> response = _container.GetItemQueryIterator<Session>(query);
Создайте новую переменную универсального списка с именем
output
.List<Session> output = new();
Создайте цикл времени, который выполняется до тех пор, пока
response.HasMoreResults
не будет выполнено значение true.while (response.HasMoreResults) { }
Примечание.
Использование цикла в то время здесь будет эффективно прокрутить все страницы ответа, пока не осталось страниц.
В цикле в то время асинхронно получите следующую страницу результатов путем вызова
ReadNextAsync
response
переменной, а затем добавьте эти результаты в переменную списка с именемoutput
.FeedResponse<Session> results = await response.ReadNextAsync(); output.AddRange(results);
За пределами цикла в то время возвращайте
output
переменную со списком сеансов в результатеGetSessionsAsync
метода.return output;
В методе удалите любой существующий
GetSessionMessagesAsync
код заполнителя.public async Task<List<Message>> GetSessionMessagesAsync(string sessionId) { }
Создайте переменную
query
типаQueryDefinition
. Используйте SQL-запросSELECT * FROM c WHERE c.sessionId = @sessionId AND c.type = @type
. Используйте метод fluentWithParameter
, чтобы назначить@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));
FeedIterator<Message>
query
Создайте переменную иGetItemQueryIterator<>
метод.FeedIterator<Message> response = _container.GetItemQueryIterator<Message>(query);
Используйте цикл времени для итерации всех страниц результатов и хранения результатов в одной
List<Message>
переменной с именемoutput
.List<Message> output = new(); while (response.HasMoreResults) { FeedResponse<Message> results = await response.ReadNextAsync(); output.AddRange(results); }
output
Возвращает переменную в результатеGetSessionMessagesAsync
метода.return output;
Сохраните файл Services/CosmosDbService.cs .
Обновление одного или нескольких сеансов или сообщений
Существуют сценарии, когда для одного сеанса требуется обновление или несколько сообщений, для которых требуется обновление. В первом сценарии используйте ReplaceItemAsync
метод пакета SDK для замены существующего элемента измененной версией. Во втором сценарии используйте пакетную функцию пакета транзакций пакета SDK для изменения нескольких сообщений в одном пакете.
В методе удалите любой существующий
UpdateSessionAsync
код заполнителя.public async Task<Session> UpdateSessionAsync(Session session) { }
Создайте переменную, используемую
PartitionKey
session.SessionId
в качестве значения ключа секции.PartitionKey partitionKey = new(session.SessionId);
ReplaceItemAsync
Вызовите передачу уникального идентификатора и ключа секции нового сообщения. Возвращает ответ в результатеUpdateSessionAsync
.return await _container.ReplaceItemAsync( item: session, id: session.Id, partitionKey: partitionKey );
В методе удалите любой существующий
UpsertSessionBatchAsync
код заполнителя.public async Task UpsertSessionBatchAsync(params dynamic[] messages) { }
Убедитесь, что все сообщения содержат один идентификатор сеанса (
SessionId
) с помощью языкового интегрированного запроса (LINQ). Если любое из сообщений содержит другое значение, вызовитеArgumentException
исключение.if (messages.Select(m => m.SessionId).Distinct().Count() > 1) { throw new ArgumentException("All items must have the same partition key."); }
Создайте новую
PartitionKey
переменную с помощьюSessionId
свойства первого сообщения.PartitionKey partitionKey = new(messages.First().SessionId);
Примечание.
Помните, что все сообщения имеют один и тот же идентификатор сеанса, если приложение перемещено на эту точку в коде метода.
Создайте новую переменную с именем
batch
типа, вызвавCreateTransactionalBatch
метод переменной_container
TransactionalBatch
. Используйте текущую переменную ключа секции для пакетных операций.TransactionalBatch batch = _container.CreateTransactionalBatch(partitionKey);
Внимание
Помните, что все транзакции в этом пакете должны находиться в одной логической секции.
Выполните итерацию по каждому сообщению в массиве
messages
с помощью цикла foreach.foreach (var message in messages) { }
В цикле foreach добавьте каждое сообщение в качестве операции upsert в пакет.
batch.UpsertItem( item: message );
Примечание.
Upsert сообщает Azure Cosmos DB определить, следует ли заменить или обновить элемент на стороне сервера. Azure Cosmos DB сделает это определение ключом и ключом
id
секции каждого элемента.Вне цикла foreach асинхронно вызывает
ExecuteAsync
метод пакета для выполнения всех операций в пакете.await batch.ExecuteAsync();
Сохраните файл Services/CosmosDbService.cs .
Удаление сеанса и всех связанных сообщений
Наконец, объедините функциональные возможности запроса и пакетной службы транзакций, чтобы удалить несколько элементов. В этом примере получите элемент сеанса и все связанные сообщения, запрашивая все элементы с определенным идентификатором сеанса независимо от типа. Затем создайте пакет транзакций для удаления всех сопоставленных элементов в виде одной транзакции.
В методе удалите любой существующий
DeleteSessionAndMessagesAsync
код заполнителя.public async Task DeleteSessionAndMessagesAsync(string sessionId) { }
Создайте переменную с именем
partitionKey
типаPartitionKey
, используяsesionId
строковое значение, переданное в качестве параметра в этот метод.PartitionKey partitionKey = new(sessionId);
Используя один и тот же
sessionId
параметр метода, создайтеQueryDefinition
объект, который находит все элементы, соответствующие идентификатору сеанса. Используйте параметр запроса дляsessionId
и убедитесь, что запрос не фильтруем по типу элемента.QueryDefinition query = new QueryDefinition("SELECT VALUE c.id FROM c WHERE c.sessionId = @sessionId") .WithParameter("@sessionId", sessionId);
Примечание.
При применении
type
фильтра в этом запросе может непреднамеренно пропустить связанные сообщения или сеансы, которые должны быть удалены в рамках этой операции.Создайте новое
FeedIterator<string>
использованиеGetItemQueryIterator
и созданный запрос.FeedIterator<string> response = _container.GetItemQueryIterator<string>(query);
Создайте именованную
TransactionalBatch
batch
переменную с помощьюCreateTransactionalBatch
и переменную ключа секции.TransactionalBatch batch = _container.CreateTransactionalBatch(partitionKey);
Создайте цикл времени для итерации всех страниц результатов. В цикле "Время" получите следующую страницу результатов и используйте цикл foreach для итерации всех идентификаторов элементов на страницу. В цикле foreach добавьте пакетную операцию для удаления элемента с помощью
batch.DeleteItem
.while (response.HasMoreResults) { FeedResponse<string> results = await response.ReadNextAsync(); foreach (var itemId in results) { batch.DeleteItem( id: itemId ); } }
После цикла в то время выполните пакет с помощью
batch.ExecuteAsync
.await batch.ExecuteAsync();
Сохраните файл Services/CosmosDbService.cs .
Проверьте свою работу
Теперь приложение имеет полную реализацию Azure OpenAI и Azure Cosmos DB. Вы можете протестировать комплексное приложение, отладив решение.
Откройте новый терминал.
Запустите приложение с включенной горячей перезагрузкой.
dotnet watch
dotnet watch run --non-interactive
Visual Studio Code снова запускает простой браузер с веб-приложением, запущенным. В веб-приложении создайте новый сеанс чата и попросите ИИ помощник вопрос. Затем закройте работающее веб-приложение.
Закройте терминал. Теперь откройте новый терминал.
Запустите приложение еще раз с включенной горячей перезагрузкой.
dotnet watch
dotnet watch run --non-interactive
Visual Studio Code снова запускает простой браузер с помощью веб-приложения, запущенного в приложении. Для этого итерации обратите внимание, что сеансы чата сохраняются между сеансами отладки.
Закройте терминал.