Skip to content

Commit

Permalink
Implement example in appendix (#44)
Browse files Browse the repository at this point in the history
* Generalise functions used in product indexing

* Add message with expected example

* Rename example with Cartesian product

* Define appendix example using predefined indexed join

* Define explicit form like in appendix

* Flip arguments to indexBy
  • Loading branch information
MatBon01 authored May 3, 2023
1 parent eb13653 commit 6333d79
Show file tree
Hide file tree
Showing 3 changed files with 40 additions and 16 deletions.
42 changes: 33 additions & 9 deletions app/AppendixExample.hs
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
module Main where

import Data.Bag
import Data.Bag as Bag
import qualified Database.Bag as BDB
import Database.IndexedTable as IT
import Data.Word
import Data.Key

-- Define types used in example
type Identifier = Int
Expand Down Expand Up @@ -31,18 +34,39 @@ invoices = Bag
, I 202 101 20160316 15
, I 203 103 20160520 10 ]

exampleSelectionCond :: (Customer, Invoice) -> Bool
exampleSelectionCond (c, i) = due i < today

exampleProjection :: (Customer, Invoice) -> (Name, Amount)
exampleProjection (c, i) = (name c, amount i)

-- Cartesian product of both databases
exampleWithCP :: (Bag Customer, Bag Invoice) -> Bag (Name, Amount)
exampleWithCP = BDB.project transformation . BDB.select cond . BDB.equijoinWithCp cid cust
where
cond :: (Customer, Invoice) -> Bool
cond (c, i) = due i < today
transformation :: (Customer, Invoice) -> (Name, Amount)
transformation (c, i) = (name c, amount i)
productExample :: (Bag Customer, Bag Invoice) -> Bag (Name, Amount)
productExample = BDB.project exampleProjection . BDB.select exampleSelectionCond . BDB.productEquijoin cid cust

predefinedIndexedEquijoin :: (Bag Customer, Bag Invoice) -> Bag (Name, Amount)
predefinedIndexedEquijoin
= BDB.project exampleProjection . BDB.select exampleSelectionCond . BDB.indexedEquijoin (fromIntegral . cid :: Customer -> Word16) (fromIntegral . cust)

explicitIndexedJoin :: (Bag Customer, Bag Invoice) -> Bag (Name, Amount)
explicitIndexedJoin (customer, invoices)= reduceBag (fmap cp (example customer invoices))
where
pair (f, g) (a, b) = (f a, g b)
-- Note: snd had to be added to example instead of copying from appendix
example cs is = fmap (pair (fmap name, fmap amount)) (cod
(fmap (pair (id, Bag.filter ((< today) . due ))) (merge (cs `BDB.indexBy` (fromIntegral . cid :: Customer -> Word16), is `BDB.indexBy` (fromIntegral . cust)))))


main :: IO ()
main = do
putStrLn "Example from appendix"
putStrLn "Expected result: [('sam',15),('pat',10)]"

putStrLn "Using bags and Cartesian products:"
print (exampleWithCP (customers, invoices))
print (productExample (customers, invoices))

putStrLn "Using predefined indexed equijoin"
print (predefinedIndexedEquijoin (customers, invoices))

putStrLn "Using explicit indexed equijoin similar to appendix"
print (explicitIndexedJoin (customers, invoices))
8 changes: 4 additions & 4 deletions src/Database/Bag.hs
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ productEquijoin fa fb = select equality . cp
where
equality (a, b) = fa a == fb b

indexBy :: (Key k) => (a -> k) -> Table a -> Map k (Table a)
indexBy keyProj = index . fmap (\x -> (keyProj x, x))
indexBy :: (Key k) => Table a -> (a -> k) -> Map k (Table a)
indexBy bs keyProj = (index . fmap (\x -> (keyProj x, x))) bs

indexedEquijoin :: (Key k) => (a -> k) -> (b -> k) -> (Table a, Table b) -> Table (a, b)
-- t1, t2 Bags
-- if1, if2 are indexing functions
indexedEquijoin if1 if2 (t1, t2) = (reduce . fmap cp . merge) (it1, it2)
where
-- Indexed table 1 and 2
it1 = indexBy if1 t1
it2 = indexBy if2 t2
it1 = t1 `indexBy` if1
it2 = t2 `indexBy` if2
6 changes: 3 additions & 3 deletions test/Database/BagSpec.hs
Original file line number Diff line number Diff line change
Expand Up @@ -135,11 +135,11 @@ spec = do
productEquijoin lastName lastName (people, people) `shouldBe` lastNameJoin
describe "indexBy" $ do
it "can correctly index with trivial key" $ do
DB.indexBy (const ()) people `shouldBe` Map.Lone people
people `DB.indexBy` const () `shouldBe` Map.Lone people
it "can correctly index an empty bag" $ do
DB.indexBy (const ()) (DB.empty :: DB.Table Int) `shouldBe` (Map.empty :: Map () (Bag.Bag Int))
(DB.empty :: DB.Table Int) `DB.indexBy` const () `shouldBe` (Map.empty :: Map () (Bag.Bag Int))
it "can correctly index a bag with a repeated index" $ do
(DB.indexBy (fromIntegral . orderId) orderItems3 :: Map.Map Word16 (Bag.Bag OrderItem)) `shouldBe` orderItems3Array
orderItems3 `DB.indexBy` (fromIntegral . orderId :: OrderItem -> Word16) `shouldBe` orderItems3Array
it "can join two tables with at most one matching element" $ do
indexedEquijoin (fromIntegral . invoiceId :: OrderInvoice -> Word16) (fromIntegral . orderId) (orderPrices1, orderItems1) `shouldBe` orderJoin1
it "can join two tables with more than one matching elements,\
Expand Down

0 comments on commit 6333d79

Please sign in to comment.