ixset
库(或更安全类型的版本
ixset-typed
)可以帮助您完成此操作。它是支持
acid-state
关系部分的库,后者还处理数据的版本序列化和/或并发保证(如果需要的话)。
Happstack Book 中有一个IxSet教程。
ixset
的优点在于它会自动为您的数据条目管理“键”。对于您的示例,您可以像这样为数据类型创建一对多的关系:
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 中一样(并删除users
和messages
字段):
data UserMessage = UserMessage { umUser :: User, umMessage :: Message }
instance Indexable UserMessage where
empty = ixSet [ ixGen (Proxy :: Proxy User), ixGen (Proxy :: Proxy Message) ]
创建这些关联的集合将允许您查询用户和消息,而无需更新任何内容。考虑到其功能,该库具有非常简单的接口!
编辑:关于您提到的“需要比较的昂贵数据”:ixset仅比较您在索引中指定的字段(因此在第一个示例中查找所有消息的用户时,它会比较“整个用户”)。
通过修改Ord实例,您可以调节它所比较的索引字段的部分。 因此,如果对您来说比较用户很昂贵,您可以添加一个userId字段,并修改instance Ord User以仅比较此字段,例如。
这也可用于解决先有鸡还是先有蛋的问题:如果您有一个id,但没有User或Message怎么办?
然后,您可以为id创建一个显式索引,使用userSet @= (12423 :: Id)查找用户,然后进行搜索。
user1Messages = toList $ messageSet @= user1
时,我遇到了“堆栈溢出”异常。我认为这是因为我无法比较具有无限递归的数据类型(User [Message [User ...)]。第一个和第三个示例对我有用。谢谢! - Johnny Liao