I am looking for a nice solution to build and query an indexed store for documents, in my case "pages with metadata".
My pages have various fields of metadata, such as "tags", "authors" etc.
Such a page could be modeled like so
data Page =
Page { content :: Text
, tags :: [Text]
, authors :: [Text]
, score :: Int
, online :: Bool }
The software handle a collection of such Pages, and need to query through this collection. My current solution is to manually handle a Map for each field of metadata, then populate each of this map manually anytime a new Page is created (using a Map of key -> Set Page). Then for each individual case of my application, I query one or more Map, intersect (or unite) results, then process. This is tedious and error prone.
This is why I would like to design a generic document store, a bit like what Data.IxSet offers. Please note that my question is about properly typing such a store, not about using IxSet.
If all fields had the same type of keys K (like Text), such an Index could be typed as : Map Field (Map K (Set Page)), where Field is the type of the fields, for example
data Field = Tag | Author | ...
Unfortunately, it is not the case, fields keys can be Text or Date or Int or Bool etc. All of them satisfy Map constraints Ord though, so this part is not problematic.
How would you design this datatype and type it properly, so that the following interfaces could be used with it. I have put question marks where I don't know what to do with the types. Often this will be related to properly handle collections of possibly-heterogeneous types.
data Field model key = Field { fieldId :: FieldId
, extractKeys :: model -> [key] }
-- initIndex create an index for values 'model' with descriptions for fields and keys extractors.
initIndex :: [Field model ?] -> Index ? model
-- adding a document to the index
addToIndex :: Index ? model -> model -> Index ? model
-- a request combines filters with boolean logic
data Request field ? = And [Request field ?] | Or [Request field ?] | Term (Filter field ?)
-- a filter is a constraint on a field keys. 'All' will intersect, 'Any' will unite ...
data Filter field k = All field [k] | Any field [k] | GreaterThan field k | LowerThan field k
queryIndex :: Index ? model -> Request field ? -> Set model
I hope this is clear enough, if not I will update it as necessary. Thank you