From fde5b5734dbe1952843d66ec6aac330595818e92 Mon Sep 17 00:00:00 2001 From: hchen Date: Mon, 1 Dec 2025 10:03:23 -0600 Subject: [PATCH] membase support --- .../Knowledges/ICypherGraphService.cs | 14 ++++++ .../Knowledges/Models/CyperGraphModels.cs | 24 +++++++++ .../BotSharp.Plugin.Membase/MembasePlugin.cs | 9 +--- .../Models/CypherQueryRequest.cs | 17 +++++++ .../Models/CypherQueryResponse.cs | 17 +++++++ .../BotSharp.Plugin.Membase/Models/Node.cs | 2 +- .../Models/NodeCreationModel.cs | 7 +-- .../Models/NodeUpdateModel.cs | 11 +++-- .../Services/IMembaseApi.cs | 18 +++++-- .../Services/MembaseService.cs | 49 ++++++++++++++++++- src/Plugins/BotSharp.Plugin.Membase/Using.cs | 8 +++ 11 files changed, 156 insertions(+), 20 deletions(-) create mode 100644 src/Infrastructure/BotSharp.Abstraction/Knowledges/ICypherGraphService.cs create mode 100644 src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/CyperGraphModels.cs create mode 100644 src/Plugins/BotSharp.Plugin.Membase/Models/CypherQueryRequest.cs create mode 100644 src/Plugins/BotSharp.Plugin.Membase/Models/CypherQueryResponse.cs diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/ICypherGraphService.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/ICypherGraphService.cs new file mode 100644 index 000000000..7cb9bf7ec --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/ICypherGraphService.cs @@ -0,0 +1,14 @@ +namespace BotSharp.Abstraction.Knowledges; + +/// +/// Graph-based semantic knowledge service that supports complex relationships and connections between entities. +/// This service allows for executing Cypher queries to traverse and analyze graph data structures. +/// +public interface ICypherGraphService +{ + Task Execute(string graphId, string query, Dictionary? args = null); + + Task MergeNode(string graphId, GraphNode node); + + Task DeleteNode(string graphId, string nodeId); +} diff --git a/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/CyperGraphModels.cs b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/CyperGraphModels.cs new file mode 100644 index 000000000..3d89488d4 --- /dev/null +++ b/src/Infrastructure/BotSharp.Abstraction/Knowledges/Models/CyperGraphModels.cs @@ -0,0 +1,24 @@ +namespace BotSharp.Abstraction.Knowledges.Models; + +public class GraphQueryResult +{ + public string[] Columns { get; set; } = []; + public Dictionary[] Items { get; set; } = []; +} + +public class GraphNode +{ + public string Id { get; set; } = string.Empty; + + public List Labels { get; set; } = new(); + + public object Properties { get; set; } = new(); + + public DateTime Time { get; set; } = DateTime.UtcNow; + + public override string ToString() + { + var labelsString = Labels.Count > 0 ? string.Join(", ", Labels) : "No Labels"; + return $"Node ({labelsString}: {Id})"; + } +} diff --git a/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs b/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs index ad53d29f7..2368e6283 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/MembasePlugin.cs @@ -1,11 +1,4 @@ -using BotSharp.Abstraction.Plugins; -using BotSharp.Abstraction.Settings; -using BotSharp.Plugin.Membase.Services; -using BotSharp.Plugin.Membase.Settings; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; using Refit; -using System.Threading.Tasks; namespace BotSharp.Plugin.Membase; @@ -29,5 +22,7 @@ public void RegisterDI(IServiceCollection services, IConfiguration config) Task.FromResult($"Bearer {dbSettings.ApiKey}") }) .ConfigureHttpClient(c => c.BaseAddress = new Uri(dbSettings.Host)); + + services.AddScoped(); } } diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/CypherQueryRequest.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/CypherQueryRequest.cs new file mode 100644 index 000000000..2a85f1530 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/CypherQueryRequest.cs @@ -0,0 +1,17 @@ +namespace BotSharp.Plugin.Membase.Models; + +public class CypherQueryRequest +{ + public string Query { get; set; } = string.Empty; + + public Dictionary Parameters { get; set; } = []; + + public bool IncludeExecutionPlan { get; set; } = false; + + /// + /// Whether to profile the query execution. + /// + public bool Profile { get; set; } = false; + + public int? TimeoutMs { get; set; } +} diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/CypherQueryResponse.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/CypherQueryResponse.cs new file mode 100644 index 000000000..724704785 --- /dev/null +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/CypherQueryResponse.cs @@ -0,0 +1,17 @@ +namespace BotSharp.Plugin.Membase.Models; + +public class CypherQueryResponse +{ + public string[] Columns { get; set; } = []; + public Dictionary[] Data { get; set; } = []; + + public CypherNotification[] Notifications { get; set; } = []; + public int RowCount { get; set; } +} + +public class CypherNotification +{ + public string Code { get; set; } = string.Empty; + public string Title { get; set; } = string.Empty; + public string Description { get; set; } = string.Empty; +} diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/Node.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/Node.cs index 27cc76127..f1807b019 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Models/Node.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/Node.cs @@ -13,7 +13,7 @@ public class Node public List Labels { get; set; } = new(); - public Dictionary Properties { get; set; } = new(); + public object Properties { get; set; } = new(); public DateTime Time { get; set; } = DateTime.UtcNow; diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/NodeCreationModel.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/NodeCreationModel.cs index 810f26d55..d32dfd3a6 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Models/NodeCreationModel.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/NodeCreationModel.cs @@ -4,7 +4,8 @@ public class NodeCreationModel { public string? Id { get; set; } public string[]? Labels { get; set; } - public Dictionary? Properties { get; set; } + public object? Properties { get; set; } + public DateTime? Time { get; set; } public Node ToNode() { @@ -12,8 +13,8 @@ public Node ToNode() { Id = Id, Labels = Labels?.ToList() ?? new List(), - Properties = Properties ?? new Dictionary(), - Time = DateTime.UtcNow + Properties = Properties ?? new(), + Time = Time ?? DateTime.UtcNow }; } } diff --git a/src/Plugins/BotSharp.Plugin.Membase/Models/NodeUpdateModel.cs b/src/Plugins/BotSharp.Plugin.Membase/Models/NodeUpdateModel.cs index a89d2220e..1a8746552 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Models/NodeUpdateModel.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Models/NodeUpdateModel.cs @@ -1,19 +1,22 @@ +using System.Text.Json; + namespace BotSharp.Plugin.Membase.Models; public class NodeUpdateModel { public string Id { get; set; } = null!; public string[]? Labels { get; set; } - public Dictionary? Properties { get; set; } + public object? Properties { get; set; } + public DateTime? Time { get; set; } public Node ToNode() { return new Node { Id = Id, - Labels = Labels?.ToList() ?? new List(), - Properties = Properties ?? new Dictionary(), - Time = DateTime.UtcNow + Labels = Labels?.ToList() ?? [], + Properties = Properties ?? new(), + Time = Time ?? DateTime.UtcNow }; } } diff --git a/src/Plugins/BotSharp.Plugin.Membase/Services/IMembaseApi.cs b/src/Plugins/BotSharp.Plugin.Membase/Services/IMembaseApi.cs index e5def687f..5455c3457 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Services/IMembaseApi.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Services/IMembaseApi.cs @@ -4,17 +4,27 @@ namespace BotSharp.Plugin.Membase.Services; +/// +/// Membase REST API interface +/// https://membase.dev/graph-api-reference +/// public interface IMembaseApi { + [Post("/cypher/execute?graphId={graphId}")] + Task CypherQueryAsync(string graphId, CypherQueryRequest request); + [Post("/graph/{graphId}/node")] - Task CreateNode(string graphId, [Body] NodeCreationModel node); + Task CreateNodeAsync(string graphId, [Body] NodeCreationModel node); [Get("/graph/{graphId}/node/{nodeId}")] - Task GetNode(string graphId, string nodeId); + Task GetNodeAsync(string graphId, string nodeId); [Put("/graph/{graphId}/node/{nodeId}")] - Task UpdateNode(string graphId, string nodeId, [Body] NodeUpdateModel node); + Task UpdateNodeAsync(string graphId, string nodeId, [Body] NodeUpdateModel node); + + [Put("/graph/{graphId}/node/{nodeId}/merge")] + Task MergeNodeAsync(string graphId, string nodeId, [Body] NodeUpdateModel node); [Delete("/graph/{graphId}/node/{nodeId}")] - Task DeleteNode(string graphId, string nodeId); + Task DeleteNodeAsync(string graphId, string nodeId); } diff --git a/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseService.cs b/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseService.cs index 42d4f7e09..530ab715c 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseService.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Services/MembaseService.cs @@ -1,5 +1,52 @@ +using BotSharp.Abstraction.Knowledges; +using BotSharp.Abstraction.Knowledges.Models; +using BotSharp.Plugin.Membase.Models; +using System.Threading.Tasks; + namespace BotSharp.Plugin.Membase.Services; -public class MembaseService +public class MembaseService : ICypherGraphService { + private readonly IServiceProvider _services; + private readonly IMembaseApi _membase; + + public MembaseService(IServiceProvider services, IMembaseApi membase) + { + _services = services; + _membase = membase; + } + + public async Task Execute(string graphId, string query, Dictionary? args = null) + { + var response = await _membase.CypherQueryAsync(graphId, new CypherQueryRequest + { + Query = query, + Parameters = args ?? [] + }); + + return new GraphQueryResult + { + Columns = response.Columns, + Items = response.Data + }; + } + + public async Task MergeNode(string graphId, GraphNode node) + { + var newNode = await _membase.MergeNodeAsync(graphId, node.Id, new NodeUpdateModel + { + Id = node.Id, + Labels = [.. node.Labels], + Properties = node.Properties, + Time = node.Time + }); + + return node; + } + + public async Task DeleteNode(string graphId, string nodeId) + { + await _membase.DeleteNodeAsync(graphId, nodeId); + return true; + } } diff --git a/src/Plugins/BotSharp.Plugin.Membase/Using.cs b/src/Plugins/BotSharp.Plugin.Membase/Using.cs index 95b43e02b..6f368942d 100644 --- a/src/Plugins/BotSharp.Plugin.Membase/Using.cs +++ b/src/Plugins/BotSharp.Plugin.Membase/Using.cs @@ -1,8 +1,16 @@ global using System; global using System.Collections.Generic; global using System.Linq; +global using System.Threading.Tasks; global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Mvc; +global using Microsoft.Extensions.Configuration; +global using Microsoft.Extensions.DependencyInjection; global using BotSharp.Abstraction.Users; +global using BotSharp.Abstraction.Knowledges; +global using BotSharp.Abstraction.Plugins; +global using BotSharp.Abstraction.Settings; +global using BotSharp.Plugin.Membase.Services; +global using BotSharp.Plugin.Membase.Settings;