4

当前状态

我有两种数据类型。

data Foo = Foo
  {  fooId :: RecordId Foo
   , bars  :: [RecordId Bar]
   ...
  }

data Bar = Bar 
  {  barId :: RecordId  Bar
  ...
  }

此模式允许每个 Foo 引用任意 Bars 列表。显然,Bars 可以在任意数量的 Foo 之间共享,也可以在没有 Foo 之间共享。

我已经有使用这种模式结构的酸状态持久化的数据。

期望的状态

data Foo = Foo
  {  fooId :: RecordId Foo
   ...
  }

data Bar = Bar 
  {  barId :: RecordId  Bar
   , fooId :: RecordId Foo
  ...
  }

在期望的状态下,每个 Bar 必须只有一个 Foo,就像常见的多对一 SQL 外键关系一样。

问题

当然,现在没有办法在这两种状态之间完美过渡,因为后者的表现力不如前者。但是,我可以在这里编写处理任何歧义的代码(对于重复引用,更喜欢具有最小 fooId 的 Foo,并简单地删除任何未被 Foo 引用的 Bars)。

我的问题是我看不到任何使用 Safecopy 在这两个模式之间迁移的路径。据我所知,Safecopy 将迁移定义为类型之间的纯函数,我无法查询迁移函数中酸状态的状态。不过,我在这里需要的是一次迁移,在特定时间点的状态上运行一次,并将一个模式转换为另一个模式。有了数据库,这将是微不足道的,但在酸性状态下,我看不到前进的方向。

我拥有的解决方案的唯一暗示是有一个单独的程序(或者说,可以从主程序调用的命令行功能)专门编译以运行处理数据迁移所需的几行代码(比如说,所有Foov0, Barv0 被转换为 Foov1,Bav1),然后在我的主程序中简单地交换新模式。

但是,我什至不明白这是如何工作的。根据我对安全复制的理解,如果我以正常方式定义迁移到新模式,那么一旦我尝试访问数据,我就会得到一个新数据类型的实例,它当然不包含我需要的数据实际迁移数据。

一个(在我看来很笨拙)选项可能是定义另外两种数据类型,将数据复制到它们,然后更改架构并运行将数据复制回新架构的迁移,然后删除其他数据类型. 这需要程序的三个编译才能按顺序在数据上运行,这似乎不是很优雅!

任何指针将不胜感激。

编辑:可能的解决方案

我没有提到上面的模式被包装在一个代表整个程序状态的数据类型中,比如

data DB = DB {
  dbFoos :: [Foo],
  dbBars :: [Bar]
}

我认为这意味着我需要做的就是定义一个新的数据 DB 并编写从 DBv0 到 DB 的迁移,在那里处理我的数据,而不需要任何排序或单子活动。如果成功,我将对此进行试验并将其发布为答案。

4

1 回答 1

1

在我的特殊情况下,因为状态是由单个 DB 类型包装的,所以解决方案是为顶级类型编写迁移。因此,迁移实例可以访问所有数据,因此可以运行必要的逻辑来完成迁移。所以解决方案看起来像这样:

data DB = DB {
  dbFoos :: [Foo],
  dbBars :: [Bar]
}

data DB_v0 = DB_v0 {
  v0_dbFoos :: [Foo_v0],
  v0_dbBars :: [Bar_v0]
}

data Foo = Foo
  {  fooId :: RecordId Foo
   ...
  }

data Bar = Bar 
  {  barId :: RecordId  Bar
   , fooId :: RecordId Foo
  ...
  }
data Foo_v0 = Foo_v0
  {  v0_fooId :: RecordId Foo
   , v0_bars  :: [RecordId Bar]
   ...
  }

data Bar_v0 = Bar_v0 
  {  v0_barId :: RecordId  Bar
  ...
  }

instance Migrate DB where
  type MigrateFrom DB = DB_v0
  migrate dbV0 = DB {
    dbFoos = migrateOldFoos
   ,dbBars = migrateOldBars
  }
 where 
  migrateOldFoos :: [Foo]
  -- (access to all old data possible here)
  migrateOldBars :: [Bar]
  -- (access to all old data possible here)

将 Foo_v0 迁移到 Foo 和 Bar_v0 到 Bar 的相关实例。一个潜在的问题是 DB_v0 的定义必须引用 Foo_v0 和 Bar_v0,否则 SafeCopy 会自动将它们迁移到 Foos 和 Bars,这意味着数据在您能够在 Migrate DB 类中使用之前就已经消失了。

SafeCopy = 真棒

于 2015-03-13T15:47:42.197 回答