gpt4 book ai didi

haskell - Haskell 中关系数据的安全建模

转载 作者:行者123 更新时间:2023-12-03 07:41:02 25 4
gpt4 key购买 nike

我发现在函数式程序中对关系数据建模是很常见的。例如,在开发网站时,我可能希望使用以下数据结构来存储有关我的用户的信息:

data User = User 
{ name :: String
, birthDate :: Date
}

接下来,我想存储有关用户在我的网站上发布的消息的数据:

data Message = Message
{ user :: User
, timestamp :: Date
, content :: String
}

此数据结构存在多个相关问题:

  • 我们没有任何方法来区分姓名和出生日期相似的用户。
  • 用户数据将在序列化/反序列化时重复
  • 比较用户需要比较他们的数据,这可能是一项成本高昂的操作。
  • User 字段的更新很脆弱 - 您可能会忘记更新数据结构中所有出现的 User

这些问题是可以管理的,而我们的数据可以表示为树。例如,您可以像这样重构:

data User = User
{ name :: String
, birthDate :: Date
, messages :: [(String, Date)] -- you get the idea
}

但是,可以将数据塑造为 DAG(想象任何多对多关系),甚至是一般图形(好吧,也许不是)。在这种情况下,我倾向于通过将数据存储在 Map 中来模拟关系数据库:

newtype Id a = Id Integer
type Table a = Map (Id a) a

这种方法可行,但由于多种原因不安全且丑陋:

  • 您只需调用一个 Id 构造函数即可避免无意义的查找。
  • 在查找时,您会得到也许是,但数据库通常会在结构上确保存在一个值。
  • 这很笨拙。
  • 很难确保数据的引用完整性。
  • 管理索引(这对于性能来说非常必要)并确保其完整性更加困难和笨拙。

是否有现有的工作来克服这些问题?

看起来 Template Haskell 可以解决这些问题(就像通常那样),但我不想重新发明轮子。

最佳答案

ixset库(或 ixset-typed ,一个更类型安全的版本)将帮助您解决此问题。它是支持 acid-state 的关系部分的库。 ,如果您需要的话,它还可以处理数据的版本化序列化和/或并发保证。

Happstack 书有 IxSet tutorial .

<小时/>

ixset 的特点是它会自动管理数据条目的“ key ”。

对于您的示例,人们将为您的数据类型创建一对多关系,如下所示:

data User =
User
{ name :: String
, birthDate :: Date
} deriving (Ord, Typeable)

data Message =
Message
{ user :: User
, timestamp :: Date
, content :: String
} deriving (Ord, Typeable)

instance Indexable Message where
empty = ixSet [ ixGen (Proxy :: Proxy User) ]

然后您可以找到特定用户的消息。如果您已经构建了一个像这样的 IxSet:

user1 = User "John Doe" undefined
user2 = User "John Smith" undefined

messageSet =
foldr insert empty
[ Message user1 undefined "bla"
, Message user2 undefined "blu"
]

...然后您可以通过以下方式查找 user1 的消息:

user1Messages = toList $ messageSet @= user1

如果您需要查找消息的用户,只需像平常一样使用user函数即可。这建立了一对多关系的模型。

现在,对于多对多关系,情况如下:

data User =
User
{ name :: String
, birthDate :: Date
, messages :: [Message]
} deriving (Ord, Typeable)

data Message =
Message
{ users :: [User]
, timestamp :: Date
, content :: String
} deriving (Ord, Typeable)

...您使用ixFun创建索引,它可以与索引列表一起使用。就像这样:

instance Indexable Message where
empty = ixSet [ ixFun users ]

instance Indexable User where
empty = ixSet [ ixFun messages ]

要查找某个用户的所有消息,您仍然使用相同的函数:

user1Messages = toList $ messageSet @= user1

此外,假设您有用户索引:

userSet =
foldr insert empty
[ User "John Doe" undefined [ messageFoo, messageBar ]
, User "John Smith" undefined [ messageBar ]
]

...您可以找到一条消息的所有用户:

messageFooUsers = toList $ userSet @= messageFoo

如果您不想在添加新用户/消息时更新消息的用户或用户的消息,则应该创建一个中间数据类型来模拟用户和消息之间的关系,只需就像在 SQL 中一样(并删除 usersmessages 字段):

data UserMessage = UserMessage { umUser :: User, umMessage :: Message } 

instance Indexable UserMessage where
empty = ixSet [ ixGen (Proxy :: Proxy User), ixGen (Proxy :: Proxy Message) ]

创建一组这些关系后,您就可以通过消息查询用户以及用户的消息,而无需更新任何内容。

考虑到它的功能,该库的界面非常简单!

编辑:关于“需要比较的昂贵数据”:ixset 仅比较您在索引中指定的字段(以便通过第一个示例中的用户,它比较​​“整个用户”)。

您可以通过更改 Ord 实例来调节它比较索引字段的哪些部分。因此,如果比较用户的成本较高,您可以添加一个 userId 字段并修改 Ord User 实例 以仅比较此字段。

这也可以用来解决先有鸡还是先有蛋的问题:如果您有一个 id,但既没有 User,也没有 Message,该怎么办?

然后,您可以简单地为 ID 创建显式索引,通过该 ID 查找用户(使用 userSet @= (12423::Id)),然后进行搜索。

关于haskell - Haskell 中关系数据的安全建模,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/9234205/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com