Add the DAG tagging operations to ca-store

This commit is contained in:
hylodon 2025-09-19 01:15:47 +01:00
parent d3822ad7a6
commit 6d4bc78475
3 changed files with 81 additions and 8 deletions

View file

@ -10,14 +10,6 @@ import CAStore.Program (Program, storeFile, unstoreFile, getFileLocation)
import CAStore.Program.IO import CAStore.Program.IO
import CAStore.Program.IO.Text import CAStore.Program.IO.Text
import CAStore.Program.Storage import CAStore.Program.Storage
( registerFiles
, unregisterFiles
, generateId
, registerTags
, assignTags
, unassignTags
, showTags
)
import CAStore.Type.Text import CAStore.Type.Text
import Control.Monad (forM_) import Control.Monad (forM_)
import Control.Monad.IO.Class (liftIO) import Control.Monad.IO.Class (liftIO)
@ -47,3 +39,17 @@ runCommand (TagRemove sid ts) = do
unassignTags sid ts unassignTags sid ts
runCommand (TagShow sid) = do runCommand (TagShow sid) = do
showTags sid showTags sid
runCommand (TagSuperAdd sup subs) = do
registerTags $ sup : subs
relateTags $ flip (,) sup <$> subs
runCommand (TagSubAdd sub sups) = do
registerTags $ sub : sups
relateTags $ (,) sub <$> sups
runCommand (TagSuperRemove sup subs) = do
unrelateTags $ flip (,) sup <$> subs
runCommand (TagSubRemove sub sups) = do
unrelateTags $ (,) sub <$> sups
runCommand (TagSuperShow sup) = do
showRelationSuper sup
runCommand (TagSubShow sub) = do
showRelationSub sub

View file

@ -13,6 +13,12 @@ data Command
| TagAdd StoreId [Tag] | TagAdd StoreId [Tag]
| TagRemove StoreId [Tag] | TagRemove StoreId [Tag]
| TagShow StoreId | TagShow StoreId
| TagSuperAdd Tag [Tag]
| TagSubAdd Tag [Tag]
| TagSuperRemove Tag [Tag]
| TagSubRemove Tag [Tag]
| TagSuperShow Tag
| TagSubShow Tag
deriving (Show) deriving (Show)
parseCommand :: [String] -> Either Error Command parseCommand :: [String] -> Either Error Command
@ -28,4 +34,16 @@ parseCommand ("tag":"remove":x:xs)
= pure $ TagRemove (StoreId x) (Tag <$> xs) = pure $ TagRemove (StoreId x) (Tag <$> xs)
parseCommand ("tag":"show":x:[]) parseCommand ("tag":"show":x:[])
= pure $ TagShow (StoreId x) = pure $ TagShow (StoreId x)
parseCommand ("tag":"super":"add":x:xs)
= pure $ TagSuperAdd (Tag x) (Tag <$> xs)
parseCommand ("tag":"sub":"add":x:xs)
= pure $ TagSubAdd (Tag x) (Tag <$> xs)
parseCommand ("tag":"super":"remove":x:xs)
= pure $ TagSuperRemove (Tag x) (Tag <$> xs)
parseCommand ("tag":"sub":"remove":x:xs)
= pure $ TagSubRemove (Tag x) (Tag <$> xs)
parseCommand ("tag":"super":"show":x:[])
= pure $ TagSuperShow $ Tag x
parseCommand ("tag":"sub":"show":x:[])
= pure $ TagSubShow $ Tag x
parseCommand xs = Left $ ErrInvalidCommand xs parseCommand xs = Left $ ErrInvalidCommand xs

View file

@ -10,6 +10,11 @@ module CAStore.Program.Storage
, assignTags , assignTags
, unassignTags , unassignTags
, showTags , showTags
, showTagsRaw
, relateTags
, unrelateTags
, showRelationSuper
, showRelationSub
) )
where where
@ -32,6 +37,13 @@ initialise = do
runSqlQ "CREATE TABLE tag (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE) STRICT" runSqlQ "CREATE TABLE tag (id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT, name TEXT NOT NULL UNIQUE) STRICT"
unless ("is_tagged_with" `elem` tables) $ do unless ("is_tagged_with" `elem` tables) $ do
runSqlQ "CREATE TABLE is_tagged_with (file INTEGER NOT NULL REFERENCES file(id), tag INTEGER NOT NULL REFERENCES tag(id), PRIMARY KEY (file, tag)) STRICT" runSqlQ "CREATE TABLE is_tagged_with (file INTEGER NOT NULL REFERENCES file(id), tag INTEGER NOT NULL REFERENCES tag(id), PRIMARY KEY (file, tag)) STRICT"
unless ("dag_tag" `elem` tables) $ do
runSqlQ "CREATE TABLE dag_tag (prev INTEGER NOT NULL REFERENCES tag(id), next INTEGER NOT NULL REFERENCES tag(id), PRIMARY KEY(prev, next)) STRICT"
views <- getAllDefinedViews
unless ("path_tag" `elem` views) $ do
runSqlQ "CREATE VIEW path_tag(start, end) AS WITH current(start, end) AS (SELECT tag.id, tag.id FROM tag UNION ALL SELECT start, next FROM current INNER JOIN dag_tag ON end == prev) SELECT start, end FROM current"
unless ("file_tag" `elem` views) $ do
runSqlQ "CREATE VIEW file_tag(hash, tag) AS SELECT DISTINCT f.hash, t.name FROM file AS f INNER JOIN is_tagged_with AS r ON f.id == r.file INNER JOIN path_tag AS p ON r.tag == p.start INNER JOIN tag AS t ON t.id == p.end"
generateId :: FilePath -> Program StoreId generateId :: FilePath -> Program StoreId
generateId = fmap (StoreId . show . hash @SHA256) . liftIO . readFile generateId = fmap (StoreId . show . hash @SHA256) . liftIO . readFile
@ -85,10 +97,42 @@ unassignTags sid ts = do
showTags :: StoreId -> Program () showTags :: StoreId -> Program ()
showTags sid = do showTags sid = do
runSql $ \h ->
fold h "SELECT tag FROM file_tag WHERE hash = ? ORDER BY tag ASC" (Only sid) ()
(\() (Only x) -> putStrLn x)
showTagsRaw :: StoreId -> Program ()
showTagsRaw sid = do
runSql $ \h -> runSql $ \h ->
fold h "SELECT t.name FROM is_tagged_with AS r INNER JOIN file AS f ON r.file = f.id INNER JOIN tag AS t ON r.tag = t.id WHERE f.hash = ? ORDER BY t.name ASC" (Only sid) () fold h "SELECT t.name FROM is_tagged_with AS r INNER JOIN file AS f ON r.file = f.id INNER JOIN tag AS t ON r.tag = t.id WHERE f.hash = ? ORDER BY t.name ASC" (Only sid) ()
(\() (Only x) -> putStrLn x) (\() (Only x) -> putStrLn x)
relateTags :: [(Tag, Tag)] -> Program ()
relateTags tts = do
runSqlQ "CREATE TEMP TABLE t_rel_tag (tag1 TEXT NOT NULL, tag2 TEXT NOT NULL) STRICT"
runSql $ \h -> executeMany h "INSERT INTO t_rel_tag (tag1, tag2) VALUES (?, ?)" tts
runSqlQ "INSERT OR IGNORE INTO dag_tag (prev, next) SELECT t1.id, t2.id FROM t_rel_tag AS r INNER JOIN tag AS t1 ON r.tag1 = t1.name INNER JOIN tag AS t2 ON r.tag2 = t2.name WHERE (t2.id, t1.id) NOT IN (SELECT * FROM path_tag)"
runSqlQ "DELETE FROM t_rel_tag"
unrelateTags :: [(Tag, Tag)] -> Program ()
unrelateTags tts = do
runSqlQ "CREATE TEMP TABLE t_unrel_tag (tag1 TEXT NOT NULL, tag2 TEXT NOT NULL) STRICT"
runSql $ \h -> executeMany h "INSERT INTO t_unrel_tag (tag1, tag2) VALUES (?, ?)" tts
runSqlQ "DELETE FROM dag_tag (prev, next) WHERE (prev, next) IN (SELECT t1.id, t2.id FROM t_unrel_tag AS r INNER JOIN tag AS t1 ON r.tag1 = t1.name INNER JOIN tag AS t2 ON r.tag2 = t2.name)"
runSqlQ "DELETE FROM t_unrel_tag"
showRelationSuper :: Tag -> Program ()
showRelationSuper t = do
runSql $ \h ->
fold h "SELECT t2.name FROM dag_tag AS r INNER JOIN tag AS t1 ON r.prev = t1.id INNER JOIN tag AS t2 ON r.next = t2.id WHERE t1.name = ? ORDER By t2.name ASC" (Only t) ()
(\() (Only x) -> putStrLn x)
showRelationSub :: Tag -> Program ()
showRelationSub t = do
runSql $ \h ->
fold h "SELECT t1.name FROM dag_tag AS r INNER JOIN tag AS t1 ON r.prev = t1.id INNER JOIN tag AS t2 ON r.next = t2.id WHERE t2.name = ? ORDER By t1.name ASC" (Only t) ()
(\() (Only x) -> putStrLn x)
runSql :: (Connection -> IO a) -> Program a runSql :: (Connection -> IO a) -> Program a
runSql f = getConnectionHandle >>= (Program . liftIO . f) runSql f = getConnectionHandle >>= (Program . liftIO . f)
@ -105,3 +149,8 @@ getAllDefinedTables :: Program [String]
getAllDefinedTables getAllDefinedTables
= getConnectionHandle >>= \h -> = getConnectionHandle >>= \h ->
liftIO $ fmap fromOnly <$> query_ h "SELECT name FROM SQLITE_SCHEMA where type = 'table' ORDER BY name" liftIO $ fmap fromOnly <$> query_ h "SELECT name FROM SQLITE_SCHEMA where type = 'table' ORDER BY name"
getAllDefinedViews :: Program [String]
getAllDefinedViews
= getConnectionHandle >>= \h ->
liftIO $ fmap fromOnly <$> query_ h "SELECT name FROM SQLITE_SCHEMA where type = 'view' ORDER BY name"