Sometimes relational is the way to go, but it's always worth looking for other options. :) The idiomatic preference, I think, would be to separate the nesting structure from the object data. The item containment graph sounds like a RoseTree, and since location is always inherited, that can just be a Map.
import qualified Data.Map as M
data Rose a = Rose a [Rose a] deriving (Eq, Show)
data Item a = Item a deriving (Eq, Show)
type ItemGraph a = Rose (Item a)
type ItemDB k a = M.Map k (ItemGraph a)
>>> let ex_itemdb = M.fromList [("a", Rose (Item 1) [Rose (Item 2) []])]
>>> M.lookup "a" ex_itemdb
Just (Rose (Item 1) [Rose (Item 2) []])
>>> let roseContains i (Rose x xs) = i == x || any (roseContains i) xs
>>> let isItemAt i l idb = maybe False id $ roseContains i <$> M.lookup l idb
>>> isItemAt (Item 2) "a" ex_itemdb
True
>>> let swapLoc l1 l2 idb = let {i1 = M.lookup l1 idb; i2 = M.lookup l2 idb} in maybe (M.delete l1) (M.insert l1) i2 . maybe (M.delete l2) (M.insert l2) i1 $ idb
>>> let moved = swapLoc "a" "b" ex_itemdb
>>> isItemAt (Item 2) "a" moved
False
>>> moved
fromList [("b",Rose (Item 1) [Rose (Item 2) []])]
There are quite a few unfinished pieces here, but hopefully it gets you thinking in new directions!