关于使用PgVector相似度算法查询脚本的问题 返回

SqlSugar 沟通中
11 105

下面是C#的调用代码:

                 SugarDbContext.UsePgVector<SentenceVectorR>(); 

                var jsonVector = $"'{SerializeHelper.SerializeObject(vector, true)}'";

                var sql = @$"select sentence_id, sentence, vector <-> @vector as distance

                    from public.sentence_vector

                    where Abs(vector <-> @vector) < {maxDistance}

                    order by vector <-> @vector

                    limit {pageSize} offset {pageNo * pageSize}";

                var parameters = new SugarParameter[]{

                    new SugarParameter("@vector", jsonVector)

                };

                var sentenceVectors = SugarDbContext.Ado.SqlQuery<SentenceVectorR>(sql, parameters);

                return Task.FromResult(sentenceVectors);


UsePgVector的定义如下:

        private static IDbConnection OpenDbConnection4Vector()

        {

            var dataSourceBuilder = new NpgsqlDataSourceBuilder("数据库连接串");

            dataSourceBuilder.UseVector();

            var dataSource = dataSourceBuilder.Build();


            return dataSource.OpenConnection();

        }


vector为float[]数组,当我直接使用向量的字符串表达(例如,'[0.1,0.2,0.3]')去代替@vector参数时,数据库脚本可以正常执行并返回正确的结果。

但是当我使用@vector参数执行时,报下面的错误:

Npgsql.PostgresException

  HResult=0x80004005

  Message=42883: operator does not exist: vector <-> text


POSITION: 38

  Source=Npgsql

  StackTrace:

   在 Npgsql.Internal.NpgsqlConnector.<<ReadMessage>g__ReadMessageLong|233_0>d.MoveNext() 在 /_/src/Npgsql/Internal/NpgsqlConnector.cs 中: 第 1334 行

   在 System.Threading.Tasks.ValueTask`1.get_Result() 在 /_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs 中: 第 800 行

   在 Npgsql.NpgsqlDataReader.<NextResult>d__47.MoveNext() 在 /_/src/Npgsql/NpgsqlDataReader.cs 中: 第 451 行

   在 Npgsql.NpgsqlDataReader.<NextResult>d__47.MoveNext() 在 /_/src/Npgsql/NpgsqlDataReader.cs 中: 第 609 行

   在 Npgsql.NpgsqlDataReader.NextResult() 在 /_/src/Npgsql/NpgsqlDataReader.cs 中: 第 342 行

   在 Npgsql.NpgsqlCommand.<ExecuteReader>d__124.MoveNext() 在 /_/src/Npgsql/NpgsqlCommand.cs 中: 第 1468 行

   在 Npgsql.NpgsqlCommand.<ExecuteReader>d__124.MoveNext() 在 /_/src/Npgsql/NpgsqlCommand.cs 中: 第 1543 行

   在 System.Threading.Tasks.ValueTask`1.get_Result() 在 /_/src/libraries/System.Private.CoreLib/src/System/Threading/Tasks/ValueTask.cs 中: 第 800 行

   在 Npgsql.NpgsqlCommand.ExecuteReader(CommandBehavior behavior) 在 /_/src/Npgsql/NpgsqlCommand.cs 中: 第 1273 行

   在 Npgsql.NpgsqlCommand.ExecuteDbDataReader(CommandBehavior behavior) 在 /_/src/Npgsql/NpgsqlCommand.cs 中: 第 1253 行

   在 SqlSugar.AdoProvider.GetDataReader(String sql, SugarParameter[] parameters)

   在 SqlSugar.AdoProvider.SqlQuery[T,T2,T3,T4,T5,T6,T7](String sql, Object parameters)

   在 SqlSugar.AdoProvider.SqlQuery[T](String sql, SugarParameter[] parameters)

   在 amaSoGrain.Ai.Grains.AiGrain.SemanticSearchSentence(Single[] vector, Int32 mode, Single maxDistance, Int32 pageNo, Int32 pageSize) 在 D:\hwq\amaSmartTask\amaSoGrain.Ai\Grains\AiGrain.cs 中: 第 81 行


  此异常最初是在此调用堆栈中引发的: 

    Npgsql.Internal.NpgsqlConnector.ReadMessage.__ReadMessageLong|233_0(Npgsql.Internal.NpgsqlConnector, bool, Npgsql.Internal.DataRowLoadingMode, bool, bool) (位于 NpgsqlConnector.cs 中)

    System.Threading.Tasks.ValueTask<TResult>.Result.get() (位于 ValueTask.cs 中)

    Npgsql.NpgsqlDataReader.NextResult(bool, bool, System.Threading.CancellationToken) (位于 NpgsqlDataReader.cs 中)

    Npgsql.NpgsqlDataReader.NextResult(bool, bool, System.Threading.CancellationToken) (位于 NpgsqlDataReader.cs 中)

    Npgsql.NpgsqlDataReader.NextResult() (位于 NpgsqlDataReader.cs 中)

    Npgsql.NpgsqlCommand.ExecuteReader(System.Data.CommandBehavior, bool, System.Threading.CancellationToken) (位于 NpgsqlCommand.cs 中)

    Npgsql.NpgsqlCommand.ExecuteReader(System.Data.CommandBehavior, bool, System.Threading.CancellationToken) (位于 NpgsqlCommand.cs 中)

    System.Threading.Tasks.ValueTask<TResult>.Result.get() (位于 ValueTask.cs 中)

    Npgsql.NpgsqlCommand.ExecuteReader(System.Data.CommandBehavior) (位于 NpgsqlCommand.cs 中)

    Npgsql.NpgsqlCommand.ExecuteDbDataReader(System.Data.CommandBehavior) (位于 NpgsqlCommand.cs 中)

    ...

    [调用堆栈已截断]


这是我使用参数的方式有问题吗?

谢谢!

热忱回答11

  • vector <-> text

    这个很明显类型不一样

    0 回复
  • SugarParameter你没有设置 isarray,我记得要传数组

    0 回复
  • @fate sta:我将参数的IsArray属性设置为true,代码如下:

                    var sql = @$"select sentence_id, sentence, vector <-> @vector as distance

                        from public.sentence_vector

                        where Abs(vector <-> @vector) < {maxDistance}

                        order by vector <-> @vector

                        limit {pageSize} offset {pageNo * pageSize}";

                    var parameters = new SugarParameter[]{

                        new SugarParameter("@vector", vector)

                    };

                    parameters[0].IsArray = true;

                    var sentenceVectors = SugarDbContext.Ado.SqlQuery<SentenceVectorR>(sql, parameters);

    报的错误为以下内容:

    Npgsql.PostgresException:“42883: operator does not exist: vector <-> real[]

    不使用参数时,直接使用字符串方式是可以正确执行的
    vector <-> '[0.1,0.2,0.3]'

    我将上述的字符串写入参数变量,感觉上应该可以的,结果就是不对。


    我将错误信息发给ChatGPT,给我的建议是这样的:

    var vector = new NpgsqlTypes.NpgsqlParameter("vector", NpgsqlTypes.NpgsqlDbType.Array | NpgsqlTypes.NpgsqlDbType.Double);

    vector.Value = new double[] {-0.007534305, 0.014973218};

    代码修改如下:

                    parameters[0].IsArray = true;

                    parameters[0].DbType = System.Data.DbType.Double;

    错误依然是:

    Npgsql.PostgresException:“42883: operator does not exist: vector <-> real[]

    请问有没有好的建议来解决这个问题?

    谢谢!


    0 回复
  •    var embedding = new Vector(new float[] { 1, 1, 1 });

    数组什么的都去掉改成直接用这个

    0 回复
  • 看ado.net用法

    0 回复
  • 我参考PgVector.Dapper的示例代码,修改后可以执行查询并返回正确的结果:

                    SqlMapper.AddTypeHandler(new VectorTypeHandler());

                    var sql = @$"select sentence_id, sentence, embedding <-> @embedding as distance

                        from public.sentence_vector

                        where Abs(embedding <-> @embedding) < {maxDistance}

                        order by embedding <-> @embedding

                        limit {pageSize} offset {pageNo * pageSize}";

                    using (var conn = DbContextExt.GetDbConnection4Vector())

                    {

                        var embedding = new Vector(vector);

                        var sentenceVectors = conn.Query<SentenceVectorR>(sql, new { embedding }).AsList();

                        return Task.FromResult(sentenceVectors);

                    }

    其中最关键的是:SqlMapper.AddTypeHandler(new VectorTypeHandler());
    我看了VectorTypeHandler的代码,似乎和SqlSugar的自定义类型思路很相似,我曾经尝试过自定义类型
    代码如下定义:

        public class PgVectorConverter : ISugarDataConverter

        {

            public SugarParameter ParameterConverter<T>(object value, int i)

            {

                var name = "@vectorP" + i;

                if (value is not Vector vector)

                    throw new SmartException("传入数据不是向量实例!", false);


                return new SugarParameter(name, vector.ToArray());

            }


            public T QueryConverter<T>(IDataRecord dr, int i)

            {

                var data = dr.GetValue(i);

                if (data is Vector)

                    return (T)data;


                return default;

            }

        }

    在向量属性的标注为下面的方式:

            [SugarColumn(ColumnName = "embedding", IsNullable = false, IsArray = true, ColumnDataType = "vector", SqlParameterDbType = typeof(PgVectorConverter))]

            public Vector Embedding

    修改后的查询代码如下:

                    SugarDbContext.UsePgVector<SentenceVectorR>();

                    var sql = @$"select sentence_id, sentence, embedding <-> @embedding as distance

                        from public.sentence_vector

                        where Abs(embedding <-> @embedding) < {maxDistance}

                        order by embedding <-> @embedding

                        limit {pageSize} offset {pageNo * pageSize}";

                    var embedding = new Vector(vector);

                    var parameters = new SugarParameter[]{

                        new SugarParameter("embedding", embedding)

                    };

                    var sentenceVectors = SugarDbContext.Ado.SqlQuery<SentenceVectorR>(sql, parameters);

                    return Task.FromResult(sentenceVectors);

    运行后报的错误如下:
    Can't write CLR type Pgvector.Vector with handler type TextHandler

    似乎很接近答案了,但又不知如何解决?
    谢谢!

    0 回复
  • var dataSourceBuilder = new NpgsqlDataSourceBuilder("数据库连接串");

                dataSourceBuilder.UseVector();

                var dataSource = dataSourceBuilder.Build();

    这个处理了吗

    0 回复
  • 实体也要用这个类型

    0 回复
  • @fate sta:参考了AI的回答:

    conn.TypeMapper.MapComposite<Vector>("vector", new VectorHandler());

    关键还是要定义Vector类型的Handler,PgVector.Dapper实现了这个功能:

    SqlMapper.AddTypeHandler(new VectorTypeHandler());

    所以可以正确执行,SqlSugar的自定义类型似乎与Hanlder的功能不一样,所以执行报错。

    感谢大力支持,谢谢!


    0 回复
  • @细雨牧童笛:解决了吗

    0 回复
  • @fate sta:目前使用

    SqlMapper.AddTypeHandler(new VectorTypeHandler());

    解决的,有时间再动态跟踪一下SqlSugar源码,这个问题花费的时间比较多,先放一放。

    感谢!

    0 回复