sql select support mongodb
This commit is contained in:
parent
d91b0a3f63
commit
f6e9f4689b
|
|
@ -105,6 +105,7 @@
|
|||
<ItemGroup>
|
||||
<PackageReference Include="MySqlConnector" />
|
||||
<PackageReference Include="Npgsql" />
|
||||
<PackageReference Include="MongoDB.Driver" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -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,73 @@ public class SqlSelect : IFunctionCallback
|
|||
}
|
||||
return connection.Query(args.Statement, dictionary);
|
||||
}
|
||||
|
||||
private IEnumerable<dynamic> RunQueryInMongoDb(SqlStatement args)
|
||||
{
|
||||
var settings = _services.GetRequiredService<SqlDriverSetting>();
|
||||
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<BsonDocument>(match.Groups[2].Value);
|
||||
|
||||
var filter = string.IsNullOrWhiteSpace(queryJson) || queryJson == "{}"
|
||||
? Builders<BsonDocument>.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<BsonDocument, BsonDocument> ApplyChainedOperations(
|
||||
IFindFluent<BsonDocument, BsonDocument> 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>(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);
|
||||
|
||||
return findFluent;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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."
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Reference in a new issue