ixset
ライブラリ (または、ixset-typed
よりタイプセーフなバージョン) がこれに役立ちます。のリレーショナル部分をサポートするライブラリでありacid-state
、必要に応じて、データのバージョン管理されたシリアル化や同時実行性の保証も処理します。
Happstack Book にはIxSet のチュートリアルがあります。
重要なのixset
は、データ エントリの「キー」を自動的に管理することです。
あなたの例では、次のようなデータ型の 1 対多の関係を作成します。
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
通常どおり関数を使用してください。これは、1 対多の関係をモデル化します。
さて、多対多の関係の場合、次のような状況になります:
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
これは、ニワトリが先か卵が先かという問題を解決するためにも使用できUser
ますMessage
。
次に、id の明示的なインデックスを作成し、その id で ( を使用してuserSet @= (12423 :: Id)
) ユーザーを検索し、検索を実行するだけです。