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
- qone 裝置
- qone 關鍵字與運算符
- qone 要領注入
- qone select 輸出
- qone orderby
- qone groupby
- qone 多數據源
- qone 嵌套子數據源
- qone limit 與分頁查詢
- links
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 }])
})