2014-02-13 3 views
0

데이터베이스에 저장된 데이터를 기반으로 Database.Esqueleto 쿼리를 동적으로 만들려고합니다 (아래 코드의 DynamicQuery Database.Persist 엔티티 참조). 아래의 코드는 컴파일되지만 반복되는 정의 (텍스트 필드 유형의 경우 op, 날짜 필드 유형의 경우 op2Bool 필드 유형의 경우 op3)로 인해 매우 우아하지 않습니다.Database.Esqueleto SQL 연산자를 동적으로 선택하십시오.

expr의 정의에서 모든 경우에 사용할 수있는 op과 유사한 더 일반적인 기능을 작성할 수 있습니까?

test.hs:68:46: 
Couldn't match expected type `Text' with actual type `Day' 
Expected type: EntityField (ItemGeneric backend0) Text 
    Actual type: EntityField (ItemGeneric backend0) Day 
In the second argument of `(^.)', namely `ItemInserted' 
In the first argument of `op', namely `(mp ^. ItemInserted)' 

코드 조각은 다음과 같습니다 : op2는 다음과 같은 오류 메시지가 결과를 사용하는 날의 필드 종류에 op을 다시 시도

{-# LANGUAGE EmptyDataDecls #-} 
{-# LANGUAGE FlexibleContexts #-} 
{-# LANGUAGE GADTs    #-} 
{-# LANGUAGE OverloadedStrings #-} 
{-# LANGUAGE QuasiQuotes  #-} 
{-# LANGUAGE TemplateHaskell #-} 
{-# LANGUAGE TypeFamilies  #-} 
{-# LANGUAGE RankNTypes  #-} 

import Database.Esqueleto 
import Database.Esqueleto.Internal.Sql 
import Data.Time.Calendar 
import Data.Text (Text) 
import qualified Data.Text as T 
import Database.Persist.TH 
import Database.Persist.Sqlite hiding ((==.), (!=.), (>=.), (<=.)) 
import Control.Monad.IO.Class (liftIO) 

import Enums 
{- enumerated field types have to be in a separate module due to GHC 
-- stage restriction. Enums.hs contains the following definitions: 
{-# LANGUAGE TemplateHaskell #-} 
module Enums where 
import Database.Persist.TH 

data DynField = DynFieldName | DynFieldInserted | DynFieldActive deriving (Eq, Read, Show) 

derivePersistField "DynField" 

data SqlBinOp = SqlBinOpLike | SqlBinOpLtEq | SqlBinOpGtEq | SqlBinOpNotEq | SqlBinOpEq deriving (Eq, Read, Show) 

derivePersistField "SqlBinOp" 

-} 


share [mkPersist sqlSettings, mkMigrate "migrateAll"] [persistLowerCase| 
DynamicQuery 
    field DynField 
    op SqlBinOp 
    value Text 
Item 
    name Text 
    inserted Day 
    active Bool 
|] 

safeRead :: forall a. Read a => Text -> Maybe a 
safeRead s = case (reads $ T.unpack s) of 
    [(v,_)] -> Just v 
    _ -> Nothing 

getItems dc = do 

    select $ from $ \mp -> do 
     where_ $ expr mp 
     return $ mp ^. ItemId 
    where 
     value = dynamicQueryValue dc 
     boolValue = case safeRead value of 
      Just b -> b 
      Nothing -> False 
     dateValue = case safeRead value of 
      Just dt -> dt 
      Nothing -> fromGregorian 1900 1 1 
     expr = \mp -> case dynamicQueryField dc of 
      DynFieldName   -> (mp ^. ItemName) `op` val value 
      DynFieldInserted  -> (mp ^. ItemInserted) `op2` val dateValue 
      DynFieldActive   -> (mp ^. ItemActive) `op3` val boolValue 
     op = case dynamicQueryOp dc of 
      SqlBinOpEq -> (==.) 
      SqlBinOpNotEq -> (!=.) 
      SqlBinOpGtEq -> (>=.) 
      SqlBinOpLtEq -> (<=.) 
      SqlBinOpLike -> unsafeSqlBinOp " ILIKE " 

     op2 = case dynamicQueryOp dc of 
      SqlBinOpEq -> (==.) 
      SqlBinOpNotEq -> (!=.) 
      SqlBinOpGtEq -> (>=.) 
      SqlBinOpLtEq -> (<=.) 
      SqlBinOpLike -> unsafeSqlBinOp " ILIKE " 

     op3 = case dynamicQueryOp dc of 
      SqlBinOpEq -> (==.) 
      SqlBinOpNotEq -> (!=.) 
      SqlBinOpGtEq -> (>=.) 
      SqlBinOpLtEq -> (<=.) 
      SqlBinOpLike -> unsafeSqlBinOp " ILIKE " 

main = runSqlite ":memory:" $ do 
    runMigration migrateAll 
    _ <- insert $ Item "item 1" (fromGregorian 2014 2 11) True 
    _ <- insert $ Item "item 2" (fromGregorian 2014 2 12) False 
    let dc = DynamicQuery DynFieldName SqlBinOpEq "item 1" 
    items <- getItems dc 
    liftIO $ print items 
+0

@ KarthikVU, 오늘 귀하의 수정 사항을 검토했습니다 ... 내가 본 것들 중 * 모든 것에 대해 추가 한 '* 작은 변경 사항'보다 편집에 대한 더 정확한 설명을 제공해주십시오. – Sheridan

답변

1

당신에 준 연산자를 사용하여 예를 들어, 명시 적 형식 서명을 제공하는 것입니다. 미세 다음 작품 : 연산자의 경우

expr = \mp -> case dynamicQueryField dc of 
    DynFieldName  -> (mp ^. ItemName)  `op` val value 
    DynFieldInserted -> (mp ^. ItemInserted) `op` val dateValue 
    DynFieldActive -> (mp ^. ItemActive) `op` val boolValue 

op :: PersistField a => SqlExpr (Value a) -> SqlExpr (Value a) -> SqlExpr (Value Bool) 
op = case dynamicQueryOp dc of 
    SqlBinOpEq -> (==.) 
    SqlBinOpNotEq -> (!=.) 
    SqlBinOpGtEq -> (>=.) 
    SqlBinOpLtEq -> (<=.) 
    SqlBinOpLike -> unsafeSqlBinOp " ILIKE " 

는 인수 (예를 들어, Num a), 다음 위의 방법은 모든 제약 조건의 조합을 가지고 전체 op을 강제 더 많은 제약이 있었다.

+0

대단히 고마워요! 나는 PersistField typ => expr (value typ) -> expr (value typ) -> expr (Value Bool)'과 같은 연산자의 타입 시그니처를 사용하려했으나 무시 무시한 컴파일러 오류를 발생시켰다. 나는 타입 변수'expr'를 적절하게 제한하는 방법을 알지 못했습니다. – Tero

관련 문제