【问题标题】:Populate a newly added calculated column in EntityFramework Core + npgsql在 EntityFramework Core + npgsql 中填充新添加的计算列
【发布时间】:2019-12-19 23:46:29
【问题描述】:

我在数据库表中添加了一列,该列是根据其他列的值计算得出的。它从不直接设置,而是在更新这些列时更新。

我已经在我的模型类中实现了这一点,没有任何原始 SQL,它可以很好地解决一个问题:我希望迁移在应用时为所有行填充此列,而不必等待手动更新每个字段改变之后。

模型的简化示例如下:

public class MyModel 
{
  private string _title;
  private string _description;
  private string _data;

  public string Title
  {
    get 
    {
      return _title;
    }
    set
    {
      _title = value;
      UpdateCombinedField(value, _description, _data);
    }
  }

  public string Description
  {
    get 
    {
      return _description;
    }
    set
    {
      _description = value;
      UpdateCombinedField(_title, value, _data);
    }
  }

  [Column(TypeName = "jsonb")]
  public string Data
  {
    get
    {
      return _data;
    }
    set
    {
      _data = value;
      UpdateCombinedField(_title, _description, _value);
    }
  }

  public string CombinedField { get; set; }

  public void UpdateCombinedField(title, description)
  {
    // some logic here extracts string values from json e.g. { k1: 'v1', k2: 'v2' } -> 'v1 v2'
    CombinedField = title + " " + description + " " + extractedJsonValues;
  }
}

这个想法是创建一个可以搜索的单个文本字段 - 不幸的是,由于 jsonb 的二进制性质,npgsql SearchVector option 不起作用。因此,UpdateCombinedField 创建了一个该字段的字符串,该字符串可以与其他字符串字段连接到CombinedField。

正如我所说的那样,问题是在应用迁移时填充CombinedField。

我尝试在 Migration 的 Up 方法中使用以下内容,该方法改编自上面的 SearchVector 指南,用于更新表:

migrationBuilder.Sql("UPDATE \"MyModels\" SET \"Title\" = \"Title\";");

但我不确定如何设置触发器以使上述代码正常工作,而不必将计算组合列的代码从类中取出并放入 SQL - 因此再次遇到 jsonb 二进制问题。

我们使用的是 postgresql 10。

总结需求,需要将文本列和 jsonb 列中的值组合到单个文本列中,当首次应用迁移时,该列也适用于现有行。此列是 Tsvector 是一个奖励,但不是必需的。

【问题讨论】:

    标签: c# postgresql entity-framework-core npgsql


    【解决方案1】:

    很难准确理解你在做什么。

    PostgreSQL 10 不支持计算列,尽管 PostgreSQL 12 附带了此功能 - 请参阅 this issue and the links contained within。此功能可能对您想要的非常有用。

    在任何情况下,您都可以有一个文本列,其中包含 jsonb 列的文本副本(通过触发器更新),然后可以在 tsvector 列中使用。但是,JSON 数据确实不是文本,对其进行全文搜索可能无法如您所愿。

    migrationBuilder.Sql("UPDATE \"MyModels\" SET \"Title\" = \"Title\";");

    您是否按照文档页面中的说明设置了触发器?此代码仅用于使触发器在创建后第一次运行。

    【讨论】:

    • 我稍微改变了这个问题,试图澄清目标。我认为有一个额外的 jsonb 列文本副本然后在 tsvector 中使用的选项是可行的,前提是可以创建一个触发器,只将 jsonb 的字符串值放入新列中,例如{ k1: 'v1', k2: 'v2', k3: true, k4: ['v2', 123]} 变为 'v1 v2'。我目前在 UpdateCombinedField 中有这样做的逻辑 - 我想这在 SQL 中不容易完成,这就是为什么我希望能够在首次更新数据库时以某种方式触发所有行的 UpdateCombinedField c# 方法
    • 从 jsonb 列中提取特定值并将它们连接成文本应该很容易,例如some_json->>'A' || some_json->>'b'。否则,如果您只想在数据库中添加一个连接其他列的列,请使用连接的 getter 定义一个新的 CLR 属性,然后使用 EF Core 将其映射到某个列。
    • 提取的问题是 jsonb 列的每一行可能具有完全不同的结构,因此需要某些触发器可以采用任何 jsonb 字段并将字符串值提取到连接字符串中以一般的方式
    • 如果jsonb的每一行都可以有完全不同的结构,你怎么能有一个c#方法对相同的数据进行连接?在一天结束时,您必须定义要搜索的日期...
    • 递归方法获取反序列化 json 的动态对象,递归地遍历键/值并最终返回一个标记列表,这些标记是对象中的所有字符串值,它们连接在一起,由UpdateCombinedField 的一个空格。所以最终如果我们有行'title:t1,description:d1,json:{k1:'v1',{k2:'v2'}}'和'title:t2,description:d2,json:{k1:[' v1']} - CombinedText 的两行是 't1 d1 v1 v2' 和 't2 d2 v1' - 所以我们可以在该列上搜索 'v1' 并检索这两行
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-06-23
    • 2022-11-23
    • 2021-02-11
    • 2018-12-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多