The ixset http://hackage.haskell.org/package/ixset图书馆(或ixset-typed https://hackage.haskell.org/package/ixset-typed-0.5/docs/Data-IxSet-Typed.html,一个更类型安全的版本)将帮助您解决这个问题。它是支持关系部分的库acid-state http://hackage.haskell.org/package/acid-state,如果您需要的话,它还可以处理数据的版本化序列化和/或并发保证。
Happstack 书有一个IxSet 教程 http://happstack.com/docs/crashcourse/index.html#ixset-a-set-with-multiple-indexed-keys.
事情是关于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
with:
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
and messages
字段):
data UserMessage = UserMessage { umUser :: User, umMessage :: Message }
instance Indexable UserMessage where
empty = ixSet [ ixGen (Proxy :: Proxy User), ixGen (Proxy :: Proxy Message) ]
创建一组这些关系将允许您通过消息和用户消息查询用户,而无需更新任何内容。
考虑到它的功能,该库的界面非常简单!
EDIT:关于您的“需要比较的昂贵数据”:ixset
仅比较您在索引中指定的字段(因此,要在第一个示例中查找用户的所有消息,它会比较“整个用户”)。
您可以通过更改来调节它比较索引字段的哪些部分Ord
实例。因此,如果比较用户对您来说成本高昂,您可以添加userId
字段并修改instance Ord User
例如,仅比较该字段。
这也可以用来解决先有鸡还是先有蛋的问题:如果你有一个 id,但没有一个,怎么办?User
, nor a Message
?
然后,您可以简单地为 id 创建一个显式索引,通过该 id 查找用户(使用userSet @= (12423 :: Id)
),然后进行搜索。