-- | convert a PersistEntity into document fields.
-- for inserts only: nulls are ignored so they will be unset in the document.
-- 'entityToDocument' includes nulls
toInsertDoc :: forall record.  (PersistEntity record, PersistEntityBackend record ~ DB.MongoContext)
            => record -> DB.Document
toInsertDoc record = zipFilter (embeddedFields $ toEmbedEntityDef entDef)
    (map toPersistValue $ toPersistFields record)
  where
    entDef = entityDef $ Just record
    zipFilter :: [EmbedFieldDef] -> [PersistValue] -> DB.Document
    zipFilter [] _  = []
    zipFilter _  [] = []
    zipFilter (fd:efields) (pv:pvs) =
        if isNull pv then recur else
          (fieldToLabel fd DB.:= embeddedVal (emFieldEmbed fd) pv):recur

      where
        recur = zipFilter efields pvs

        isNull PersistNull = True
        isNull (PersistMap m) = null m
        isNull (PersistList l) = null l
        isNull _ = False

    -- make sure to removed nulls from embedded entities also
    embeddedVal :: Maybe EmbedEntityDef -> PersistValue -> DB.Value
    embeddedVal (Just emDef) (PersistMap m) = DB.Doc $
      zipFilter (embeddedFields emDef) $ map snd m
    embeddedVal je@(Just _) (PersistList l) = DB.Array $ map (embeddedVal je) l
    embeddedVal _ pv = DB.val pv
toInsertDoc :: forall record.  (PersistEntity record, PersistEntityBackend record ~ DB.MongoContext)
            => record -> DB.Document
toInsertDoc record = zipFilter (embeddedFields $ toEmbedEntityDef entDef)
    (map toPersistValue $ toPersistFields record)
  where
    entDef = entityDef $ Just record
    zipFilter :: [EmbedFieldDef] -> [PersistValue] -> DB.Document
    zipFilter [] _  = []
    zipFilter _  [] = []
    zipFilter (fd:efields) (pv:pvs) =
        if isNull pv then recur else
          (fieldToLabel fd DB.:= embeddedVal (emFieldEmbed fd) pv):recur

      where
        recur = zipFilter efields pvs

        isNull PersistNull = True
        isNull (PersistMap m) = null m
        isNull (PersistList l) = null l
        isNull _ = False

    -- make sure to removed nulls from embedded entities also
    embeddedVal :: Maybe EmbedEntityDef -> PersistValue -> DB.Value
    embeddedVal (Just emDef) (PersistMap m) = DB.Doc $
      zipFilter (embeddedFields emDef) $ map snd m
    embeddedVal je@(Just _) (PersistList l) = DB.Array $ map (embeddedVal je) l
    embeddedVal _ pv = DB.val pv
toInsertDoc record = zipFilter (embeddedFields $ toEmbedEntityDef entDef)
    (map toPersistValue $ toPersistFields record)
  where
    entDef = entityDef $ Just record
    zipFilter :: [EmbedFieldDef] -> [PersistValue] -> DB.Document
    zipFilter [] _  = []
    zipFilter _  [] = []
    zipFilter (fd:efields) (pv:pvs) =
        if isNull pv then recur else
          (fieldToLabel fd DB.:= embeddedVal (emFieldEmbed fd) pv):recur

      where
        recur = zipFilter efields pvs

        isNull PersistNull = True
        isNull (PersistMap m) = null m
        isNull (PersistList l) = null l
        isNull _ = False

    -- make sure to removed nulls from embedded entities also
    embeddedVal :: Maybe EmbedEntityDef -> PersistValue -> DB.Value
    embeddedVal (Just emDef) (PersistMap m) = DB.Doc $
      zipFilter (embeddedFields emDef) $ map snd m
    embeddedVal je@(Just _) (PersistList l) = DB.Array $ map (embeddedVal je) l
    embeddedVal _ pv = DB.val pv