diff --git a/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj b/src/Plugins/BotSharp.Plugin.SqlDriver/BotSharp.Plugin.SqlDriver.csproj index bda50ed0..42012bb6 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 3e693f80..0f898200 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 class SqlSelect : IFunctionCallback "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 class SqlSelect : IFunctionCallback } else { + if (dbType == "mongodb") message.StopCompletion = true; message.Content = JsonSerializer.Serialize(result); args.Return.Value = message.Content; } @@ -85,4 +90,79 @@ public class SqlSelect : IFunctionCallback } 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 67acab47..3d2f295b 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 9f902a01..07ffb31f 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 f2aa4c36..9d87fa74 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 7cc97bb0..3c896374 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;