Qone 正式開源,使 javascript 支持 .NET LINQ

来源:https://www.cnblogs.com/iamzhanglei/archive/2018/04/24/8930625.html
-Advertisement-
Play Games

Qone 下一代 Web 查詢語言,使 javascript 支持 LINQ Github: "https://github.com/dntzhang/qone" 緣由 最近剛好修改了騰訊文檔 Excel 表格公式的一些 bug,主要是修改公式的 parser 。比如下麵的腳本怎麼轉成 javasc ...


Qone

下一代 Web 查詢語言,使 javascript 支持 LINQ

Github: https://github.com/dntzhang/qone

緣由

最近剛好修改了騰訊文檔 Excel 表格公式的一些 bug,主要是修改公式的 parser 。比如下麵的腳本怎麼轉成 javascript 運行?

= IF(SUM(J6:J7) + SUM(J6:J7) > 10, "A2 是 foo", "A2 不是 foo")

公式或一些腳本語言的實現包含幾個主要步驟:

scanner > lexer > parser > ast > code string

得到 code string 之後可以動態運行,比如 js 里使用 eval ,eval 能保留上下文信息,缺點是執行代碼包含編譯器代碼,eval 的安全性等。
得到 code string 之後也可直接使用生成的 code string 運行,缺點是依賴構建工具或者編輯器插件去動態替換源代碼。

比如 wind 同時支持 JIT 和 AOT, qone 的思路和上面類似,但不完全相同, qone 的如下:

scanner > lexer > parser > ast > method(ast)

這個後面寫原理時候再細說。

總的來說,因為騰訊文檔公式相關工作、早年的 kmdjs 開發 (uglify2) 和 .NET 開發,所以有了 qone 。


LINQ

LINQ (語言集成查詢) 是 .NET Framework 3.5 版中引入的一項創新功能。在 Visual Studio 中,可以用 Visual Basic 或 C# 為以下數據源編寫 LINQ 查詢:SQL Server 資料庫、XML 文檔、ADO.NET 數據集,以及可枚舉的 Objects(即 LINQ to Objects)。

qone 是一款讓 Web 前端工程師在 javascript 使用 .NET 平臺下類似 LINQ 語法的前端庫。qone 讓 Web 前端工程師通過字元串的形式實現了 LINQ to Objects 的調用(下麵統一叫做 qone to objects),Objects即 JSON 組成的 Array。舉個簡單的例子(qone 遠比下麵的例子強大):

var list = [
    { name: 'qone', age: 1 },
    { name: 'linq', age: 18 },
    { name: 'dntzhang', age: 28 }
]

var result = qone({ list }).query(`
            from n in list   
            where n.age > 18
            select n
        `)

assert.deepEqual(result, [{ "name": "dntzhang", "age": 28 }])

與 LINQ 一樣,和 SQL 不同,qone 的 from 也在前面,為後面語句能夠有智能提示,qone 是基於 string 的實時編譯,不是 javasript 的原生語法,所以雖然 from 寫在前面但不支持智能提示,但可以專門為 qone 寫個編輯器插件去實現智能提示,所以 qone 語法設計上依然把 from 放在前面。

從根本上說,qone to objects 表示一種新的處理集合的方法。 採用舊方法,您必須編寫指定如何從集合檢索數據的複雜的 foreach 迴圈。 而採用 qone 方法,您只需編寫描述要檢索的內容的聲明性代碼。
另外,與傳統的 foreach 迴圈相比,qone 查詢具有三大優勢:

  • 它們更簡明、更易讀,尤其在篩選多個條件時
  • 它們使用最少的應用程式代碼提供強大的篩選、排序和分組功能
  • 無需修改或只需做很小的修改即可將它們移植到其他數據源

通常,您要對數據執行的操作越複雜,就越能體會到 qone 相較於傳統迭代技術的優勢。

qone 安裝

npm install qone

qone 關鍵字與運算符

  • from
  • in
  • where
  • select
  • orderby
  • desc
  • asc
  • groupby
  • limit

其中 from 和 in 一起使用,orderby 和 desc 或者 asc 一起使用。

from 也可以把子屬性作為 dataSource:

from n in data.list   

qone 支持下麵三類運算符:

  • 括弧:( )
  • 比較運算符: = , > , < , >= , <= , !=
  • 與或非: && , || , !

條件判斷語句也支持 null, undefined, true, false。

通過上面各種組合,你可以寫出很複雜的查詢條件。比如:

qone({ list }).query(`
    from n in list   
    where !(n.age > 17 || n.age < 2) && n.name != 'dntzhang'
    select n
`)

也支持 bool 類型的查詢:

var list = [
    { name: 'qone', age: 1, isBaby: true },
    { name: 'linq', age: 18 },
    { name: 'dntzhang', age: 28 }]

var result = qone({ list }).query(`
        from a in list       
        where a.isBaby && n.name = 'qone'
        select a
    `)

assert.deepEqual(result, [{ name: 'qone', age: 1, isBaby: true }])

其中 isBaby 是 bool 類型,同樣的寫法:
a.isBaby = true (等同於: a.isBaby)
a.isBaby = false (等同於: !a.isBaby)

qone 方法註入

通過上面介紹發現 qone 不支持加減乘除位求模運算?怎麼才能圖靈完備?方法註入搞定一切!如下所示:

QUnit.test("Method test 8", function (assert) {
    var arr = [1, 2, 3, 4, 5]

    qone('square', function (num) {
        return num * num
    })

    qone('sqrt', function (num) {
        return Math.sqrt(num)
    })

    var result = qone({ arr }).query(`
      from n in arr   
      where  sqrt(n) >= 2 
      select { squareValue : square(n) }
  `)

    assert.deepEqual(result, [{ "squareValue": 16 }, { "squareValue": 25 }])
})

方法也是支持多參數傳入,所以可以寫出任意的查詢條件。其中select, where, orderby, groupby 語句都支持方法註入。

qone select 輸出

通過 select 可以輸出各種格式和欄位的數據:

QUnit.test("Select JSON test", function (assert) {
    var list = [
        { name: 'qone', age: 1 },
        { name: 'linq', age: 18 },
        { name: 'dntzhang', age: 28 }
    ]

    var result = qone({ list }).query(`
                from n in list   
                where n.age < 20
                select {n.age, n.name}
            `)

    assert.deepEqual(result, [
        { "age": 1 , "name": "qone" },
        { "age": 18, "name": "linq" }
    ])

})

把所有場景列舉一下:

  • select n 輸出源 item
  • select n.name 輸出一維表
  • select n.name, n.age 輸出二維表
  • select { n.age, n.name } 預設方式輸出 JSON Array(key自動使用 age 和 name)
  • select { a: n.age, b: n.name } 指定 key 輸出 JSON Array
  • select { a: methodName(n.age), b: n.name } 註入方法
  • select methodName(n.age), n.name 註入方法
  • select methodName(n.age, n.name, 1, true, 'abc') 註入方法並傳遞參數

qone orderby

var result = qone({ list }).query(`
                from n in list   
                where n.age > 0
                orderby n.age asc, n.name desc
                select n
            `)

如果沒有標記 asc 或者 desc,使用預設排序 asc。

qone groupby

QUnit.test("Simple groupby test 1", function (assert) {
    var list = [
        { name: 'qone', age: 1 },
        { name: 'linq', age: 18 },
        { name: 'dntzhang1', age: 28 },
        { name: 'dntzhang2', age: 28 },
        { name: 'dntzhang3', age: 29 }
    ]

    var result = qone({ list }).query(`
                from n in list   
                where n.age > 18
                groupby n.age
            `)

    assert.deepEqual(result, [
        [{ "name": "dntzhang1", "age": 28 }, { "name": "dntzhang2", "age": 28 }],
        [{ "name": "dntzhang3", "age": 29 }]])

})

groupby 可以作為結束語句,不用跟著也不能跟著 select 語句,groupby 也可以支持方法註入。

qone 多數據源

QUnit.test("Multi datasource with props condition", function (assert) {

    var listA = [
        { name: 'qone', age: 1 },
        { name: 'linq', age: 18 },
        { name: 'dntzhang', age: 28 }]


    var listB = [
        { name: 'x', age: 11 },
        { name: 'xx', age: 18 },
        { name: 'xxx', age: 13 }
    ]

    var result = qone({ listA, listB }).query(`
            from a in listA     
            from b in listB      
            where a.age = b.age
            select a, b
        `)

    assert.deepEqual(result, [[{ "name": "linq", "age": 18 }, { "name": "xx", "age": 18 }]])
})

多數據源會產生笛卡兒積。

qone 嵌套子數據源

QUnit.test("Multi deep from test ", function (assert) {

    var list = [
        { name: 'qone', age: 1, isBaby: true, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] },
        { name: 'linq', age: 18, colors: [{ xx: [100, 2, 3] }, { xx: [11, 2, 3] }] },
        { name: 'dntzhang', age: 28, colors: [{ xx: [1, 2, 3] }, { xx: [11, 2, 3] }] }]

    var result = qone({ list }).query(`
            from a in list   
            from c in a.colors   
            from d in c.xx  
            where d === 100
            select a.name, c,d
        `)

    assert.deepEqual(result, [["linq", { "xx": [100, 2, 3] }, 100]])
})

也可以和自身的數據源會產生笛卡兒積。

qone limit 與分頁查詢

通過 limit 可以應付最常見的兩種查詢場景 - top N 和 分頁。

查詢top3:

QUnit.test("Limit top 3", function (assert) {
    var list = [
        { name: 'dntzhang1', age: 1 },
        { name: 'dntzhang2', age: 2 },
        { name: 'dntzhang3', age: 3 },
        { name: 'dntzhang4', age: 4 },
        { name: 'dntzhang5', age: 5 },
        { name: 'dntzhang6', age: 6 },
        { name: 'dntzhang7', age: 7 },
        { name: 'dntzhang8', age: 8 },
        { name: 'dntzhang9', age: 9 },
        { name: 'dntzhang10', age: 10 }
    ]

    var pageIndex = 1,
        pageSize = 4
    var result = qone({ list }).query(`
                    from n in list   
                    select n
                    limit 0, 3
                `)


    assert.deepEqual(result, [

        { name: 'dntzhang1', age: 1 },
        { name: 'dntzhang2', age: 2 },
        { name: 'dntzhang3', age: 3 }
    ])

})

分頁查詢:

QUnit.test("Limit one page test", function (assert) {
    var list = [
        { name: 'dntzhang1', age: 1 },
        { name: 'dntzhang2', age: 2 },
        { name: 'dntzhang3', age: 3 },
        { name: 'dntzhang4', age: 4 },
        { name: 'dntzhang5', age: 5 },
        { name: 'dntzhang6', age: 6 },
        { name: 'dntzhang7', age: 7 },
        { name: 'dntzhang8', age: 8 },
        { name: 'dntzhang9', age: 9 },
        { name: 'dntzhang10', age: 10 }
    ]

    var pageIndex = 1,
        pageSize = 4
    var result = qone({ list }).query(`
                    from n in list   
                    where n.age > 0
                    select n
                    limit ${pageIndex * pageSize}, ${pageSize}
                `)


    assert.deepEqual(result, [
    { name: 'dntzhang5', age: 5 },
    { name: 'dntzhang6', age: 6 },
    { name: 'dntzhang7', age: 7 },
    { name: 'dntzhang8', age: 8 }])

})

star & fork & pr & repl & follow me


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • HTML5 Canvas核心技術:圖形、動畫與游戲開發 內容簡介: 《HTML5 Canvas核心技術:圖形、動畫與游戲開發》中,暢銷書作家David Geary(基瑞)先生以實用的範常式序直接切入這套API,全面講解其功能,以求讓讀者實現出內容豐富且界面一致的網路應用程式,並將開發好的程式部署在多 ...
  • 圖片局部放大效果結合的知識點主要是DOM的操作,以及事件的應用,所以首先要對DOM的操作有一定瞭解,其次能對事件的應用有一定的累積。 如上圖,可以看到,這是放大鏡的基本效果,主要分成左右兩個部分。左邊分成一張大圖,和一個導航欄,在右邊則是一個放大鏡放大後的圖片。因此,我在畫頁面的時候,大體的HTMl ...
  • <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>製作二級菜單 ...
  • 什麼是 QRCode.js? QRCode.js 是一個用於生成二維碼的 JavaScript 庫。主要是通過獲取 DOM 的標簽,再通過 HTML5 Canvas 繪製而成,不依賴任何庫。 基本用法 載入 JavaScript 文件 <script type="text/javascript" s ...
  • block 塊元素 inline 內聯元素 常見的塊元素有:div, p, h1~h6, table, form, ol, ul等 常見的內聯元素有:span, a, strong, em, label, input, select, textarea, img, br等 display:block ...
  • 場景: 我實際用到的是這樣的,我父組件引用子組件related,父組件調用獲取頁面詳情的方法,更新了state值related,子組件根據該related來渲染相關新聞內容,但是頁面打開的時候總是先載入子組件,子組件在渲染的時候還沒有獲取到更新之後的related值,即使在子組件中watch該值的變 ...
  • 主要是要引用$compile方法 ...
  • 看到小程式這一大串的“Do not have bindName handler in current page: pages/card/card. Please make sure that bindName handler has been defined in pages/card/card, ... ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...