xf 1 سال پیش
والد
کامیت
9ca4b8cde5

+ 11 - 3
src/Hotline.Api/Controllers/TestController.cs

@@ -58,6 +58,7 @@ public class TestController : BaseController
     private readonly IWexClient _wexClient;
     private readonly ISugarUnitOfWork<WexDbContext> _uowWex;
     private readonly IOrderUrgeRepository _orderUrgeRepository;
+    private readonly IRepositoryTextSearch<OrderTs> _repositoryts;
     private readonly IGroupManager _goupManager;
 
     //private readonly ITypedCache<List<User>> _cache;
@@ -84,8 +85,8 @@ public class TestController : BaseController
         IDistributedLock distributedLock,
         IWexClient wexClient,
         ISugarUnitOfWork<WexDbContext> uowWex,
-        IOrderUrgeRepository orderUrgeRepository
-        )
+        IOrderUrgeRepository orderUrgeRepository,
+        IRepositoryTextSearch<OrderTs> repositoryts)
     {
         _logger = logger;
         _authorizeGenerator = authorizeGenerator;
@@ -104,12 +105,19 @@ public class TestController : BaseController
         _wexClient = wexClient;
         _uowWex = uowWex;
         _orderUrgeRepository = orderUrgeRepository;
+        _repositoryts = repositoryts;
     }
 
     [HttpGet("time")]
     public async Task<OpenResponse> GetTime()
     {
-        
+        //await _repositoryts.AddVectorAsync("f595e730-909a-45e4-9138-a84bf15f4662", DateTime.Now,
+        //    new List<string> { "xx", "bb" }, HttpContext.RequestAborted);
+
+        //var result = await _repositoryts.SearchAsync(new List<string> { "bb" }, HttpContext.RequestAborted);
+
+        //await _repositoryts.UpdateVectorAsync("f595e730-909a-45e4-9138-a84bf15f4662",
+        //    new List<string> { "ss", "bb" }, HttpContext.RequestAborted);
 
         //_uow.Db.Ado.SqlQueryAsync<OrderUrge>("SELECT to_tsvector('fat cats ate fat rats') @@ to_tsquery('fat & rat')")
 

+ 28 - 28
src/Hotline.Repository.SqlSugar/Ts/BaseRepositoryTextSearch.cs

@@ -7,7 +7,7 @@ using XF.Domain.Repository;
 namespace Hotline.Repository.SqlSugar.Ts;
 
 public class BaseRepositoryTextSearch<TEntity> : IRepositoryTextSearch<TEntity>
-    where TEntity : class, IEntity<string>, ITextSearch, new()
+    where TEntity : class, ITextSearch, new()
 {
     private readonly ISugarUnitOfWork<HotlineDbContext> _uow;
     private readonly ISqlSugarClient _db;
@@ -26,32 +26,28 @@ public class BaseRepositoryTextSearch<TEntity> : IRepositoryTextSearch<TEntity>
     /// <param name="texts"></param>
     /// <param name="cancellationToken"></param>
     /// <returns></returns>
-    public Task AddVectorAsync(string id, DateTime creationTime, ICollection<string> texts, CancellationToken cancellationToken)
+    public async Task AddVectorAsync(string id, DateTime creationTime, ICollection<string> texts, CancellationToken cancellationToken)
     {
         var weights = texts.Select(d => new NpgsqlWeight
         {
             Text = d
         }).ToList();
-        return AddVectorAsync(id, creationTime, weights, cancellationToken);
-    }
 
-    /// <summary>
-    /// 自定义权重
-    /// </summary>
-    /// <param name="id">原始数据Id</param>
-    /// <param name="creationTime">原始数据CreationTime</param>
-    /// <param name="cancellationToken"></param>
-    /// <returns></returns>
-    public async Task AddVectorAsync(string id, DateTime creationTime, ICollection<NpgsqlWeight> weights, CancellationToken cancellationToken)
-    {
         var tableName = typeof(TEntity).Name.ToSnakeCase();
         var vectorSql = CreateVectorSql(weights);
-        var sql = $"INSERT INTO @tableName VALUES ('@id', '@creationTime', @vector)";
-        await _db.Ado.ExecuteCommandAsync(sql, new { tableName, id, creationTime, vector = vectorSql }, cancellationToken);
+        var vectorTexts = JsonSerializer.Serialize(texts);
+        var sql = $"INSERT INTO {tableName} (\"Id\",\"Vector\",\"VectorTexts\",\"CreationTime\") VALUES ('{id}', {vectorSql}, '{vectorTexts}'::json, '{creationTime}')";
+        await _db.Ado.ExecuteCommandAsync(sql, null, cancellationToken);
         /*
-         INSERT INTO "order_ts" VALUES ('f595e730-909a-45e4-9138-a84bf15f4660', '2023-09-06 10:51:38', setweight(to_tsvector('simple', 'aa'), 'C') || setweight(to_tsvector('simple', 'bb'), 'C'))
-         */
-
+        INSERT INTO "order_ts"
+("Id","Vector","VectorTexts","CreationTime")
+VALUES ('f595e730-909a-45e4-9138-a84bf15f4661', 
+setweight(to_tsvector('simple', 'cc'), 'C') ||
+setweight(to_tsvector('simple', 'bb'), 'C'),
+'["cc","bb"]'
+, '2023-09-06 10:51:39'
+)
+        */
     }
 
     /// <summary>
@@ -66,26 +62,30 @@ public class BaseRepositoryTextSearch<TEntity> : IRepositoryTextSearch<TEntity>
 
         var tableName = typeof(TEntity).Name.ToSnakeCase();
         var vectorSql = CreateVectorSql(weights);
-
-        var sql = $"UPDATE @tableName SET \"Vector\"=@vector, \"VectorTexts\"=@vectorTexts WHERE id=@id ";
         var vectorTexts = JsonSerializer.Serialize(texts);
-        await _db.Ado.ExecuteCommandAsync(sql, new { tableName, id, vector = vectorSql, vectorTexts }, cancellationToken);
+        var sql = $"UPDATE {tableName} SET \"Vector\"={vectorSql}, \"VectorTexts\"='{vectorTexts}'::json WHERE \"Id\"=@id ";
+        await _db.Ado.ExecuteCommandAsync(sql, new { id }, cancellationToken);
     }
 
-    public async Task<List<RankedResult<TEntity>>> SearchAsync(IReadOnlyList<string> texts, CancellationToken cancellationToken)
+    public async Task<List<TEntity>> SearchAsync(IReadOnlyList<string> texts, CancellationToken cancellationToken)
     {
         var tableName = typeof(TEntity).Name.ToSnakeCase();
-        var tsquery = string.Join(' ', texts);
-        var sql = "SELECT Id, CreationTime, Vector, Texts, ts_rank(\"Vector\", query) AS score FROM @tableName to_tsquery('simple', @tsquery) query WHERE \"Vector\" @@ query ORDER BY score DESC)";
-        return await _db.Ado.SqlQueryAsync<RankedResult<TEntity>>(sql, new { tableName, tsquery }, cancellationToken);
-    }
-
+        var tsquery = string.Join('|', texts);
+        var sql =
+            $"SELECT \"Id\", \"Vector\", \"VectorTexts\", \"CreationTime\", ts_rank(\"Vector\", query) AS score FROM {tableName}, to_tsquery('simple', '{tsquery}') query WHERE \"Vector\" @@ query ORDER BY score DESC";
+        return await _db.Ado.SqlQueryAsync<TEntity>(sql, null, cancellationToken);
 
+        /*
+         SELECT "Id", "Vector", "VectorTexts", "CreationTime", ts_rank("Vector", query) AS score 
+FROM order_ts, to_tsquery('simple', 'bb') query WHERE "Vector" @@ query ORDER BY score DESC
+         */
+    }
+    
     #region private method
 
     private static string CreateVectorSql(ICollection<NpgsqlWeight> weights)
     {
-        var setweghtStrings = weights.Select(d => $"setweight(to_tsvector('simple', {d.Text}), '{d.Weight}')");
+        var setweghtStrings = weights.Select(d => $"setweight(to_tsvector('simple', '{d.Text}'), '{d.Weight}')");
         var vectorString = string.Join(" || ", setweghtStrings);
         return vectorString;
     }

+ 9 - 14
src/Hotline.Repository.SqlSugar/Ts/IRepositoryTextSearch.cs

@@ -4,7 +4,7 @@ using XF.Domain.Repository;
 namespace Hotline.Repository.SqlSugar.Ts;
 
 public interface IRepositoryTextSearch<TEntity>
-    where TEntity : class, IEntity<string>, ITextSearch, new()
+    where TEntity : class, ITextSearch, new()
 {
     /// <summary>
     /// 全都采用默认权重
@@ -16,28 +16,23 @@ public interface IRepositoryTextSearch<TEntity>
     /// <returns></returns>
     Task AddVectorAsync(string id, DateTime creationTime, ICollection<string> texts, CancellationToken cancellationToken);
 
-    /// <summary>
-    /// 自定义权重
-    /// </summary>
-    /// <param name="id">原始数据Id</param>
-    /// <param name="creationTime">原始数据CreationTime</param>
-    /// <param name="cancellationToken"></param>
-    /// <returns></returns>
-    Task AddVectorAsync(string id, DateTime creationTime, ICollection<NpgsqlWeight> weights, CancellationToken cancellationToken);
-
     /// <summary>
     /// 全都采用默认权重
     /// </summary>
     Task UpdateVectorAsync(string id, ICollection<string> texts, CancellationToken cancellationToken);
 
-    Task<List<RankedResult<TEntity>>> SearchAsync(IReadOnlyList<string> texts, CancellationToken cancellationToken);
+    /// <summary>
+    /// 返回值默认按权重排序(返回值继承:RankedResult可获得排序值:score)
+    /// </summary>
+    /// <param name="texts"></param>
+    /// <param name="cancellationToken"></param>
+    /// <returns></returns>
+    Task<List<TEntity>> SearchAsync(IReadOnlyList<string> texts, CancellationToken cancellationToken);
 }
 
-public class RankedResult<TEntity>
+public class RankedResult
 {
     public double Score { get; set; }
-
-    public TEntity Entity { get; set; }
 }
 
 public class NpgsqlWeight

+ 3 - 3
src/XF.Domain/Dependency/DependencyInjectionExtensions.cs

@@ -90,17 +90,17 @@ internal class ServiceRegister
         if (HasImplInterface(type, typeof(ISingletonDependency)))
         {
             services.AddSingleton(serviceType, type);
-            Console.WriteLine($"lifeTime: singleton, {serviceType.FullName} => {type.FullName}");
+            //Console.WriteLine($"lifeTime: singleton, {serviceType.FullName} => {type.FullName}");
         }
         else if (HasImplInterface(type, typeof(IScopeDependency)))
         {
             services.AddScoped(serviceType, type);
-            Console.WriteLine($"lifeTime: scope, {serviceType.FullName} => {type.FullName}");
+            //Console.WriteLine($"lifeTime: scope, {serviceType.FullName} => {type.FullName}");
         }
         else if (HasImplInterface(type, typeof(ITransientDependency)))
         {
             services.AddTransient(serviceType, type);
-            Console.WriteLine($"lifeTime: transient, {serviceType.FullName} => {type.FullName}");
+            //Console.WriteLine($"lifeTime: transient, {serviceType.FullName} => {type.FullName}");
         }
         else
         {