如何在酸态数据中实现monadic /顺序迁移?

时间:2015-03-12 13:45:15

标签: haskell acid-state

当前状态

我有两种数据类型。

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

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

此架构允许每个Foo引用任意条形列表。显然,Bars可以在任意数量的Foos之间共享,或者没有Foos。

我已经将数据保持在使用这种模式结构的酸状态。

期望状态

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,Barv1)然后只需在我的主程序中交换新模式。

然而,我甚至不知道这是如何运作的。在我对safecopy的理解中,如果我以正常方式定义了迁移到新模式,那么一旦我尝试访问数据,我将获得新数据类型的实例,当然这不包含我需要的数据实际迁移数据。

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

任何指针都会非常感激。

编辑:可能的解决方案

我忽略了提到上面的模式包含在表示程序整个状态的数据类型中,比如

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

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

1 个答案:

答案 0 :(得分:1)

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

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类中使用它之前数据已经消失。 / p>

SafeCopy =真棒