2013-03-26 88 views
2

我一直在研究scala,主要關於如何構建類似於C#LINQ/SQL的DSL。使用C#LINQ Query提供程序後,很容易引入我們自己的自定義查詢提供程序,它將LINQ查詢轉換爲我們自己的專有數據存儲腳本。我在scala中尋找類似於例如。斯卡拉SQL DSL(內部/外部)

val query = select Min(Close), Max(Close) 
from StockPrices 
where open > 0 

首先,這甚至有可能在使用內部DSL的scala中實現。

在這方面的任何想法/想法是高度讚賞。

我在scala空間中仍然是新的,但開始尋找斯卡拉MetaProgramming &油滑。我對Slick的抱怨是我想讓我的DSL接近SQL查詢 - 類似於上面的語法。

+0

生成的SQL看起來如何? 「Open,Close」和「Open.Close」是什麼意思? 「打開」是「StockPrices」的屬性? – EECOLOR 2013-03-26 20:29:56

+0

對不起,這是一個錯字,我已經修復了我原來的帖子。我將把這個查詢翻譯成我們專有的腳本語言。不過,我根據你的代碼示例得到了這個想法,並開始閱讀關於宏的內容,讓我看看我從這裏走多遠。 – GammaVega 2013-03-28 18:00:49

回答

3

沒有辦法讓內部DSL(當前版本)與您提供的示例完全相同。

使用宏我仍然this answer過,最接近我能得到(相對較快)是:

select(Min(StockPrices.Open), Max(StockPrices.Open)) 
    .from(StockPrices) 

一個real解決方案將需要相當長的時間來創建。如果你願意這樣做,你可以使用宏的(不是一個簡單的主題)很遠。

如果你真的想要完全相同的語法,我推薦像XText這樣的東西,它允許你用'免費'的基於eclipse的編輯器創建一個DSL。

對於上面的例子中所需要的代碼(我沒有包括提到的宏):

trait SqlElement { 
    def toString(): String 
} 

trait SqlMethod extends SqlElement { 
    protected val methodName: String 
    protected val arguments: Seq[String] 

    override def toString() = { 
    val argumentsString = arguments mkString "," 
    s"$methodName($argumentsString)" 
    } 
} 

case class Select(elements: Seq[SqlElement]) extends SqlElement { 
    override def toString() = s"SELECT ${elements mkString ", "}" 
} 

case class From(table: Metadata) extends SqlElement { 
    private val tableName = table.name 
    override def toString() = s"FROM $tableName" 
} 
case class Min(element: Metadata) extends SqlMethod { 
    val methodName = "Min" 
    val arguments = Seq(element.name) 
} 
case class Max(element: Metadata) extends SqlMethod { 
    val methodName = "Max" 
    val arguments = Seq(element.name) 
} 

class QueryBuilder(elements: Seq[SqlElement]) { 
    def this(element: SqlElement) = this(Seq(element)) 

    def from(o: Metadata) = new QueryBuilder(elements :+ From(o)) 
    def where(element: SqlElement) = new QueryBuilder(elements :+ element) 
    override def toString() = elements mkString ("\n") 
} 

def select(args: SqlElement*) = new QueryBuilder(Select(args)) 

trait Column 
object Column extends Column 

object tables { 

    object StockPrices$ { 
    val Open: Column = Column 
    val Close: Column = Column 
    } 
    val StockPrices = StockPrices$ 
} 

然後使用它:

import tables._ 
import StockPrices._ 

select(Min(StockPrices.Open), Max(StockPrices.Open)) 
    .from(StockPrices) 
    .toString 
+0

看到這個宏也會很有趣。我們希望在2014年與[jOOQ](http://www.jooq.org)一起演變成這樣一個方向...... – 2014-03-08 16:13:30

+0

我回答了這個問題,我在回答中鏈接了這個問題,這裏是直接鏈接答案:http://stackoverflow.com/a/14968212/596816 – EECOLOR 2014-03-09 11:17:15

+0

謝謝!我很抱歉,不知道我是如何錯過它的 – 2014-03-09 12:24:25

1

這是一個令人欽佩的項目,但已經啓動並且在一般版本中可用。

我說的是Slick,當然。

1

如果斯卡拉/ Java的互操作性不太多對於你來說是一個問題,如果你願意使用一個內部的DSL,並且與你所建議的語法相比有一些語法怪癖,那麼jOOQ正在成爲Slick的熱門替代品。 jOOQ manual的一個示例:

for (r <- e 
    select (
     T_BOOK.ID * T_BOOK.AUTHOR_ID, 
     T_BOOK.ID + T_BOOK.AUTHOR_ID * 3 + 4, 
     T_BOOK.TITLE || " abc" || " xy" 
    ) 
    from T_BOOK 
    leftOuterJoin (
     select (x.ID, x.YEAR_OF_BIRTH) 
     from x 
     limit 1 
     asTable x.getName() 
    ) 
    on T_BOOK.AUTHOR_ID === x.ID 
    where (T_BOOK.ID <> 2) 
    or (T_BOOK.TITLE in ("O Alquimista", "Brida")) 
    fetch 
) { 
    println(r) 
}