diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj index bda50ed05..42012bb6a 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj @@ -105,6 +105,7 @@ + diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs index 3e693f801..0f898200d 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Functions/SqlSelect.cs @@ -1,6 +1,9 @@ using Microsoft.Data.SqlClient; using MySqlConnector; using Npgsql; +using MongoDB.Driver; +using MongoDB.Bson; +using System.Text.RegularExpressions; using static Dapper.SqlMapper; namespace BotSharp.Plugin.SqlDriver.Functions; @@ -34,6 +37,7 @@ public async Task Execute(RoleDialogModel message) "mysql" => RunQueryInMySql(args), "sqlserver" or "mssql" => RunQueryInSqlServer(args), "redshift" => RunQueryInRedshift(args), + "mongodb" => RunQueryInMongoDb(args), _ => throw new NotImplementedException($"Database type {dbType} is not supported.") }; @@ -43,6 +47,7 @@ public async Task Execute(RoleDialogModel message) } else { + if (dbType == "mongodb") message.StopCompletion = true; message.Content = JsonSerializer.Serialize(result); args.Return.Value = message.Content; } @@ -85,4 +90,79 @@ private IEnumerable RunQueryInRedshift(SqlStatement args) } return connection.Query(args.Statement, dictionary); } + + private IEnumerable RunQueryInMongoDb(SqlStatement args) + { + var settings = _services.GetRequiredService(); + var client = new MongoClient(settings.MongoDbConnectionString); + + // Normalize multi-line query to single line + var statement = Regex.Replace(args.Statement.Trim(), @"\s+", " "); + + // Parse MongoDB query: database.collection.find({query}).projection({}).sort({}).limit(100) + var match = Regex.Match(statement, + @"^([^.]+)\.([^.]+)\.find\s*\((.*?)\)(.*)?$", + RegexOptions.Singleline); + + if (!match.Success) + return ["Invalid MongoDB query format. Expected: database.collection.find({query})"]; + + var queryJson = ApplyParameters(match.Groups[3].Value.Trim(), args.Parameters); + + try + { + var database = client.GetDatabase(match.Groups[1].Value); + var collection = database.GetCollection(match.Groups[2].Value); + + var filter = string.IsNullOrWhiteSpace(queryJson) || queryJson == "{}" + ? Builders.Filter.Empty + : BsonDocument.Parse(queryJson); + + var findFluent = collection.Find(filter); + findFluent = ApplyChainedOperations(findFluent, match.Groups[4].Value); + + return findFluent.ToList().Select(doc => BsonTypeMapper.MapToDotNetValue(doc)); + } + catch (Exception ex) + { + return [$"Invalid MongoDB query: {ex.Message}"]; + } + } + + private string ApplyParameters(string query, Models.SqlParameter[] parameters) + { + foreach (var p in parameters) + query = query.Replace($"@{p.Name}", p.Value?.ToString() ?? "null"); + return query; + } + + private IFindFluent ApplyChainedOperations( + IFindFluent findFluent, string chainedOps) + { + if (string.IsNullOrWhiteSpace(chainedOps)) return findFluent; + + // Apply projection + var projMatch = Regex.Match(chainedOps, @"\.projection\s*\((.*?)\)", RegexOptions.Singleline); + if (projMatch.Success) + findFluent = findFluent.Project(BsonDocument.Parse(projMatch.Groups[1].Value.Trim())); + + // Apply sort + var sortMatch = Regex.Match(chainedOps, @"\.sort\s*\((.*?)\)", RegexOptions.Singleline); + if (sortMatch.Success) + findFluent = findFluent.Sort(BsonDocument.Parse(sortMatch.Groups[1].Value.Trim())); + + // Apply limit + var limitMatch = Regex.Match(chainedOps, @"\.limit\s*\((\d+)\)"); + if (limitMatch.Success && int.TryParse(limitMatch.Groups[1].Value, out var limit)) + { + findFluent = findFluent.Limit(limit); + } + else + { + findFluent = findFluent.Limit(10); + } + + return findFluent; + } + } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs b/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs index 67acab47c..3d2f295b5 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/Settings/SqlDriverSetting.cs @@ -9,6 +9,7 @@ public class SqlDriverSetting public string SqlServerConnectionString { get; set; } = null!; public string SqlServerExecutionConnectionString { get; set; } = null!; public string RedshiftConnectionString { get; set; } = null!; + public string MongoDbConnectionString { get; set; } = null!; public bool ExecuteSqlSelectAutonomous { get; set; } = false; public bool FormattingResult { get; set; } = true; } diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json index 9f902a015..07ffb31f6 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/agent.json @@ -1,7 +1,7 @@ { "id": "beda4c12-e1ec-4b4b-b328-3df4a6687c4f", "name": "SQL Driver", - "description": "Transfer to this Agent when user mentions to execute the sql statement. Only call when executable SQL statements are explicitly provided in the context.", + "description": "Transfer to this Agent when user mentions to execute the sql statement or MongoDB query. Only call when executable SQL statements or MongoDB queries are explicitly provided in the context.", "iconUrl": "https://cdn-icons-png.flaticon.com/512/3161/3161158.png", "type": "task", "createdDateTime": "2023-11-15T13:49:00Z", @@ -18,7 +18,7 @@ "field": "sql_statement", "required": true, "field_type": "string", - "description": "SQL statement explicitly provided in the context." + "description": "SQL statement or MongoDB query explicitly provided in the context." } ] } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/functions/sql_select.json b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/functions/sql_select.json index f2aa4c36f..9d87fa748 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/functions/sql_select.json +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/functions/sql_select.json @@ -6,7 +6,7 @@ "properties": { "sql_statement": { "type": "string", - "description": "SQL statement with SELECT provided in context." + "description": "Provide an SQL query statement or a JSON query for MongoDB." }, "reason": { "type": "string", diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/instructions/instruction.liquid b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/instructions/instruction.liquid index 7cc97bb08..3c8963744 100644 --- a/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/instructions/instruction.liquid +++ b/src/Plugins/BotSharp.Plugin.SqlDriver/data/agents/beda4c12-e1ec-4b4b-b328-3df4a6687c4f/instructions/instruction.liquid @@ -1,6 +1,9 @@ -You're a SQL driver who can execute the sql statement. +You're a database driver who can execute SQL statements and MongoDB queries. Your response must meet below requirements: -* You can only execute the SQL from the conversation. You can't generate one by yourself; +* You can only execute the SQL statement or MongoDB query from the conversation. You can't generate one by yourself; +* **IMPORTANT**: Keep the original query format. Do NOT convert MongoDB queries to SQL or SQL to MongoDB queries; +* For SQL statements: Execute as-is for MySQL, SQL Server, or Redshift; +* For MongoDB queries: Use format `database.collection.find({query})`. Example: `db.users.find({"age": 25})`; * The return field alias should be meaningful, you can use the combination of column and value as the alias name; * Use "Unique Index" to help check record existence;