文章阅读目录大纲
https://github.com/xieguigang/Darwinism
对于LINQ数据查询引擎而言,其可以接收任意类型的数据源,进行数据查询。只要存在有相对应的数据源驱动程序即可。
在之前的博客文章《【Darwinism】LINQ数据查询脚本引擎开发》我提到过,我们可以为Sqlite数据库编写数据驱动程序,这样子我们就可以使用LINQ脚本查询引擎进行Sqlite数据库的查询分析操作了。在这里,我们就来介绍一下,我是如何为LINQ查询脚本引擎编写Sqlite数据库的数据源驱动程序来完成LINQ脚本对Sqlite数据库的查询分析的。
通过R#脚本浏览Sqlite数据库
在执行LINQ脚本对Sqlite数据库的查询之前,我们首先使用R#环境之中所提供的开发者工具来看一下我们的数据库文件里面有哪些数据吧。在R#脚本之中,我们可以通过系统自带的sqlite程序包进行数据库文件内容的查看,例如:
imports "sqlite" from "devkit";
using xcc as sqlite::open(`${dirname(@script)}/xcc.db`) {
xcc
|> sqlite::ls
|> print()
;
for(name in ["Pathways", "GenePathways"]) {
print("view data contents of table:");
print(name);
xcc
|> sqlite::load(name)
|> head
|> print
;
}
}
可以看到,脚本帮我们把数据库之中的所有数据表的表名称都打印出来了。其中,有一个名称为
的数据表,待会我们就使用这个数据表进行LINQ查询脚本的测试。GenePathways
[1] "view data contents of table:"
[1] "GenePathways"
gid pid
<mode> <string> <integer>
[1, ] "xcc:XCC0734" 551
[2, ] "xcc:XCC0188" 11
[3, ] "xcc:XCC2008" 171
[4, ] "xcc:XCC1212" 1091
[5, ] "xcc:XCC1722" 41
[6, ] "xcc:XCC2712" 121
LINQ脚本数据源驱动程序的创建
LINQ脚本查询引擎可以通过插件的形式,加载外部的数据源驱动程序,从而能够查询任意来源的数据对象。在LINQ查询引擎中,所有的数据源驱动程序模块都会需要继承
对象,并实现一个用于加载数据源的迭代器函数:DataSourceDriver
Public MustInherit Class DataSourceDriver
Protected ReadOnly arguments As String()
Sub New(arguments As String())
Me.arguments = arguments
End Sub
Public MustOverride Function ReadFromUri(uri As String) As IEnumerable(Of Object)
End Class
如果你希望为这个LINQ查询引擎编写自己的数据源驱动程序,你需要完成下面的两个操作步骤:
- 创建一个新的对象,之后继承
这个数据源驱动程序抽象对象DataSourceDriver
- 实现数据源加载操作,即编写
函数进行文件数据的读取操作ReadFromUri
- 添加类型名称标记:
DriverFlagAttribute
例如,在这里实现的就是我们需要进行Sqlite数据库查询用到的Sqlite数据表加载过程:
Imports LINQ.Runtime.Drivers
Imports Microsoft.VisualBasic.Data.IO.ManagedSqlite.Core
Imports Microsoft.VisualBasic.Data.IO.ManagedSqlite.Core.SQLSchema
Imports Microsoft.VisualBasic.Data.IO.ManagedSqlite.Core.Tables
Imports Microsoft.VisualBasic.My.JavaScript
<DriverFlag("table")>
Public Class TableReader : Inherits DataSourceDriver
Public Sub New(arguments() As String)
MyBase.New(arguments)
End Sub
Public Overrides Iterator Function ReadFromUri(uri As String) As IEnumerable(Of Object)
Dim tableName As String = arguments(Scan0)
Dim sqlite As Sqlite3Database = Sqlite3Database.OpenFile(dbFile:=uri)
Dim rawRef As Sqlite3Table = sqlite.GetTable(tableName)
Dim rows As Sqlite3Row() = rawRef.EnumerateRows.ToArray
Dim schema As Schema = rawRef.SchemaDefinition.ParseSchema
Dim colnames As String() = schema.columns.Select(Function(c) c.Name).ToArray
For Each row As Sqlite3Row In rows
Dim jsObj As New JavaScriptObject
For i As Integer = 0 To colnames.Length - 1
jsObj(colnames(i)) = row.Item(i)
Next
Yield jsObj
Next
End Function
End Class
如何加载数据源驱动程序
数据源驱动程序是通过脚本最开始的
命令操作来完成的。在LINQ查询脚本的解析操作过程之中,解析器会将imports操作附加到LINQ表达式对象之中,例如:imports
imports "Sqlite3"
可以看到,我们通过这段代码将要向LINQ查询脚本的执行环境中加载一个来自于Sqlite3.dll的驱动程序。对于这个驱动程序的加载过程,我将加载过程放在了LINQ查询引擎的注册表之中完成:
Dim assembly As Assembly = Assembly.LoadFile(driverDll)
For Each type As Type In From m As Type
In assembly.GetTypes
Let flag As DriverFlagAttribute = m.GetCustomAttribute(Of DriverFlagAttribute)
Where Not flag Is Nothing
Select m
Dim flag As DriverFlagAttribute = type.GetCustomAttribute(Of DriverFlagAttribute)()
drivers(flag.type) = Function(args)
Return Activator.CreateInstance(type, {args})
End Function
Next
上面所示的一个数据驱动程序注册过程实际上就是一个反射加载类型的过程:
- 我们首先通过Assembly.LoadFile函数从文件路径加载目标驱动程序文件
- 之后在目标程序集之中查找具有DriverFlagAttribute标签的类型
- 查找到目标标签数据后,我们就有了类型名称了。这样子我们就拥有了一个从脚本之中所声明的类型名称与相对应的驱动程序的关联关系了
在Registry模块之中,存在有一个哈希表:
ReadOnly drivers As New Dictionary(Of String, IDriverLoader)
这个哈希表就是起着关联类型名称与对应的驱动程序之间的关系的功能。例如,我们在LINQ脚本之中声明了类型:
FROM x as table("GenePathways")
其将会被解析为符号申明:
- 符号类型为来自于数据源驱动程序table
- 数据源驱动程序的初始化参数值为GenePathways
通过前面的代码可以了解到,我们所编写的Sqlite数据源驱动程序是通过自定义属性标记为table名字的:
<DriverFlag("table")>
所以,我们就可以通过这样子的类型声明,加载Sqlite数据库之中对应的GenePathways数据表作为我们的LINQ查询操作的数据源了。
在加载数据源驱动程序模块的时候,我们就只需要按照定义的类型名称,在代码中查找哈希表就可以了:
Public Function GetReader(type As String, arguments As String()) As DataSourceDriver
If type = "row" Then
Return New DataFrameDriver
ElseIf drivers.ContainsKey(type) Then
Return drivers(type)(arguments)
Else
Throw New MissingPrimaryKeyException(type)
End If
End Function
LINQ查询的DEMO
下面是一个用于Sqlite数据源驱动程序的DEMO的LINQ查询脚本:
imports "Sqlite3"
FROM x as table("GenePathways")
IN "xcc.db"
WHERE x.pid = 11
ORDER BY gid
在上面的LINQ查询脚本之中,因为Sqlite并非系统内置的数据源驱动程序,其是定义在外部的一个驱动程序。所以我们会首先需要通过imports命令将我们前面编写好的Sqlite数据源驱动程序加载进入LINQ查询的解释器环境之中。
接着,就是我们的LINQ查询Sqlite数据库的具体查询内容了,在这个DEMO里:
- 我们打开的是一个文件名叫
的Sqlite数据库文件xcc.db
- 查询的对象是该Sqlite数据库文件里面的一个名称为
的数据表,从之前的R#脚本查看可以看到,在这个数据表中存在有gid和pid这两个字段。GenePathways
- 我们对这个数据表应用筛选条件:pid的值应该是等于11的
- 之后输出表格的筛选结果,并且结果应该是按照gid升序排序的
LINQ脚本的命令行传参
在LINQ脚本之中,可以通过
的方式从命令行之中得到参数信息,例如我们可以将上面的脚本改写为下面从命令行参数中得到数据库文件位置:?"argument_name"
Imports "Sqlite3"
# LINQ "./test/sqlite_cli.linq" --table "GenePathways" --db "./test/xcc.db"
FROM x as table(?"--table")
IN ?"--db"
WHERE x.pid = 11
ORDER BY gid
TAKE 25
在VisualStudio之中调试一下上面的脚本,我们的解释器程序已经正确的从命令行参数
之中得到"GenePathways"这个数据表名称了。
--table
好的,我们将上面的demo查询脚本保存一下,通过LINQ命令执行一下。我们可以看到,LINQ查询解释器已经将所有的pid等于11的结果都筛选出来了:
嗯,结果的表格有点太长了,我们应用一下TAKE操作,只取前25条记录看看。好的,LINQ查询执行成功,没有任何问题了。
- 【MZKit】简单自动化组织分区 - 2023年11月5日
- 【MZKit教程】质谱成像原始数据文件查看 - 2023年6月29日
- 生物序列图嵌入算法 - 2023年6月29日
One response
[…] 《【Darwinism】LINQ脚本查询Sqlite数据库》 […]