From e2ab68c154c8de5881644226b0aff484d83ee98b Mon Sep 17 00:00:00 2001 From: Gil Zhang Date: Thu, 22 Aug 2024 11:59:40 +0800 Subject: [PATCH 1/3] Add conversation title update and fix query conditions --- .../Controllers/ConversationController.cs | 26 ++++++++++++++++++- .../UpdateConversationTitleModel.cs | 9 +++++++ 2 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationTitleModel.cs diff --git a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs index 89cdc6e96..9b964dffd 100644 --- a/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs +++ b/src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs @@ -52,7 +52,7 @@ public async Task> GetConversations([FromBody] return new PagedItems(); } - filter.UserId = user.Role != UserRole.Admin ? user.Id : null; + filter.UserId = user.Role != UserRole.Admin ? user.Id : filter.UserId; var conversations = await convService.GetConversations(filter); var agentService = _services.GetRequiredService(); var list = conversations.Items.Select(x => ConversationViewModel.FromSession(x)).ToList(); @@ -154,6 +154,7 @@ public async Task> GetDialogs([FromRoute] string var result = ConversationViewModel.FromSession(conversations.Items.First()); var state = _services.GetRequiredService(); result.States = state.Load(conversationId, isReadOnly: true); + user = await userService.GetUser(result.User.Id); result.User = UserViewModel.FromUser(user); return result; @@ -195,6 +196,29 @@ public async Task GetConversationUser([FromRoute] string conversa return UserViewModel.FromUser(user); } + [HttpPut("/conversation/{conversationId}/update-title")] + public async Task UpdateConversationTitle([FromRoute] string conversationId, [FromBody] UpdateConversationTitleModel newTile) + { + var userService = _services.GetRequiredService(); + var conversationService = _services.GetRequiredService(); + + var user = await userService.GetUser(_user.Id); + var filter = new ConversationFilter + { + Id = conversationId, + UserId = user.Role != UserRole.Admin ? user.Id : null + }; + var conversations = await conversationService.GetConversations(filter); + + if (conversations.Items.IsNullOrEmpty()) + { + return false; + } + + var response = await conversationService.UpdateConversationTitle(conversationId, newTile.NewTitle); + return response != null; + } + [HttpDelete("/conversation/{conversationId}")] public async Task DeleteConversation([FromRoute] string conversationId) { diff --git a/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationTitleModel.cs b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationTitleModel.cs new file mode 100644 index 000000000..3a9ed2bf6 --- /dev/null +++ b/src/Infrastructure/BotSharp.OpenAPI/ViewModels/Conversations/UpdateConversationTitleModel.cs @@ -0,0 +1,9 @@ +using System.ComponentModel.DataAnnotations; + +namespace BotSharp.OpenAPI.ViewModels.Conversations; + +public class UpdateConversationTitleModel +{ + [Required] + public string NewTitle { get; set; } +} From 4825a51f806b560a63061f8274ab3ef98368f742 Mon Sep 17 00:00:00 2001 From: Gil Zhang Date: Thu, 22 Aug 2024 12:00:36 +0800 Subject: [PATCH 2/3] Implement conversation mongodb field sorting --- .../MongoRepository.Conversation.cs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index 88163b60f..59fe4dad3 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -284,6 +284,22 @@ public PagedItems GetConversations(ConversationFilter filter) var filterDef = convBuilder.And(convFilters); var sortDef = Builders.Sort.Descending(x => x.CreatedTime); var pager = filter?.Pager ?? new Pagination(); + + // Apply sorting based on sort and order fields + if (!string.IsNullOrEmpty(pager?.Sort)) + { + var sortField = ConvertSnakeCaseToPascalCase(pager.Sort); + + if (pager.Order == "asc") + { + sortDef = Builders.Sort.Ascending(sortField); + } + else if (pager.Order == "desc") + { + sortDef = Builders.Sort.Descending(sortField); + } + } + var conversationDocs = _dc.Conversations.Find(filterDef).Sort(sortDef).Skip(pager.Offset).Limit(pager.Size).ToList(); var count = _dc.Conversations.CountDocuments(filterDef); @@ -479,4 +495,22 @@ public IEnumerable TruncateConversation(string conversationId, string me return deletedMessageIds; } + + private string ConvertSnakeCaseToPascalCase(string snakeCase) + { + string[] words = snakeCase.Split('_'); + StringBuilder pascalCase = new(); + + foreach (string word in words) + { + if (!string.IsNullOrEmpty(word)) + { + string firstLetter = word[..1].ToUpper(); + string restOfWord = word[1..].ToLower(); + pascalCase.Append(firstLetter + restOfWord); + } + } + + return pascalCase.ToString(); + } } From f2e021ffd9ebfe397edcaf7d7a48078f9cb325b5 Mon Sep 17 00:00:00 2001 From: Gil Zhang Date: Thu, 22 Aug 2024 12:09:53 +0800 Subject: [PATCH 3/3] conversation add title keyword query --- .../Repositories/Filters/ConversationFilter.cs | 1 + .../FileRepository.Conversation.cs | 4 ++++ .../Repository/MongoRepository.Conversation.cs | 18 +++++++++--------- 3 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs index f752633b6..48a19fa60 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Repositories/Filters/ConversationFilter.cs @@ -7,6 +7,7 @@ public class ConversationFilter /// Conversation Id /// public string? Id { get; set; } + public string? Title { get; set; } public string? AgentId { get; set; } public string? Status { get; set; } public string? Channel { get; set; } diff --git a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs index 5ccd3777e..e4bb50980 100644 --- a/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs +++ b/src/Infrastructure/BotSharp.Core/Repository/FileRepository/FileRepository.Conversation.cs @@ -293,6 +293,10 @@ public PagedItems GetConversations(ConversationFilter filter) { matched = matched && record.Id == filter.Id; } + if (filter?.Title != null) + { + matched = matched && record.Title.Contains(filter.Title); + } if (filter?.AgentId != null) { matched = matched && record.AgentId == filter.AgentId; diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs index 59fe4dad3..e3f95f278 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Repository/MongoRepository.Conversation.cs @@ -1,9 +1,5 @@ -using Amazon.Util.Internal; using BotSharp.Abstraction.Conversations.Models; using BotSharp.Abstraction.Repositories.Filters; -using MongoDB.Bson.Serialization; -using MongoDB.Driver; -using System.Collections.Immutable; namespace BotSharp.Plugin.MongoStorage.Repository; @@ -66,7 +62,7 @@ public bool DeleteConversations(IEnumerable conversationIds) var statesDeleted = _dc.ConversationStates.DeleteMany(filterSates); var dialogDeleted = _dc.ConversationDialogs.DeleteMany(filterDialog); var convDeleted = _dc.Conversations.DeleteMany(filterConv); - + return convDeleted.DeletedCount > 0 || dialogDeleted.DeletedCount > 0 || statesDeleted.DeletedCount > 0 || exeLogDeleted.DeletedCount > 0 || promptLogDeleted.DeletedCount > 0 || contentLogDeleted.DeletedCount > 0 || stateLogDeleted.DeletedCount > 0; @@ -237,6 +233,10 @@ public PagedItems GetConversations(ConversationFilter filter) { convFilters.Add(convBuilder.Eq(x => x.Id, filter.Id)); } + if (!string.IsNullOrEmpty(filter?.Title)) + { + convFilters.Add(convBuilder.Regex(x => x.Title, new BsonRegularExpression(filter.Title, "i"))); + } if (!string.IsNullOrEmpty(filter?.AgentId)) { convFilters.Add(convBuilder.Eq(x => x.AgentId, filter.AgentId)); @@ -380,7 +380,7 @@ public List GetIdleConversations(int batchSize, int messageLimit, int bu { break; } - + conversationIds = conversationIds.Concat(candidates).Distinct().ToList(); if (conversationIds.Count >= batchSize) { @@ -419,7 +419,7 @@ public IEnumerable TruncateConversation(string conversationId, string me // Handle truncated dialogs var truncatedDialogs = foundDialog.Dialogs.Where((x, idx) => idx < foundIdx).ToList(); - + // Handle truncated states var refTime = foundDialog.Dialogs.ElementAt(foundIdx).MetaData.CreateTime; var stateFilter = Builders.Filter.Eq(x => x.ConversationId, conversationId); @@ -457,7 +457,7 @@ public IEnumerable TruncateConversation(string conversationId, string me var truncatedBreakpoints = breakpoints.Where(x => x.CreatedTime < refTime).ToList(); foundStates.Breakpoints = truncatedBreakpoints; } - + // Update _dc.ConversationStates.ReplaceOne(stateFilter, foundStates); } @@ -492,7 +492,7 @@ public IEnumerable TruncateConversation(string conversationId, string me _dc.ContentLogs.DeleteMany(contentLogBuilder.And(contentLogFilters)); _dc.StateLogs.DeleteMany(stateLogBuilder.And(stateLogFilters)); } - + return deletedMessageIds; }