提交 2d576b92 编写于 作者: S StoneT2000

Docs Update; Completed async functionality for Node.js connector, updated to 1.2.0

Docs
- Docs updated
  - include third party connectors links
  - local docs updated to web versions and fixed for compatability
Node
- Async added
- Deleted uncessary code
- Docs updated
上级 76f19a7a
...@@ -45,9 +45,9 @@ test/ ...@@ -45,9 +45,9 @@ test/
Then fill the configuration file _test/cfg/taos.cfg_: Then fill the configuration file _test/cfg/taos.cfg_:
``` ```
echo -e "dataDir $(pwd)/test/data\nlogDir $(pwd)/test/log" > test/cfg/taos.cfg echo -e "dataDir $(pwd)/test/data\nlogDir $(pwd)/test/log" > test/cfg/taos.cfg
``` --> ``` -->
To start the TDengine server, run the command below in terminal: To start the TDengine server, run the command below in terminal:
```cmd ```cmd
./build/bin/taosd -c test/cfg ./build/bin/taosd -c test/cfg
``` ```
In another terminal, use the TDengine shell to connect the server: In another terminal, use the TDengine shell to connect the server:
...@@ -88,7 +88,9 @@ drop database db; ...@@ -88,7 +88,9 @@ drop database db;
``` ```
# Developing with TDengine # Developing with TDengine
TDengine provides abundant developing tools for users to develop on TDengine. Follow the links below to find your desired connectors. ### Official Connectors
TDengine provides abundant developing tools for users to develop on TDengine. Follow the links below to find your desired connectors and relevant documentation.
- [Java](https://www.taosdata.com/en/documentation/connector/#Java-Connector) - [Java](https://www.taosdata.com/en/documentation/connector/#Java-Connector)
- [C/C++](https://www.taosdata.com/en/documentation/connector/#C/C++-Connector) - [C/C++](https://www.taosdata.com/en/documentation/connector/#C/C++-Connector)
...@@ -97,6 +99,13 @@ TDengine provides abundant developing tools for users to develop on TDengine. Fo ...@@ -97,6 +99,13 @@ TDengine provides abundant developing tools for users to develop on TDengine. Fo
- [RESTful API](https://www.taosdata.com/en/documentation/connector/#RESTful-Connector) - [RESTful API](https://www.taosdata.com/en/documentation/connector/#RESTful-Connector)
- [Node.js](https://www.taosdata.com/en/documentation/connector/#Node.js-Connector) - [Node.js](https://www.taosdata.com/en/documentation/connector/#Node.js-Connector)
### Third Party Connectors
The TDengine community has also kindly built some of their own connectors! Follow the links below to find the source code for them.
- [Rust Connector](https://github.com/taosdata/TDengine/tree/master/tests/examples/rust)
- [.Net Core Connector](https://github.com/maikebing/Maikebing.EntityFrameworkCore.Taos)
# TDengine Roadmap # TDengine Roadmap
- Support event-driven stream computing - Support event-driven stream computing
- Support user defined functions - Support user defined functions
......
...@@ -3,15 +3,15 @@ ...@@ -3,15 +3,15 @@
<a class='anchor' id='什么是超级表'></a><h2>什么是超级表</h2> <a class='anchor' id='什么是超级表'></a><h2>什么是超级表</h2>
<p>STable是同一类型数据采集点的抽象,是同类型采集实例的集合,包含多张数据结构一样的子表。每个STable为其子表定义了表结构和一组标签:表结构即表中记录的数据列及其数据类型;标签名和数据类型由STable定义,标签值记录着每个子表的静态信息,用以对子表进行分组过滤。子表本质上就是普通的表,由一个时间戳主键和若干个数据列组成,每行记录着具体的数据,数据查询操作与普通表完全相同;但子表与普通表的区别在于每个子表从属于一张超级表,并带有一组由STable定义的标签值。每种类型的采集设备可以定义一个STable。数据模型定义表的每列数据的类型,如温度、压力、电压、电流、GPS实时位置等,而标签信息属于Meta Data,如采集设备的序列号、型号、位置等,是静态的,是表的元数据。用户在创建表(数据采集点)时指定STable(采集类型)外,还可以指定标签的值,也可事后增加或修改。</p> <p>STable是同一类型数据采集点的抽象,是同类型采集实例的集合,包含多张数据结构一样的子表。每个STable为其子表定义了表结构和一组标签:表结构即表中记录的数据列及其数据类型;标签名和数据类型由STable定义,标签值记录着每个子表的静态信息,用以对子表进行分组过滤。子表本质上就是普通的表,由一个时间戳主键和若干个数据列组成,每行记录着具体的数据,数据查询操作与普通表完全相同;但子表与普通表的区别在于每个子表从属于一张超级表,并带有一组由STable定义的标签值。每种类型的采集设备可以定义一个STable。数据模型定义表的每列数据的类型,如温度、压力、电压、电流、GPS实时位置等,而标签信息属于Meta Data,如采集设备的序列号、型号、位置等,是静态的,是表的元数据。用户在创建表(数据采集点)时指定STable(采集类型)外,还可以指定标签的值,也可事后增加或修改。</p>
<p>TDengine扩展标准SQL语法用于定义STable,使用关键词tags指定标签信息。语法如下:</p> <p>TDengine扩展标准SQL语法用于定义STable,使用关键词tags指定标签信息。语法如下:</p>
<pre><code class='language-mysql' lang='mysql'>CREATE TABLE &lt;stable_name&gt; (&lt;field_name&gt; TIMESTAMP, field_name1 field_type,…) TAGS(tag_name tag_type, …) </code></pre> <pre><code class="mysql language-mysql">CREATE TABLE &lt;stable_name&gt; (&lt;field_name&gt; TIMESTAMP, field_name1 field_type,…) TAGS(tag_name tag_type, …) </code></pre>
<p>其中tag_name是标签名,tag_type是标签的数据类型。标签可以使用时间戳之外的其他TDengine支持的数据类型,标签的个数最多为6个,名字不能与系统关键词相同,也不能与其他列名相同。如:</p> <p>其中tag_name是标签名,tag_type是标签的数据类型。标签可以使用时间戳之外的其他TDengine支持的数据类型,标签的个数最多为6个,名字不能与系统关键词相同,也不能与其他列名相同。如:</p>
<pre><code class='language-mysql' lang='mysql'>create table thermometer (ts timestamp, degree float) <pre><code class="mysql language-mysql">create table thermometer (ts timestamp, degree float)
tags (location binary(20), type int)</code></pre> tags (location binary(20), type int)</code></pre>
<p>上述SQL创建了一个名为thermometer的STable,带有标签location和标签type。</p> <p>上述SQL创建了一个名为thermometer的STable,带有标签location和标签type。</p>
<p>为某个采集点创建表时,可以指定其所属的STable以及标签的值,语法如下:</p> <p>为某个采集点创建表时,可以指定其所属的STable以及标签的值,语法如下:</p>
<pre><code class='language-mysql' lang='mysql'>CREATE TABLE &lt;tb_name&gt; USING &lt;stb_name&gt; TAGS (tag_value1,...)</code></pre> <pre><code class="mysql language-mysql">CREATE TABLE &lt;tb_name&gt; USING &lt;stb_name&gt; TAGS (tag_value1,...)</code></pre>
<p>沿用上面温度计的例子,使用超级表thermometer建立单个温度计数据表的语句如下:</p> <p>沿用上面温度计的例子,使用超级表thermometer建立单个温度计数据表的语句如下:</p>
<pre><code class='language-mysql' lang='mysql'>create table t1 using thermometer tags (‘beijing’, 10)</code></pre> <pre><code class="mysql language-mysql">create table t1 using thermometer tags (‘beijing’, 10)</code></pre>
<p>上述SQL以thermometer为模板,创建了名为t1的表,这张表的Schema就是thermometer的Schema,但标签location值为‘beijing’,标签type值为10。</p> <p>上述SQL以thermometer为模板,创建了名为t1的表,这张表的Schema就是thermometer的Schema,但标签location值为‘beijing’,标签type值为10。</p>
<p>用户可以使用一个STable创建数量无上限的具有不同标签的表,从这个意义上理解,STable就是若干具有相同数据模型,不同标签的表的集合。与普通表一样,用户可以创建、删除、查看超级表STable,大部分适用于普通表的查询操作都可运用到STable上,包括各种聚合和投影选择函数。除此之外,可以设置标签的过滤条件,仅对STbale中部分表进行聚合查询,大大简化应用的开发。</p> <p>用户可以使用一个STable创建数量无上限的具有不同标签的表,从这个意义上理解,STable就是若干具有相同数据模型,不同标签的表的集合。与普通表一样,用户可以创建、删除、查看超级表STable,大部分适用于普通表的查询操作都可运用到STable上,包括各种聚合和投影选择函数。除此之外,可以设置标签的过滤条件,仅对STbale中部分表进行聚合查询,大大简化应用的开发。</p>
<p>TDengine对表的主键(时间戳)建立索引,暂时不提供针对数据模型中其他采集量(比如温度、压力值)的索引。每个数据采集点会采集若干数据记录,但每个采集点的标签仅仅是一条记录,因此数据标签在存储上没有冗余,且整体数据规模有限。TDengine将标签数据与采集的动态数据完全分离存储,而且针对STable的标签建立了高性能内存索引结构,为标签提供全方位的快速操作支持。用户可按照需求对其进行增删改查(Create,Retrieve,Update,Delete,CRUD)操作。</p> <p>TDengine对表的主键(时间戳)建立索引,暂时不提供针对数据模型中其他采集量(比如温度、压力值)的索引。每个数据采集点会采集若干数据记录,但每个采集点的标签仅仅是一条记录,因此数据标签在存储上没有冗余,且整体数据规模有限。TDengine将标签数据与采集的动态数据完全分离存储,而且针对STable的标签建立了高性能内存索引结构,为标签提供全方位的快速操作支持。用户可按照需求对其进行增删改查(Create,Retrieve,Update,Delete,CRUD)操作。</p>
...@@ -19,69 +19,51 @@ tags (location binary(20), type int)</code></pre> ...@@ -19,69 +19,51 @@ tags (location binary(20), type int)</code></pre>
<a class='anchor' id='超级表管理'></a><h2>超级表管理</h2> <a class='anchor' id='超级表管理'></a><h2>超级表管理</h2>
<ul> <ul>
<li><p>创建超级表</p> <li><p>创建超级表</p>
<pre><code class='language-mysql' lang='mysql'>CREATE TABLE &lt;stable_name&gt; (&lt;field_name&gt; TIMESTAMP, field_name1 field_type,…) TAGS(tag_name tag_type, …)</code></pre> <pre><code class="mysql language-mysql">CREATE TABLE &lt;stable_name&gt; (&lt;field_name&gt; TIMESTAMP, field_name1 field_type,…) TAGS(tag_name tag_type, …)</code></pre>
<p>与创建表的SQL语法相似。但需指定TAGS字段的名称和类型。 </p> <p>与创建表的SQL语法相似。但需指定TAGS字段的名称和类型。 </p>
<p>说明:</p> <p>说明:</p>
<ol> <ol>
<li>TAGS列总长度不能超过512 bytes;</li> <li>TAGS列总长度不能超过512 bytes;</li>
<li>TAGS列的数据类型不能是timestamp和nchar类型;</li> <li>TAGS列的数据类型不能是timestamp和nchar类型;</li>
<li>TAGS列名不能与其他列名相同;</li> <li>TAGS列名不能与其他列名相同;</li>
<li>TAGS列名不能为预留关键字. </li> <li>TAGS列名不能为预留关键字. </li></ol></li>
</ol>
</li>
<li><p>显示已创建的超级表</p> <li><p>显示已创建的超级表</p>
<pre><code class='language-mysql' lang='mysql'>show stables;</code></pre> <pre><code class="mysql language-mysql">show stables;</code></pre>
<p>查看数据库内全部STable,及其相关信息,包括STable的名称、创建时间、列数量、标签(TAG)数量、通过该STable建表的数量。 </p> <p>查看数据库内全部STable,及其相关信息,包括STable的名称、创建时间、列数量、标签(TAG)数量、通过该STable建表的数量。 </p></li>
</li>
<li><p>删除超级表</p> <li><p>删除超级表</p>
<pre><code class='language-mysql' lang='mysql'>DROP TABLE &lt;stable_name&gt;</code></pre> <pre><code class="mysql language-mysql">DROP TABLE &lt;stable_name&gt;</code></pre>
<p>Note: 删除STable不会级联删除通过STable创建的表;相反删除STable时要求通过该STable创建的表都已经被删除。</p> <p>Note: 删除STable不会级联删除通过STable创建的表;相反删除STable时要求通过该STable创建的表都已经被删除。</p></li>
</li>
<li><p>查看属于某STable并满足查询条件的表</p> <li><p>查看属于某STable并满足查询条件的表</p>
<pre><code class='language-mysql' lang='mysql'>SELECT TBNAME,[TAG_NAME,…] FROM &lt;stable_name&gt; WHERE &lt;tag_name&gt; &lt;[=|=&lt;|&gt;=|&lt;&gt;] values..&gt; ([AND|OR] …)</code></pre> <pre><code class="mysql language-mysql">SELECT TBNAME,[TAG_NAME,…] FROM &lt;stable_name&gt; WHERE &lt;tag_name&gt; &lt;[=|=&lt;|&gt;=|&lt;&gt;] values..&gt; ([AND|OR] …)</code></pre>
<p>查看属于某STable并满足查询条件的表。说明:TBNAME为关键词,显示通过STable建立的子表表名,查询过程中可以使用针对标签的条件。</p> <p>查看属于某STable并满足查询条件的表。说明:TBNAME为关键词,显示通过STable建立的子表表名,查询过程中可以使用针对标签的条件。</p>
<pre><code class='language-mysql' lang='mysql'>SELECT COUNT(TBNAME) FROM &lt;stable_name&gt; WHERE &lt;tag_name&gt; &lt;[=|=&lt;|&gt;=|&lt;&gt;] values..&gt; ([AND|OR] …)</code></pre> <pre><code class="mysql language-mysql">SELECT COUNT(TBNAME) FROM &lt;stable_name&gt; WHERE &lt;tag_name&gt; &lt;[=|=&lt;|&gt;=|&lt;&gt;] values..&gt; ([AND|OR] …)</code></pre>
<p>统计属于某个STable并满足查询条件的子表的数量</p> <p>统计属于某个STable并满足查询条件的子表的数量</p></li>
</li>
</ul> </ul>
<a class='anchor' id='写数据时自动建子表'></a><h2>写数据时自动建子表</h2> <a class='anchor' id='写数据时自动建子表'></a><h2>写数据时自动建子表</h2>
<p>在某些特殊场景中,用户在写数据时并不确定某个设备的表是否存在,此时可使用自动建表语法来实现写入数据时里用超级表定义的表结构自动创建不存在的子表,若该表已存在则不会建立新表。注意:自动建表语句只能自动建立子表而不能建立超级表,这就要求超级表已经被事先定义好。自动建表语法跟insert/import语法非常相似,唯一区别是语句中增加了超级表和标签信息。具体语法如下:</p> <p>在某些特殊场景中,用户在写数据时并不确定某个设备的表是否存在,此时可使用自动建表语法来实现写入数据时里用超级表定义的表结构自动创建不存在的子表,若该表已存在则不会建立新表。注意:自动建表语句只能自动建立子表而不能建立超级表,这就要求超级表已经被事先定义好。自动建表语法跟insert/import语法非常相似,唯一区别是语句中增加了超级表和标签信息。具体语法如下:</p>
<pre><code class='language-mysql' lang='mysql'>INSERT INTO &lt;tb_name&gt; USING &lt;stb_name&gt; TAGS (&lt;tag1_value&gt;, ...) VALUES (field_value, ...) (field_value, ...) ...; <pre><code class="mysql language-mysql">INSERT INTO &lt;tb_name&gt; USING &lt;stb_name&gt; TAGS (&lt;tag1_value&gt;, ...) VALUES (field_value, ...) (field_value, ...) ...;</code></pre>
</code></pre>
<p>向表tb_name中插入一条或多条记录,如果tb_name这张表不存在,则会用超级表stb_name定义的表结构以及用户指定的标签值(即tag1_value…)来创建名为tb_name新表,并将用户指定的值写入表中。如果tb_name已经存在,则建表过程会被忽略,系统也不会检查tb_name的标签是否与用户指定的标签值一致,也即不会更新已存在表的标签。</p> <p>向表tb_name中插入一条或多条记录,如果tb_name这张表不存在,则会用超级表stb_name定义的表结构以及用户指定的标签值(即tag1_value…)来创建名为tb_name新表,并将用户指定的值写入表中。如果tb_name已经存在,则建表过程会被忽略,系统也不会检查tb_name的标签是否与用户指定的标签值一致,也即不会更新已存在表的标签。</p>
<pre><code class='language-mysql' lang='mysql'>INSERT INTO &lt;tb1_name&gt; USING &lt;stb1_name&gt; TAGS (&lt;tag1_value1&gt;, ...) VALUES (&lt;field1_value1&gt;, ...) (&lt;field1_value2&gt;, ...) ... &lt;tb_name2&gt; USING &lt;stb_name2&gt; TAGS(&lt;tag1_value2&gt;, ...) VALUES (&lt;field1_value1&gt;, ...) ...; <pre><code class="mysql language-mysql">INSERT INTO &lt;tb1_name&gt; USING &lt;stb1_name&gt; TAGS (&lt;tag1_value1&gt;, ...) VALUES (&lt;field1_value1&gt;, ...) (&lt;field1_value2&gt;, ...) ... &lt;tb_name2&gt; USING &lt;stb_name2&gt; TAGS(&lt;tag1_value2&gt;, ...) VALUES (&lt;field1_value1&gt;, ...) ...;</code></pre>
</code></pre>
<p>向多张表tb1_name,tb2_name等插入一条或多条记录,并分别指定各自的超级表进行自动建表。</p> <p>向多张表tb1_name,tb2_name等插入一条或多条记录,并分别指定各自的超级表进行自动建表。</p>
<a class='anchor' id='STable中TAG管理'></a><h2>STable中TAG管理</h2> <a class='anchor' id='STable中TAG管理'></a><h2>STable中TAG管理</h2>
<p>除了更新标签的值的操作是针对子表进行,其他所有的标签操作(添加标签、删除标签等)均只能作用于STable,不能对单个子表操作。对STable添加标签以后,依托于该STable建立的所有表将自动增加了一个标签,对于数值型的标签,新增加的标签的默认值是0.</p> <p>除了更新标签的值的操作是针对子表进行,其他所有的标签操作(添加标签、删除标签等)均只能作用于STable,不能对单个子表操作。对STable添加标签以后,依托于该STable建立的所有表将自动增加了一个标签,对于数值型的标签,新增加的标签的默认值是0.</p>
<ul> <ul>
<li><p>添加新的标签</p> <li><p>添加新的标签</p>
<pre><code class='language-mysql' lang='mysql'>ALTER TABLE &lt;stable_name&gt; ADD TAG &lt;new_tag_name&gt; &lt;TYPE&gt; <pre><code class="mysql language-mysql">ALTER TABLE &lt;stable_name&gt; ADD TAG &lt;new_tag_name&gt; &lt;TYPE&gt;</code></pre>
</code></pre> <p>为STable增加一个新的标签,并指定新标签的类型。标签总数不能超过6个。</p></li>
<p>为STable增加一个新的标签,并指定新标签的类型。标签总数不能超过6个。</p>
</li>
<li><p>删除标签</p> <li><p>删除标签</p>
<pre><code class='language-mysql' lang='mysql'>ALTER TABLE &lt;stable_name&gt; DROP TAG &lt;tag_name&gt; <pre><code class="mysql language-mysql">ALTER TABLE &lt;stable_name&gt; DROP TAG &lt;tag_name&gt;</code></pre>
</code></pre>
<p>删除超级表的一个标签,从超级表删除某个标签后,该超级表下的所有子表也会自动删除该标签。</p> <p>删除超级表的一个标签,从超级表删除某个标签后,该超级表下的所有子表也会自动删除该标签。</p>
<p>说明:第一列标签不能删除,至少需要为STable保留一个标签。</p> <p>说明:第一列标签不能删除,至少需要为STable保留一个标签。</p></li>
</li>
<li><p>修改标签名</p> <li><p>修改标签名</p>
<pre><code class='language-mysql' lang='mysql'>ALTER TABLE &lt;stable_name&gt; CHANGE TAG &lt;old_tag_name&gt; &lt;new_tag_name&gt; <pre><code class="mysql language-mysql">ALTER TABLE &lt;stable_name&gt; CHANGE TAG &lt;old_tag_name&gt; &lt;new_tag_name&gt;</code></pre>
</code></pre> <p>修改超级表的标签名,从超级表修改某个标签名后,该超级表下的所有子表也会自动更新该标签名。</p></li>
<p>修改超级表的标签名,从超级表修改某个标签名后,该超级表下的所有子表也会自动更新该标签名。</p>
</li>
<li><p>修改子表的标签值</p> <li><p>修改子表的标签值</p>
<pre><code class='language-mysql' lang='mysql'>ALTER TABLE &lt;table_name&gt; SET TAG &lt;tag_name&gt;=&lt;new_tag_value&gt; <pre><code class="mysql language-mysql">ALTER TABLE &lt;table_name&gt; SET TAG &lt;tag_name&gt;=&lt;new_tag_value&gt;</code></pre></li>
</code></pre>
</li>
</ul> </ul>
<a class='anchor' id='STable多表聚合'></a><h2>STable多表聚合</h2> <a class='anchor' id='STable多表聚合'></a><h2>STable多表聚合</h2>
<p>针对所有的通过STable创建的子表进行多表聚合查询,支持按照全部的TAG值进行条件过滤,并可将结果按照TAGS中的值进行聚合,暂不支持针对binary类型的模糊匹配过滤。语法如下:</p> <p>针对所有的通过STable创建的子表进行多表聚合查询,支持按照全部的TAG值进行条件过滤,并可将结果按照TAGS中的值进行聚合,暂不支持针对binary类型的模糊匹配过滤。语法如下:</p>
<pre><code class='language-mysql' lang='mysql'>SELECT function&lt;field_name&gt;,… <pre><code class="mysql language-mysql">SELECT function&lt;field_name&gt;,…
FROM &lt;stable_name&gt; FROM &lt;stable_name&gt;
WHERE &lt;tag_name&gt; &lt;[=|&lt;=|&gt;=|&lt;&gt;] values..&gt; ([AND|OR] …) WHERE &lt;tag_name&gt; &lt;[=|&lt;=|&gt;=|&lt;&gt;] values..&gt; ([AND|OR] …)
INTERVAL (&lt;time range&gt;) INTERVAL (&lt;time range&gt;)
...@@ -90,41 +72,39 @@ tags (location binary(20), type int)</code></pre> ...@@ -90,41 +72,39 @@ tags (location binary(20), type int)</code></pre>
SLIMIT &lt;group_limit&gt; SLIMIT &lt;group_limit&gt;
SOFFSET &lt;group_offset&gt; SOFFSET &lt;group_offset&gt;
LIMIT &lt;record_limit&gt; LIMIT &lt;record_limit&gt;
OFFSET &lt;record_offset&gt; OFFSET &lt;record_offset&gt;</code></pre>
</code></pre>
<p><strong>说明</strong></p> <p><strong>说明</strong></p>
<p>超级表聚合查询,TDengine目前支持以下聚合\选择函数:sum、count、avg、first、last、min、max、top、bottom,以及针对全部或部分列的投影操作,使用方式与单表查询的计算过程相同。暂不支持其他类型的聚合计算和四则运算。当前所有的函数及计算过程均不支持嵌套的方式进行执行。</p> <p>超级表聚合查询,TDengine目前支持以下聚合\选择函数:sum、count、avg、first、last、min、max、top、bottom,以及针对全部或部分列的投影操作,使用方式与单表查询的计算过程相同。暂不支持其他类型的聚合计算和四则运算。当前所有的函数及计算过程均不支持嵌套的方式进行执行。</p>
<p> 不使用GROUP BY的查询将会对超级表下所有满足筛选条件的表按时间进行聚合,结果输出默认是按照时间戳单调递增输出,用户可以使用ORDER BY _c0 ASC|DESC选择查询结果时间戳的升降排序;使用GROUP BY &lt;tag_name&gt; 的聚合查询会按照tags进行分组,并对每个组内的数据分别进行聚合,输出结果为各个组的聚合结果,组间的排序可以由ORDER BY &lt;tag_name&gt; 语句指定,每个分组内部,时间序列是单调递增的。 </p> <p>不使用GROUP BY的查询将会对超级表下所有满足筛选条件的表按时间进行聚合,结果输出默认是按照时间戳单调递增输出,用户可以使用ORDER BY _c0 ASC|DESC选择查询结果时间戳的升降排序;使用GROUP BY <tag_name> 的聚合查询会按照tags进行分组,并对每个组内的数据分别进行聚合,输出结果为各个组的聚合结果,组间的排序可以由ORDER BY <tag_name> 语句指定,每个分组内部,时间序列是单调递增的。 </p>
<p>使用SLIMIT/SOFFSET语句指定组间分页,即指定结果集中输出的最大组数以及对组起始的位置。使用LIMIT/OFFSET语句指定组内分页,即指定结果集中每个组内最多输出多少条记录以及记录起始的位置。</p> <p>使用SLIMIT/SOFFSET语句指定组间分页,即指定结果集中输出的最大组数以及对组起始的位置。使用LIMIT/OFFSET语句指定组内分页,即指定结果集中每个组内最多输出多少条记录以及记录起始的位置。</p>
<a class='anchor' id='STable使用示例'></a><h2>STable使用示例</h2> <a class='anchor' id='STable使用示例'></a><h2>STable使用示例</h2>
<p>以温度传感器采集时序数据作为例,示范STable的使用。 在这个例子中,对每个温度计都会建立一张表,表名为温度计的ID,温度计读数的时刻记为ts,采集的值记为degree。通过tags给每个采集器打上不同的标签,其中记录温度计的地区和类型,以方便我们后面的查询。所有温度计的采集量都一样,因此我们用STable来定义表结构。</p> <p>以温度传感器采集时序数据作为例,示范STable的使用。 在这个例子中,对每个温度计都会建立一张表,表名为温度计的ID,温度计读数的时刻记为ts,采集的值记为degree。通过tags给每个采集器打上不同的标签,其中记录温度计的地区和类型,以方便我们后面的查询。所有温度计的采集量都一样,因此我们用STable来定义表结构。</p>
<a class='anchor' id='定义STable表结构并使用它创建子表'></a><h3>定义STable表结构并使用它创建子表</h3> <a class='anchor' id='定义STable表结构并使用它创建子表'></a><h3>定义STable表结构并使用它创建子表</h3>
<p>创建STable语句如下:</p> <p>创建STable语句如下:</p>
<pre><code class='language-mysql' lang='mysql'>CREATE TABLE thermometer (ts timestamp, degree double) <pre><code class="mysql language-mysql">CREATE TABLE thermometer (ts timestamp, degree double)
TAGS(location binary(20), type int)</code></pre> TAGS(location binary(20), type int)</code></pre>
<p>假设有北京,天津和上海三个地区的采集器共4个,温度采集器有3种类型,我们就可以对每个采集器建表如下: </p> <p>假设有北京,天津和上海三个地区的采集器共4个,温度采集器有3种类型,我们就可以对每个采集器建表如下: </p>
<pre><code class='language-mysql' lang='mysql'>CREATE TABLE therm1 USING thermometer TAGS (’beijing’, 1); <pre><code class="mysql language-mysql">CREATE TABLE therm1 USING thermometer TAGS (’beijing’, 1);
CREATE TABLE therm2 USING thermometer TAGS (’beijing’, 2); CREATE TABLE therm2 USING thermometer TAGS (’beijing’, 2);
CREATE TABLE therm3 USING thermometer TAGS (’tianjin’, 1); CREATE TABLE therm3 USING thermometer TAGS (’tianjin’, 1);
CREATE TABLE therm4 USING thermometer TAGS (’shanghai’, 3);</code></pre> CREATE TABLE therm4 USING thermometer TAGS (’shanghai’, 3);</code></pre>
<p>其中therm1,therm2,therm3,therm4是超级表thermometer四个具体的子表,也即普通的Table。以therm1为例,它表示采集器therm1的数据,表结构完全由thermometer定义,标签location=”beijing”, type=1表示therm1的地区是北京,类型是第1类的温度计。</p> <p>其中therm1,therm2,therm3,therm4是超级表thermometer四个具体的子表,也即普通的Table。以therm1为例,它表示采集器therm1的数据,表结构完全由thermometer定义,标签location=”beijing”, type=1表示therm1的地区是北京,类型是第1类的温度计。</p>
<a class='anchor' id='写入数据'></a><h3>写入数据</h3> <a class='anchor' id='写入数据'></a><h3>写入数据</h3>
<p>注意,写入数据时不能直接对STable操作,而是要对每张子表进行操作。我们分别向四张表therm1,therm2, therm3, therm4写入一条数据,写入语句如下:</p> <p>注意,写入数据时不能直接对STable操作,而是要对每张子表进行操作。我们分别向四张表therm1,therm2, therm3, therm4写入一条数据,写入语句如下:</p>
<pre><code class='language-mysql' lang='mysql'>INSERT INTO therm1 VALUES (’2018-01-01 00:00:00.000’, 20); <pre><code class="mysql language-mysql">INSERT INTO therm1 VALUES (’2018-01-01 00:00:00.000’, 20);
INSERT INTO therm2 VALUES (’2018-01-01 00:00:00.000’, 21); INSERT INTO therm2 VALUES (’2018-01-01 00:00:00.000’, 21);
INSERT INTO therm3 VALUES (’2018-01-01 00:00:00.000’, 24); INSERT INTO therm3 VALUES (’2018-01-01 00:00:00.000’, 24);
INSERT INTO therm4 VALUES (’2018-01-01 00:00:00.000’, 23);</code></pre> INSERT INTO therm4 VALUES (’2018-01-01 00:00:00.000’, 23);</code></pre>
<a class='anchor' id='按标签聚合查询'></a><h3>按标签聚合查询</h3> <a class='anchor' id='按标签聚合查询'></a><h3>按标签聚合查询</h3>
<p>查询位于北京(beijing)和天津(tianjing)两个地区的温度传感器采样值的数量count(*)、平均温度avg(degree)、最高温度max(degree)、最低温度min(degree),并将结果按所处地域(location)和传感器类型(type)进行聚合。</p> <p>查询位于北京(beijing)和天津(tianjing)两个地区的温度传感器采样值的数量count(*)、平均温度avg(degree)、最高温度max(degree)、最低温度min(degree),并将结果按所处地域(location)和传感器类型(type)进行聚合。</p>
<pre><code class='language-mysql' lang='mysql'>SELECT COUNT(*), AVG(degree), MAX(degree), MIN(degree) <pre><code class="mysql language-mysql">SELECT COUNT(*), AVG(degree), MAX(degree), MIN(degree)
FROM thermometer FROM thermometer
WHERE location=’beijing’ or location=’tianjing’ WHERE location=’beijing’ or location=’tianjing’
GROUP BY location, type </code></pre> GROUP BY location, type </code></pre>
<a class='anchor' id='按时间周期聚合查询'></a><h3>按时间周期聚合查询</h3> <a class='anchor' id='按时间周期聚合查询'></a><h3>按时间周期聚合查询</h3>
<p>查询仅位于北京以外地区的温度传感器最近24小时(24h)采样值的数量count(*)、平均温度avg(degree)、最高温度max(degree)和最低温度min(degree),将采集结果按照10分钟为周期进行聚合,并将结果按所处地域(location)和传感器类型(type)再次进行聚合。</p> <p>查询仅位于北京以外地区的温度传感器最近24小时(24h)采样值的数量count(*)、平均温度avg(degree)、最高温度max(degree)和最低温度min(degree),将采集结果按照10分钟为周期进行聚合,并将结果按所处地域(location)和传感器类型(type)再次进行聚合。</p>
<pre><code class='language-mysql' lang='mysql'>SELECT COUNT(*), AVG(degree), MAX(degree), MIN(degree) <pre><code class="mysql language-mysql">SELECT COUNT(*), AVG(degree), MAX(degree), MIN(degree)
FROM thermometer FROM thermometer
WHERE name&lt;&gt;’beijing’ and ts&gt;=now-1d WHERE name&lt;&gt;’beijing’ and ts&gt;=now-1d
INTERVAL(10M) INTERVAL(10M)
GROUP BY location, type</code></pre> GROUP BY location, type</code></pre><a href='../index.html'>回去</a></section></main></div><?php include($s.'/footer.php'); ?><script>$('pre').addClass('prettyprint linenums');PR.prettyPrint()</script><script src='lib/docs/liner.js'></script></body></html>
<a href='../index.html'>回去</a></section></main></div><?php include($s.'/footer.php'); ?><script>$('pre').addClass('prettyprint linenums');PR.prettyPrint()</script><script src='lib/docs/liner.js'></script></body></html> \ No newline at end of file
\ No newline at end of file
...@@ -2,44 +2,40 @@ ...@@ -2,44 +2,40 @@
<a class='anchor' id='Continuous-Query'></a><h2>Continuous Query</h2> <a class='anchor' id='Continuous-Query'></a><h2>Continuous Query</h2>
<p>Continuous Query is a query executed by TDengine periodically with a sliding window, it is a simplified stream computing driven by timers, not by events. Continuous query can be applied to a table or a STable, and the result set can be passed to the application directly via call back function, or written into a new table in TDengine. The query is always executed on a specified time window (window size is specified by parameter interval), and this window slides forward while time flows (the sliding period is specified by parameter sliding). </p> <p>Continuous Query is a query executed by TDengine periodically with a sliding window, it is a simplified stream computing driven by timers, not by events. Continuous query can be applied to a table or a STable, and the result set can be passed to the application directly via call back function, or written into a new table in TDengine. The query is always executed on a specified time window (window size is specified by parameter interval), and this window slides forward while time flows (the sliding period is specified by parameter sliding). </p>
<p>Continuous query is defined by TAOS SQL, there is nothing special. One of the best applications is downsampling. Once it is defined, at the end of each cycle, the system will execute the query, pass the result to the application or write it to a database. </p> <p>Continuous query is defined by TAOS SQL, there is nothing special. One of the best applications is downsampling. Once it is defined, at the end of each cycle, the system will execute the query, pass the result to the application or write it to a database. </p>
<p>If historical data pints are inserted into the stream, the query won&#39;t be re-executed, and the result set won&#39;t be updated. If the result set is passed to the application, the application needs to keep the status of continuous query, the server won&#39;t maintain it. If application re-starts, it needs to decide the time where the stream computing shall be started.</p> <p>If historical data pints are inserted into the stream, the query won't be re-executed, and the result set won't be updated. If the result set is passed to the application, the application needs to keep the status of continuous query, the server won't maintain it. If application re-starts, it needs to decide the time where the stream computing shall be started.</p>
<h4>How to use continuous query</h4> <h4>How to use continuous query</h4>
<ul> <ul>
<li><p>Pass result set to application</p> <li><p>Pass result set to application</p>
<p>Application shall use API taos_stream (details in connector section) to start the stream computing. Inside the API, the SQL syntax is:</p> <p>Application shall use API taos_stream (details in connector section) to start the stream computing. Inside the API, the SQL syntax is:</p>
<pre><code class='language-sql' lang='sql'>SELECT aggregation FROM [table_name | stable_name] <pre><code class="sql language-sql">SELECT aggregation FROM [table_name | stable_name]
INTERVAL(window_size) SLIDING(period)</code></pre> INTERVAL(window_size) SLIDING(period)</code></pre>
<p>where the new keyword INTERVAL specifies the window size, and SLIDING specifies the sliding period. If parameter sliding is not specified, the sliding period will be the same as window size. The minimum window size is 10ms. The sliding period shall not be larger than the window size. If you set a value larger than the window size, the system will adjust it to window size automatically.</p> <p>where the new keyword INTERVAL specifies the window size, and SLIDING specifies the sliding period. If parameter sliding is not specified, the sliding period will be the same as window size. The minimum window size is 10ms. The sliding period shall not be larger than the window size. If you set a value larger than the window size, the system will adjust it to window size automatically.</p>
<p>For example:</p> <p>For example:</p>
<pre><code class='language-sql' lang='sql'>SELECT COUNT(*) FROM FOO_TABLE <pre><code class="sql language-sql">SELECT COUNT(*) FROM FOO_TABLE
INTERVAL(1M) SLIDING(30S)</code></pre> INTERVAL(1M) SLIDING(30S)</code></pre>
<p>The above SQL statement will count the number of records for the past 1-minute window every 30 seconds.</p> <p>The above SQL statement will count the number of records for the past 1-minute window every 30 seconds.</p></li>
</li>
<li><p>Save the result into a database</p> <li><p>Save the result into a database</p>
<p>If you want to save the result set of stream computing into a new table, the SQL shall be: </p> <p>If you want to save the result set of stream computing into a new table, the SQL shall be: </p>
<pre><code class='language-sql' lang='sql'>CREATE TABLE table_name AS <pre><code class="sql language-sql">CREATE TABLE table_name AS
SELECT aggregation from [table_name | stable_name] SELECT aggregation from [table_name | stable_name]
INTERVAL(window_size) SLIDING(period)</code></pre> INTERVAL(window_size) SLIDING(period)</code></pre>
<p>Also, you can set the time range to execute the continuous query. If no range is specified, the continuous query will be executed forever. For example, the following continuous query will be executed from now and will stop in one hour.</p> <p>Also, you can set the time range to execute the continuous query. If no range is specified, the continuous query will be executed forever. For example, the following continuous query will be executed from now and will stop in one hour.</p>
<pre><code class='language-sql' lang='sql'>CREATE TABLE QUERY_RES AS <pre><code class="sql language-sql">CREATE TABLE QUERY_RES AS
SELECT COUNT(*) FROM FOO_TABLE SELECT COUNT(*) FROM FOO_TABLE
WHERE TS &gt; NOW AND TS &lt;= NOW + 1H WHERE TS &gt; NOW AND TS &lt;= NOW + 1H
INTERVAL(1M) SLIDING(30S) </code></pre> INTERVAL(1M) SLIDING(30S) </code></pre></li>
</li>
</ul> </ul>
<a class='anchor' id='Manage-the-Continuous-Query'></a><h3>Manage the Continuous Query</h3> <a class='anchor' id='Manage-the-Continuous-Query'></a><h3>Manage the Continuous Query</h3>
<p>Inside TDengine shell, you can use the command &quot;show streams&quot; to list the ongoing continuous queries, the command &quot;kill stream&quot; to kill a specific continuous query. </p> <p>Inside TDengine shell, you can use the command "show streams" to list the ongoing continuous queries, the command "kill stream" to kill a specific continuous query. </p>
<p>If you drop a table generated by the continuous query, the query will be removed too.</p> <p>If you drop a table generated by the continuous query, the query will be removed too.</p>
<a class='anchor' id='Publisher/Subscriber'></a><h2>Publisher/Subscriber</h2> <a class='anchor' id='Publisher/Subscriber'></a><h2>Publisher/Subscriber</h2>
<p>Time series data is a sequence of data points over time. Inside a table, the data points are stored in order of timestamp. Also, there is a data retention policy, the data points will be removed once their lifetime is passed. From another view, a table in DTengine is just a standard message queue. </p> <p>Time series data is a sequence of data points over time. Inside a table, the data points are stored in order of timestamp. Also, there is a data retention policy, the data points will be removed once their lifetime is passed. From another view, a table in DTengine is just a standard message queue. </p>
<p>To reduce the development complexity and improve data consistency, TDengine provides the pub/sub functionality. To publish a message, you simply insert a record into a table. Compared with popular messaging tool Kafka, you subscribe to a table or a SQL query statement, instead of a topic. Once new data points arrive, TDengine will notify the application. The process is just like Kafka. </p> <p>To reduce the development complexity and improve data consistency, TDengine provides the pub/sub functionality. To publish a message, you simply insert a record into a table. Compared with popular messaging tool Kafka, you subscribe to a table or a SQL query statement, instead of a topic. Once new data points arrive, TDengine will notify the application. The process is just like Kafka. </p>
<p>The detailed API will be introduced in the <a href=''>connectors</a> section. </p> <p>The detailed API will be introduced in the <a href="https://www.taosdata.com/en/documentation/advanced-features/">connectors</a> section. </p>
<a class='anchor' id='Caching'></a><h2>Caching</h2> <a class='anchor' id='Caching'></a><h2>Caching</h2>
<p>TDengine allocates a fixed-size buffer in memory, the newly arrived data will be written into the buffer first. Every device or table gets one or more memory blocks. For typical IoT scenarios, the hot data shall always be newly arrived data, they are more important for timely analysis. Based on this observation, TDengine manages the cache blocks in First-In-First-Out strategy. If no enough space in the buffer, the oldest data will be saved into hard disk first, then be overwritten by newly arrived data. TDengine also guarantees every device can keep at least one block of data in the buffer. </p> <p>TDengine allocates a fixed-size buffer in memory, the newly arrived data will be written into the buffer first. Every device or table gets one or more memory blocks. For typical IoT scenarios, the hot data shall always be newly arrived data, they are more important for timely analysis. Based on this observation, TDengine manages the cache blocks in First-In-First-Out strategy. If no enough space in the buffer, the oldest data will be saved into hard disk first, then be overwritten by newly arrived data. TDengine also guarantees every device can keep at least one block of data in the buffer. </p>
<p>By this design, the application can retrieve the latest data from each device super-fast, since they are all available in memory. You can use last or last_row function to return the last data record. If the super table is used, it can be used to return the last data records of all or a subset of devices. For example, to retrieve the latest temperature from thermometers in located Beijing, execute the following SQL </p> <p>By this design, the application can retrieve the latest data from each device super-fast, since they are all available in memory. You can use last or last_row function to return the last data record. If the super table is used, it can be used to return the last data records of all or a subset of devices. For example, to retrieve the latest temperature from thermometers in located Beijing, execute the following SQL </p>
<pre><code class='language-mysql' lang='mysql'>select last(*) from thermometers where location=’beijing’</code></pre> <pre><code class="mysql language-mysql">select last(*) from thermometers where location=’beijing’</code></pre>
<p>By this design, caching tool, like Redis, is not needed in the system. It will reduce the complexity of the system. </p> <p>By this design, caching tool, like Redis, is not needed in the system. It will reduce the complexity of the system. </p>
<p>TDengine creates one or more virtual nodes(vnode) in each data node. Each vnode contains data for multiple tables and has its own buffer. The buffer of a vnode is fully separated from the buffer of another vnode, not shared. But the tables in a vnode share the same buffer. </p> <p>TDengine creates one or more virtual nodes(vnode) in each data node. Each vnode contains data for multiple tables and has its own buffer. The buffer of a vnode is fully separated from the buffer of another vnode, not shared. But the tables in a vnode share the same buffer. </p>
<p>System configuration parameter cacheBlockSize configures the cache block size in bytes, and another parameter cacheNumOfBlocks configures the number of cache blocks. The total memory for the buffer of a vnode is cacheBlockSize \times cacheNumOfBlocks. Another system parameter numOfBlocksPerMeter configures the maximum number of cache blocks a table can use. When you create a database, you can specify these parameters. </p> <p>System configuration parameter cacheBlockSize configures the cache block size in bytes, and another parameter cacheNumOfBlocks configures the number of cache blocks. The total memory for the buffer of a vnode is $cacheBlockSize \times cacheNumOfBlocks$. Another system parameter numOfBlocksPerMeter configures the maximum number of cache blocks a table can use. When you create a database, you can specify these parameters. </p><a href='../index.html'>Back</a></section></main></div><?php include($s.'/footer.php'); ?><script>$('pre').addClass('prettyprint linenums');PR.prettyPrint()</script><script src='lib/docs/liner.js'></script></body></html>
<a href='../index.html'>Back</a></section></main></div><?php include($s.'/footer.php'); ?><script>$('pre').addClass('prettyprint linenums');PR.prettyPrint()</script><script src='lib/docs/liner.js'></script></body></html> \ No newline at end of file
\ No newline at end of file
...@@ -47,7 +47,7 @@ ...@@ -47,7 +47,7 @@
<li><p><code>void taos_fetch_row_a(TAOS_RES *res, void (*fp)(void *param, TAOS_RES *, TAOS_ROW row), void *param);</code></p> <li><p><code>void taos_fetch_row_a(TAOS_RES *res, void (*fp)(void *param, TAOS_RES *, TAOS_ROW row), void *param);</code></p>
<p>The async API to fetch a result row. <em>res</em> is the result handle. <em>fp</em> is the callback function. <em>param</em> is a user-defined structure to pass to <em>fp</em>. The third parameter of the callback function is a single result row, which is different from that of <em>taos_fetch_rows_a</em> API. With this API, it is not necessary to call <em>taos_fetch_row</em> to retrieve each result row, which is handier than <em>taos_fetch_rows_a</em> but less efficient.</p></li> <p>The async API to fetch a result row. <em>res</em> is the result handle. <em>fp</em> is the callback function. <em>param</em> is a user-defined structure to pass to <em>fp</em>. The third parameter of the callback function is a single result row, which is different from that of <em>taos_fetch_rows_a</em> API. With this API, it is not necessary to call <em>taos_fetch_row</em> to retrieve each result row, which is handier than <em>taos_fetch_rows_a</em> but less efficient.</p></li>
</ul> </ul>
<p>Applications may apply operations on multiple tables. However, <strong>it is important to make sure the operations on the same table are serialized</strong>. That means after sending an insert request in a table to the server, no operations on the table are allowed before a request is received.</p> <p>Applications may apply operations on multiple tables. However, <strong>it is important to make sure the operations on the same table are serialized</strong>. That means after sending an insert request in a table to the server, no operations on the table are allowed before a response is received.</p>
<a class='anchor' id='C/C++-continuous-query-interface'></a><h3>C/C++ continuous query interface</h3> <a class='anchor' id='C/C++-continuous-query-interface'></a><h3>C/C++ continuous query interface</h3>
<p>TDengine provides APIs for continuous query driven by time, which run queries periodically in the background. There are only two APIs:</p> <p>TDengine provides APIs for continuous query driven by time, which run queries periodically in the background. There are only two APIs:</p>
<ul> <ul>
...@@ -268,17 +268,26 @@ promise.then(function(result) { ...@@ -268,17 +268,26 @@ promise.then(function(result) {
result.pretty(); //logs the results to the console as if you were in the taos shell result.pretty(); //logs the results to the console as if you were in the taos shell
});</code></pre> });</code></pre>
<p>You can also query by binding parameters to a query by filling in the question marks in a string as so. The query will automatically parse what was binded and convert it to the proper format for use with TDengine</p> <p>You can also query by binding parameters to a query by filling in the question marks in a string as so. The query will automatically parse what was binded and convert it to the proper format for use with TDengine</p>
<pre><code class="javascript language-javascript">var query = cursor.query('select * from meterinfo.meters where ts &lt;= ? and areaid = ?').bind(new Date(), 5); <pre><code class="javascript language-javascript">var query = cursor.query('select * from meterinfo.meters where ts &lt;= ? and areaid = ?;').bind(new Date(), 5);
query.execute().then(function(result) { query.execute().then(function(result) {
result.pretty(); result.pretty();
})</code></pre> })</code></pre>
<p>The TaosQuery object can also be immediately executed upon creation by passing true as the second argument, returning a promise instead of a TaosQuery.</p> <p>The TaosQuery object can also be immediately executed upon creation by passing true as the second argument, returning a promise instead of a TaosQuery.</p>
<pre><code class="javascript language-javascript">var promise = cursor.query('select * from meterinfo.meters where v1 = 30', true) <pre><code class="javascript language-javascript">var promise = cursor.query('select * from meterinfo.meters where v1 = 30;', true)
promise.then(function(result) { promise.then(function(result) {
result.pretty(); result.pretty();
})</code></pre> })</code></pre>
<h4>Async functionality</h4> <h4>Async functionality</h4>
<p>Coming soon</p> <p>Async queries can be performed using the same functions such as <code>cursor.execute</code>, <code>cursor.query</code>, but now with <code>_a</code> appended to them.</p>
<p>Say you want to execute an two async query on two seperate tables, using <code>cursor.query_a</code>, you can do that and get a TaosQuery object, which upon executing with the <code>execute_a</code> function, returns a promise that resolves with a TaosResult object.</p>
<pre><code class="javascript language-javascript">var promise1 = cursor.query_a('select count(*), avg(v1), avg(v2) from meter1;').execute_a()
var promise2 = cursor.query_a('select count(*), avg(v1), avg(v2) from meter2;').execute_a();
promise1.then(function(result) {
result.pretty();
})
promise2.then(function(result) {
result.pretty();
})</code></pre>
<h3>Example</h3> <h3>Example</h3>
<p>An example of using the NodeJS connector to create a table with weather data and create and execute queries can be found <a href="https://github.com/taosdata/TDengine/tree/master/tests/examples/nodejs/node-example.js">here</a> (The preferred method for using the connector)</p> <p>An example of using the NodeJS connector to create a table with weather data and create and execute queries can be found <a href="https://github.com/taosdata/TDengine/tree/master/tests/examples/nodejs/node-example.js">here</a> (The preferred method for using the connector)</p>
<p>An example of using the NodeJS connector to achieve the same things but without all the object wrappers that wrap around the data returned to achieve higher functionality can be found <a href="https://github.com/taosdata/TDengine/tree/master/tests/examples/nodejs/node-example-raw.js">here</a></p><a href='../index.html'>Back</a></section></main></div><?php include($s.'/footer.php'); ?><script>$('pre').addClass('prettyprint linenums');PR.prettyPrint()</script><script src='lib/docs/liner.js'></script></body></html> <p>An example of using the NodeJS connector to achieve the same things but without all the object wrappers that wrap around the data returned to achieve higher functionality can be found <a href="https://github.com/taosdata/TDengine/tree/master/tests/examples/nodejs/node-example-raw.js">here</a></p><a href='../index.html'>Back</a></section></main></div><?php include($s.'/footer.php'); ?><script>$('pre').addClass('prettyprint linenums');PR.prettyPrint()</script><script src='lib/docs/liner.js'></script></body></html>
\ No newline at end of file
<!DOCTYPE html><html lang='en'><head><title>Documentation | Taos Data</title><meta name='description' content='TDengine is an open-source big data platform for IoT. Along with a 10x faster time-series database, it provides caching, stream computing, message queuing, and other functionalities. It is designed and optimized for Internet of Things, Connected Cars, and Industrial IoT. Read the documentation for TDengine here to get started right away.'><meta name='keywords' content='TDengine, Big Data, Open Source, IoT, Connected Cars, Industrial IoT, time-series database, caching, stream computing, message queuing, IT infrastructure monitoring, application performance monitoring, Internet of Things,TAOS Data, Documentation, programming, coding, syntax, frequently asked questions, questions, faq'><meta name='title' content='Documentation | Taos Data'><meta property='og:site_name' content='Taos Data'/><meta property='og:title' content='Documentation | Taos Data'/><meta property='og:type' content='article'/><meta property='og:url' content='https://www.taosdata.com/en/documentation/super-table/index.php'/><meta property='og:description' content='TDengine is an open-source big data platform for IoT. Along with a 10x faster time-series database, it provides caching, stream computing, message queuing, and other functionalities. It is designed and optimized for Internet of Things, Connected Cars, and Industrial IoT. Read the documentation for TDengine here to get started right away.' /><link rel='canonical' href='https://www.taosdata.com/en/documentation/super-table/index.php'/><script src='../lib/jquery-3.4.1.min.js' type='application/javascript'></script><link href='../lib/bootstrap.min.css' rel='stylesheet'><link href='../styles/base.min.css' rel='stylesheet'><link rel='stylesheet' href='../lib/docs/taosdataprettify.css'><link rel='stylesheet' href='../lib/docs/docs.css'><script src='../lib/docs/prettify.js'></script><script src='../lib/docs/prettyprint-sql.js'></script></head><body><script>$('#documentation-href').addClass('active')</script><div class='container-fluid'><main class='content-wrapper'><section class='documentation'><a href='../index.html'>Back</a><h1>STable: Super Table</h1> <!DOCTYPE html><html lang='en'><head><title>Documentation | Taos Data</title><meta name='description' content='TDengine is an open-source big data platform for IoT. Along with a 10x faster time-series database, it provides caching, stream computing, message queuing, and other functionalities. It is designed and optimized for Internet of Things, Connected Cars, and Industrial IoT. Read the documentation for TDengine here to get started right away.'><meta name='keywords' content='TDengine, Big Data, Open Source, IoT, Connected Cars, Industrial IoT, time-series database, caching, stream computing, message queuing, IT infrastructure monitoring, application performance monitoring, Internet of Things,TAOS Data, Documentation, programming, coding, syntax, frequently asked questions, questions, faq'><meta name='title' content='Documentation | Taos Data'><meta property='og:site_name' content='Taos Data'/><meta property='og:title' content='Documentation | Taos Data'/><meta property='og:type' content='article'/><meta property='og:url' content='https://www.taosdata.com/en/documentation/super-table/index.php'/><meta property='og:description' content='TDengine is an open-source big data platform for IoT. Along with a 10x faster time-series database, it provides caching, stream computing, message queuing, and other functionalities. It is designed and optimized for Internet of Things, Connected Cars, and Industrial IoT. Read the documentation for TDengine here to get started right away.' /><link rel='canonical' href='https://www.taosdata.com/en/documentation/super-table/index.php'/><script src='../lib/jquery-3.4.1.min.js' type='application/javascript'></script><link href='../lib/bootstrap.min.css' rel='stylesheet'><link href='../styles/base.min.css' rel='stylesheet'><link rel='stylesheet' href='../lib/docs/taosdataprettify.css'><link rel='stylesheet' href='../lib/docs/docs.css'><script src='../lib/docs/prettify.js'></script><script src='../lib/docs/prettyprint-sql.js'></script></head><body><script>$('#documentation-href').addClass('active')</script><div class='container-fluid'><main class='content-wrapper'><section class='documentation'><a href='../index.html'>Back</a><h1>STable: Super Table</h1>
<p>&quot;One Table for One Device&quot; design can improve the insert/query performance significantly for a single device. But it has a side effect, the aggregation of multiple tables becomes hard. To reduce the complexity and improve the efficiency, TDengine introduced a new concept: STable (Super Table). </p> <p>"One Table for One Device" design can improve the insert/query performance significantly for a single device. But it has a side effect, the aggregation of multiple tables becomes hard. To reduce the complexity and improve the efficiency, TDengine introduced a new concept: STable (Super Table). </p>
<a class='anchor' id='What-is-a-Super-Table'></a><h2>What is a Super Table</h2> <a class='anchor' id='What-is-a-Super-Table'></a><h2>What is a Super Table</h2>
<p>STable is an abstract and a template for a type of device. A STable contains a set of devices (tables) that have the same schema or data structure. Besides the shared schema, a STable has a set of tags, like the model, serial number and so on. Tags are used to record the static attributes for the devices and are used to group a set of devices (tables) for aggregation. Tags are metadata of a table and can be added, deleted or changed. </p> <p>STable is an abstract and a template for a type of device. A STable contains a set of devices (tables) that have the same schema or data structure. Besides the shared schema, a STable has a set of tags, like the model, serial number and so on. Tags are used to record the static attributes for the devices and are used to group a set of devices (tables) for aggregation. Tags are metadata of a table and can be added, deleted or changed. </p>
<p>TDengine does not save tags as a part of the data points collected. Instead, tags are saved as metadata. Each table has a set of tags. To improve query performance, tags are all cached and indexed. One table can only belong to one STable, but one STable may contain many tables. </p> <p>TDengine does not save tags as a part of the data points collected. Instead, tags are saved as metadata. Each table has a set of tags. To improve query performance, tags are all cached and indexed. One table can only belong to one STable, but one STable may contain many tables. </p>
<p>Like a table, you can create, show, delete and describe STables. Most query operations on tables can be applied to STable too, including the aggregation and selector functions. For queries on a STable, if no tags filter, the operations are applied to all the tables created via this STable. If there is a tag filter, the operations are applied only to a subset of the tables which satisfy the tag filter conditions. It will be very convenient to use tags to put devices into different groups for aggregation.</p> <p>Like a table, you can create, show, delete and describe STables. Most query operations on tables can be applied to STable too, including the aggregation and selector functions. For queries on a STable, if no tags filter, the operations are applied to all the tables created via this STable. If there is a tag filter, the operations are applied only to a subset of the tables which satisfy the tag filter conditions. It will be very convenient to use tags to put devices into different groups for aggregation.</p>
<a class='anchor' id='Create-a-STable'></a><h2>Create a STable</h2> <a class='anchor' id='Create-a-STable'></a><h2>Create a STable</h2>
<p>Similiar to creating a standard table, syntax is: </p> <p>Similiar to creating a standard table, syntax is: </p>
<pre><code class='language-mysql' lang='mysql'>CREATE TABLE &lt;stable_name&gt; (&lt;field_name&gt; TIMESTAMP, field_name1 field_type,…) TAGS(tag_name tag_type, …)</code></pre> <pre><code class="mysql language-mysql">CREATE TABLE &lt;stable_name&gt; (&lt;field_name&gt; TIMESTAMP, field_name1 field_type,…) TAGS(tag_name tag_type, …)</code></pre>
<p>New keyword &quot;tags&quot; is introduced, where tag_name is the tag name, and tag_type is the associated data type. </p> <p>New keyword "tags" is introduced, where tag_name is the tag name, and tag_type is the associated data type. </p>
<p>Note:</p> <p>Note:</p>
<ol> <ol>
<li>The bytes of all tags together shall be less than 512 </li> <li>The bytes of all tags together shall be less than 512 </li>
<li>Tag&#39;s data type can not be time stamp or nchar</li> <li>Tag's data type can not be time stamp or nchar</li>
<li>Tag name shall be different from the field name</li> <li>Tag name shall be different from the field name</li>
<li>Tag name shall not be the same as system keywords</li> <li>Tag name shall not be the same as system keywords</li>
<li>Maximum number of tags is 6 </li> <li>Maximum number of tags is 6 </li>
</ol> </ol>
<p>For example:</p> <p>For example:</p>
<pre><code class='language-mysql' lang='mysql'>create table thermometer (ts timestamp, degree float) <pre><code class="mysql language-mysql">create table thermometer (ts timestamp, degree float)
tags (location binary(20), type int)</code></pre> tags (location binary(20), type int)</code></pre>
<p>The above statement creates a STable thermometer with two tag &quot;location&quot; and &quot;type&quot;</p> <p>The above statement creates a STable thermometer with two tag "location" and "type"</p>
<a class='anchor' id='Create-a-Table-via-STable'></a><h2>Create a Table via STable</h2> <a class='anchor' id='Create-a-Table-via-STable'></a><h2>Create a Table via STable</h2>
<p>To create a table for a device, you can use a STable as its template and assign the tag values. The syntax is:</p> <p>To create a table for a device, you can use a STable as its template and assign the tag values. The syntax is:</p>
<pre><code class='language-mysql' lang='mysql'>CREATE TABLE &lt;tb_name&gt; USING &lt;stb_name&gt; TAGS (tag_value1,...)</code></pre> <pre><code class="mysql language-mysql">CREATE TABLE &lt;tb_name&gt; USING &lt;stb_name&gt; TAGS (tag_value1,...)</code></pre>
<p>You can create any number of tables via a STable, and each table may have different tag values. For example, you create five tables via STable thermometer below:</p> <p>You can create any number of tables via a STable, and each table may have different tag values. For example, you create five tables via STable thermometer below:</p>
<pre><code class='language-mysql' lang='mysql'> create table t1 using thermometer tags (‘beijing’, 10); <pre><code class="mysql language-mysql"> create table t1 using thermometer tags (‘beijing’, 10);
create table t2 using thermometer tags (‘beijing’, 20); create table t2 using thermometer tags (‘beijing’, 20);
create table t3 using thermometer tags (‘shanghai’, 10); create table t3 using thermometer tags (‘shanghai’, 10);
create table t4 using thermometer tags (‘shanghai’, 20); create table t4 using thermometer tags (‘shanghai’, 20);
create table t5 using thermometer tags (‘new york’, 10);</code></pre> create table t5 using thermometer tags (‘new york’, 10);</code></pre>
<a class='anchor' id='Aggregate-Tables-via-STable'></a><h2>Aggregate Tables via STable</h2> <a class='anchor' id='Aggregate-Tables-via-STable'></a><h2>Aggregate Tables via STable</h2>
<p>You can group a set of tables together by specifying the tags filter condition, then apply the aggregation operations. The result set can be grouped and ordered based on tag value. Syntax is:</p> <p>You can group a set of tables together by specifying the tags filter condition, then apply the aggregation operations. The result set can be grouped and ordered based on tag value. Syntax is:</p>
<pre><code class='language-mysql' lang='mysql'>SELECT function&lt;field_name&gt;,… <pre><code class="mysql language-mysql">SELECT function&lt;field_name&gt;,…
FROM &lt;stable_name&gt; FROM &lt;stable_name&gt;
WHERE &lt;tag_name&gt; &lt;[=|&lt;=|&gt;=|&lt;&gt;] values..&gt; ([AND|OR] …) WHERE &lt;tag_name&gt; &lt;[=|&lt;=|&gt;=|&lt;&gt;] values..&gt; ([AND|OR] …)
INTERVAL (&lt;time range&gt;) INTERVAL (&lt;time range&gt;)
...@@ -44,54 +43,53 @@ tags (location binary(20), type int)</code></pre> ...@@ -44,54 +43,53 @@ tags (location binary(20), type int)</code></pre>
OFFSET &lt;record_offset&gt;</code></pre> OFFSET &lt;record_offset&gt;</code></pre>
<p>For the time being, STable supports only the following aggregation/selection functions: <em>sum, count, avg, first, last, min, max, top, bottom</em>, and the projection operations, the same syntax as a standard table. Arithmetic operations are not supported, embedded queries not either. </p> <p>For the time being, STable supports only the following aggregation/selection functions: <em>sum, count, avg, first, last, min, max, top, bottom</em>, and the projection operations, the same syntax as a standard table. Arithmetic operations are not supported, embedded queries not either. </p>
<p><em>INTERVAL</em> is used for the aggregation over a time range.</p> <p><em>INTERVAL</em> is used for the aggregation over a time range.</p>
<p>If <em>GROUP BY</em> is not used, the aggregation is applied to all the selected tables, and the result set is output in ascending order of the timestamp, but you can use &quot;<em>ORDER BY _c0 ASC|DESC</em>&quot; to specify the order you like. </p> <p>If <em>GROUP BY</em> is not used, the aggregation is applied to all the selected tables, and the result set is output in ascending order of the timestamp, but you can use "<em>ORDER BY _c0 ASC|DESC</em>" to specify the order you like. </p>
<p>If <em>GROUP BY &lt;tag_name&gt;</em> is used, the aggregation is applied to groups based on tags. Each group is aggregated independently. Result set is a group of aggregation results. The group order is decided by <em>ORDER BY &lt;tag_name&gt;</em>. Inside each group, the result set is in the ascending order of the time stamp. </p> <p>If <em>GROUP BY <tag_name></em> is used, the aggregation is applied to groups based on tags. Each group is aggregated independently. Result set is a group of aggregation results. The group order is decided by <em>ORDER BY <tag_name></em>. Inside each group, the result set is in the ascending order of the time stamp. </p>
<p><em>SLIMIT/SOFFSET</em> are used to limit the number of groups and starting group number.</p> <p><em>SLIMIT/SOFFSET</em> are used to limit the number of groups and starting group number.</p>
<p><em>LIMIT/OFFSET</em> are used to limit the number of records in a group and the starting rows.</p> <p><em>LIMIT/OFFSET</em> are used to limit the number of records in a group and the starting rows.</p>
<a class='anchor' id='Example-1:'></a><h3>Example 1:</h3> <a class='anchor' id='Example-1:'></a><h3>Example 1:</h3>
<p>Check the average, maximum, and minimum temperatures of Beijing and Shanghai, and group the result set by location and type. The SQL statement shall be:</p> <p>Check the average, maximum, and minimum temperatures of Beijing and Shanghai, and group the result set by location and type. The SQL statement shall be:</p>
<pre><code class='language-mysql' lang='mysql'>SELECT COUNT(*), AVG(degree), MAX(degree), MIN(degree) <pre><code class="mysql language-mysql">SELECT COUNT(*), AVG(degree), MAX(degree), MIN(degree)
FROM thermometer FROM thermometer
WHERE location=’beijing’ or location=’tianjing’ WHERE location=’beijing’ or location=’tianjing’
GROUP BY location, type </code></pre> GROUP BY location, type </code></pre>
<a class='anchor' id='Example-2:'></a><h3>Example 2:</h3> <a class='anchor' id='Example-2:'></a><h3>Example 2:</h3>
<p>List the number of records, average, maximum, and minimum temperature every 10 minutes for the past 24 hours for all the thermometers located in Beijing with type 10. The SQL statement shall be:</p> <p>List the number of records, average, maximum, and minimum temperature every 10 minutes for the past 24 hours for all the thermometers located in Beijing with type 10. The SQL statement shall be:</p>
<pre><code class='language-mysql' lang='mysql'>SELECT COUNT(*), AVG(degree), MAX(degree), MIN(degree) <pre><code class="mysql language-mysql">SELECT COUNT(*), AVG(degree), MAX(degree), MIN(degree)
FROM thermometer FROM thermometer
WHERE name=’beijing’ and type=10 and ts&gt;=now-1d WHERE name=’beijing’ and type=10 and ts&gt;=now-1d
INTERVAL(10M)</code></pre> INTERVAL(10M)</code></pre>
<a class='anchor' id='Create-Table-Automatically'></a><h2>Create Table Automatically</h2> <a class='anchor' id='Create-Table-Automatically'></a><h2>Create Table Automatically</h2>
<p>Insert operation will fail if the table is not created yet. But for STable, TDengine can create the table automatically if the application provides the STable name, table name and tags&#39; value when inserting data points. The syntax is:</p> <p>Insert operation will fail if the table is not created yet. But for STable, TDengine can create the table automatically if the application provides the STable name, table name and tags' value when inserting data points. The syntax is:</p>
<pre><code class='language-mysql' lang='mysql'>INSERT INTO &lt;tb_name&gt; USING &lt;stb_name&gt; TAGS (&lt;tag1_value&gt;, ...) VALUES (field_value, ...) (field_value, ...) ... &lt;tb_name2&gt; USING &lt;stb_name2&gt; TAGS(&lt;tag1_value2&gt;, ...) VALUES (&lt;field1_value1&gt;, ...) ...;</code></pre> <pre><code class="mysql language-mysql">INSERT INTO &lt;tb_name&gt; USING &lt;stb_name&gt; TAGS (&lt;tag1_value&gt;, ...) VALUES (field_value, ...) (field_value, ...) ... &lt;tb_name2&gt; USING &lt;stb_name2&gt; TAGS(&lt;tag1_value2&gt;, ...) VALUES (&lt;field1_value1&gt;, ...) ...;</code></pre>
<p>When inserting data points into table tb_name, the system will check if table tb_name is created or not. If it is already created, the data points will be inserted as usual. But if the table is not created yet, the system will create the table tb_bame using STable stb_name as the template with the tags. Multiple tables can be specified in the SQL statement. </p> <p>When inserting data points into table tb_name, the system will check if table tb_name is created or not. If it is already created, the data points will be inserted as usual. But if the table is not created yet, the system will create the table tb_bame using STable stb_name as the template with the tags. Multiple tables can be specified in the SQL statement. </p>
<a class='anchor' id='Management-of-STables'></a><h2>Management of STables</h2> <a class='anchor' id='Management-of-STables'></a><h2>Management of STables</h2>
<p>After you can create a STable, you can describe, delete, change STables. This section lists all the supported operations.</p> <p>After you can create a STable, you can describe, delete, change STables. This section lists all the supported operations.</p>
<a class='anchor' id='Show-STables-in-current-DB'></a><h3>Show STables in current DB</h3> <a class='anchor' id='Show-STables-in-current-DB'></a><h3>Show STables in current DB</h3>
<pre><code class='language-mysql' lang='mysql'>show stables;</code></pre> <pre><code class="mysql language-mysql">show stables;</code></pre>
<p>It lists all STables in current DB, including the name, created time, number of fileds, number of tags, and number of tables which are created via this STable. </p> <p>It lists all STables in current DB, including the name, created time, number of fileds, number of tags, and number of tables which are created via this STable. </p>
<a class='anchor' id='Describe-a-STable'></a><h3>Describe a STable</h3> <a class='anchor' id='Describe-a-STable'></a><h3>Describe a STable</h3>
<pre><code class='language-mysql' lang='mysql'>DESCRIBE &lt;stable_name&gt;</code></pre> <pre><code class="mysql language-mysql">DESCRIBE &lt;stable_name&gt;</code></pre>
<p>It lists the STable&#39;s schema and tags</p> <p>It lists the STable's schema and tags</p>
<a class='anchor' id='Drop-a-STable'></a><h3>Drop a STable</h3> <a class='anchor' id='Drop-a-STable'></a><h3>Drop a STable</h3>
<pre><code class='language-mysql' lang='mysql'>DROP TABLE &lt;stable_name&gt;</code></pre> <pre><code class="mysql language-mysql">DROP TABLE &lt;stable_name&gt;</code></pre>
<p>To delete a STable, all the tables created via this STable shall be deleted first, otherwise, it will fail.</p> <p>To delete a STable, all the tables created via this STable shall be deleted first, otherwise, it will fail.</p>
<a class='anchor' id='List-the-Associated-Tables-of-a-STable'></a><h3>List the Associated Tables of a STable</h3> <a class='anchor' id='List-the-Associated-Tables-of-a-STable'></a><h3>List the Associated Tables of a STable</h3>
<pre><code class='language-mysql' lang='mysql'>SELECT TBNAME,[TAG_NAME,…] FROM &lt;stable_name&gt; WHERE &lt;tag_name&gt; &lt;[=|=&lt;|&gt;=|&lt;&gt;] values..&gt; ([AND|OR] …)</code></pre> <pre><code class="mysql language-mysql">SELECT TBNAME,[TAG_NAME,…] FROM &lt;stable_name&gt; WHERE &lt;tag_name&gt; &lt;[=|=&lt;|&gt;=|&lt;&gt;] values..&gt; ([AND|OR] …)</code></pre>
<p>It will list all the tables which satisfy the tag filter conditions. The tables are all created from this specific STable. TBNAME is a new keyword introduced, it is the table name associated with the STable. </p> <p>It will list all the tables which satisfy the tag filter conditions. The tables are all created from this specific STable. TBNAME is a new keyword introduced, it is the table name associated with the STable. </p>
<pre><code class='language-mysql' lang='mysql'>SELECT COUNT(TBNAME) FROM &lt;stable_name&gt; WHERE &lt;tag_name&gt; &lt;[=|=&lt;|&gt;=|&lt;&gt;] values..&gt; ([AND|OR] …)</code></pre> <pre><code class="mysql language-mysql">SELECT COUNT(TBNAME) FROM &lt;stable_name&gt; WHERE &lt;tag_name&gt; &lt;[=|=&lt;|&gt;=|&lt;&gt;] values..&gt; ([AND|OR] …)</code></pre>
<p>The above SQL statement will list the number of tables in a STable, which satisfy the filter condition.</p> <p>The above SQL statement will list the number of tables in a STable, which satisfy the filter condition.</p>
<a class='anchor' id='Management-of-Tags'></a><h2>Management of Tags</h2> <a class='anchor' id='Management-of-Tags'></a><h2>Management of Tags</h2>
<p>You can add, delete and change the tags for a STable, and you can change the tag value of a table. The SQL commands are listed below. </p> <p>You can add, delete and change the tags for a STable, and you can change the tag value of a table. The SQL commands are listed below. </p>
<a class='anchor' id='Add-a-Tag'></a><h3>Add a Tag</h3> <a class='anchor' id='Add-a-Tag'></a><h3>Add a Tag</h3>
<pre><code class='language-mysql' lang='mysql'>ALTER TABLE &lt;stable_name&gt; ADD TAG &lt;new_tag_name&gt; &lt;TYPE&gt;</code></pre> <pre><code class="mysql language-mysql">ALTER TABLE &lt;stable_name&gt; ADD TAG &lt;new_tag_name&gt; &lt;TYPE&gt;</code></pre>
<p>It adds a new tag to the STable with a data type. The maximum number of tags is 6. </p> <p>It adds a new tag to the STable with a data type. The maximum number of tags is 6. </p>
<a class='anchor' id='Drop-a-Tag'></a><h3>Drop a Tag</h3> <a class='anchor' id='Drop-a-Tag'></a><h3>Drop a Tag</h3>
<pre><code class='language-mysql' lang='mysql'>ALTER TABLE &lt;stable_name&gt; DROP TAG &lt;tag_name&gt;</code></pre> <pre><code class="mysql language-mysql">ALTER TABLE &lt;stable_name&gt; DROP TAG &lt;tag_name&gt;</code></pre>
<p>It drops a tag from a STable. The first tag could not be deleted, and there must be at least one tag.</p> <p>It drops a tag from a STable. The first tag could not be deleted, and there must be at least one tag.</p>
<a class='anchor' id='Change-a-Tag&#39;s-Name'></a><h3>Change a Tag&#39;s Name</h3> <a class='anchor' id='Change-a-Tag's-Name'></a><h3>Change a Tag's Name</h3>
<pre><code class='language-mysql' lang='mysql'>ALTER TABLE &lt;stable_name&gt; CHANGE TAG &lt;old_tag_name&gt; &lt;new_tag_name&gt;</code></pre> <pre><code class="mysql language-mysql">ALTER TABLE &lt;stable_name&gt; CHANGE TAG &lt;old_tag_name&gt; &lt;new_tag_name&gt;</code></pre>
<p>It changes the name of a tag from old to new. </p> <p>It changes the name of a tag from old to new. </p>
<a class='anchor' id='Change-the-Tag&#39;s-Value'></a><h3>Change the Tag&#39;s Value</h3> <a class='anchor' id='Change-the-Tag's-Value'></a><h3>Change the Tag's Value</h3>
<pre><code class='language-mysql' lang='mysql'>ALTER TABLE &lt;table_name&gt; SET TAG &lt;tag_name&gt;=&lt;new_tag_value&gt;</code></pre> <pre><code class="mysql language-mysql">ALTER TABLE &lt;table_name&gt; SET TAG &lt;tag_name&gt;=&lt;new_tag_value&gt;</code></pre>
<p>It changes a table&#39;s tag value to a new one. </p> <p>It changes a table's tag value to a new one. </p><a href='../index.html'>Back</a></section></main></div><?php include($s.'/footer.php'); ?><script>$('pre').addClass('prettyprint linenums');PR.prettyPrint()</script><script src='lib/docs/liner.js'></script></body></html>
<a href='../index.html'>Back</a></section></main></div><?php include($s.'/footer.php'); ?><script>$('pre').addClass('prettyprint linenums');PR.prettyPrint()</script><script src='lib/docs/liner.js'></script></body></html> \ No newline at end of file
\ No newline at end of file
...@@ -25,7 +25,7 @@ function convertTimestamp(data, num_of_rows, nbytes = 0, offset = 0, micro=false ...@@ -25,7 +25,7 @@ function convertTimestamp(data, num_of_rows, nbytes = 0, offset = 0, micro=false
if (micro == true) { if (micro == true) {
timestampConverter = convertMicrosecondsToDatetime; timestampConverter = convertMicrosecondsToDatetime;
} }
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = []; let res = [];
let currOffset = 0; let currOffset = 0;
while (currOffset < data.length) { while (currOffset < data.length) {
...@@ -46,7 +46,7 @@ function convertTimestamp(data, num_of_rows, nbytes = 0, offset = 0, micro=false ...@@ -46,7 +46,7 @@ function convertTimestamp(data, num_of_rows, nbytes = 0, offset = 0, micro=false
return res; return res;
} }
function convertBool(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { function convertBool(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = new Array(data.length); let res = new Array(data.length);
for (let i = 0; i < data.length; i++) { for (let i = 0; i < data.length; i++) {
if (data[i] == 0) { if (data[i] == 0) {
...@@ -59,7 +59,7 @@ function convertBool(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { ...@@ -59,7 +59,7 @@ function convertBool(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
return res; return res;
} }
function convertTinyint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { function convertTinyint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = []; let res = [];
let currOffset = 0; let currOffset = 0;
while (currOffset < data.length) { while (currOffset < data.length) {
...@@ -69,7 +69,7 @@ function convertTinyint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) ...@@ -69,7 +69,7 @@ function convertTinyint(data, num_of_rows, nbytes = 0, offset = 0, micro=false)
return res; return res;
} }
function convertSmallint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { function convertSmallint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = []; let res = [];
let currOffset = 0; let currOffset = 0;
while (currOffset < data.length) { while (currOffset < data.length) {
...@@ -79,7 +79,7 @@ function convertSmallint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) ...@@ -79,7 +79,7 @@ function convertSmallint(data, num_of_rows, nbytes = 0, offset = 0, micro=false)
return res; return res;
} }
function convertInt(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { function convertInt(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = []; let res = [];
let currOffset = 0; let currOffset = 0;
while (currOffset < data.length) { while (currOffset < data.length) {
...@@ -98,7 +98,7 @@ function readBigInt64LE(buffer, offset = 0) { ...@@ -98,7 +98,7 @@ function readBigInt64LE(buffer, offset = 0) {
return ((BigInt(val) << 32n) + BigInt(first + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + buffer[++offset] * 2 ** 24)); return ((BigInt(val) << 32n) + BigInt(first + buffer[++offset] * 2 ** 8 + buffer[++offset] * 2 ** 16 + buffer[++offset] * 2 ** 24));
} }
function convertBigint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { function convertBigint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = []; let res = [];
let currOffset = 0; let currOffset = 0;
while (currOffset < data.length) { while (currOffset < data.length) {
...@@ -108,7 +108,7 @@ function convertBigint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { ...@@ -108,7 +108,7 @@ function convertBigint(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
return res; return res;
} }
function convertFloat(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { function convertFloat(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = []; let res = [];
let currOffset = 0; let currOffset = 0;
while (currOffset < data.length) { while (currOffset < data.length) {
...@@ -118,7 +118,7 @@ function convertFloat(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { ...@@ -118,7 +118,7 @@ function convertFloat(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
return res; return res;
} }
function convertDouble(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { function convertDouble(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = []; let res = [];
let currOffset = 0; let currOffset = 0;
while (currOffset < data.length) { while (currOffset < data.length) {
...@@ -128,7 +128,7 @@ function convertDouble(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { ...@@ -128,7 +128,7 @@ function convertDouble(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
return res; return res;
} }
function convertBinary(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { function convertBinary(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = []; let res = [];
let currOffset = 0; let currOffset = 0;
while (currOffset < data.length) { while (currOffset < data.length) {
...@@ -139,7 +139,7 @@ function convertBinary(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { ...@@ -139,7 +139,7 @@ function convertBinary(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
return res; return res;
} }
function convertNchar(data, num_of_rows, nbytes = 0, offset = 0, micro=false) { function convertNchar(data, num_of_rows, nbytes = 0, offset = 0, micro=false) {
data = ref.reinterpret(data.deref().deref(), nbytes * num_of_rows, offset); data = ref.reinterpret(data.deref(), nbytes * num_of_rows, offset);
let res = []; let res = [];
let currOffset = 0; let currOffset = 0;
//every 4; //every 4;
...@@ -185,7 +185,9 @@ TaosField.defineProperty('type', ref.types.char); ...@@ -185,7 +185,9 @@ TaosField.defineProperty('type', ref.types.char);
function CTaosInterface (config = null, pass = false) { function CTaosInterface (config = null, pass = false) {
ref.types.char_ptr = ref.refType(ref.types.char); ref.types.char_ptr = ref.refType(ref.types.char);
ref.types.void_ptr = ref.refType(ref.types.void); ref.types.void_ptr = ref.refType(ref.types.void);
ref.types.void_ptr2 = ref.refType(ref.types.void_ptr);
/*Declare a bunch of functions first*/ /*Declare a bunch of functions first*/
/* Note, pointers to TAOS_RES, TAOS, are ref.types.void_ptr. The connection._conn buffer is supplied for pointers to TAOS */
this.libtaos = ffi.Library('libtaos', { this.libtaos = ffi.Library('libtaos', {
'taos_options': [ ref.types.int, [ ref.types.int , ref.types.void_ptr ] ], 'taos_options': [ ref.types.int, [ ref.types.int , ref.types.void_ptr ] ],
'taos_init': [ ref.types.void, [ ] ], 'taos_init': [ ref.types.void, [ ] ],
...@@ -201,6 +203,11 @@ function CTaosInterface (config = null, pass = false) { ...@@ -201,6 +203,11 @@ function CTaosInterface (config = null, pass = false) {
'taos_affected_rows': [ ref.types.int, [ ref.types.void_ptr] ], 'taos_affected_rows': [ ref.types.int, [ ref.types.void_ptr] ],
//int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows) //int taos_fetch_block(TAOS_RES *res, TAOS_ROW *rows)
'taos_fetch_block': [ ref.types.int, [ ref.types.void_ptr, ref.types.void_ptr] ], 'taos_fetch_block': [ ref.types.int, [ ref.types.void_ptr, ref.types.void_ptr] ],
//int taos_num_fields(TAOS_RES *res);
'taos_num_fields': [ ref.types.int, [ ref.types.void_ptr] ],
//TAOS_ROW taos_fetch_row(TAOS_RES *res)
//TAOS_ROW is void **, but we set the return type as a reference instead to get the row
'taos_fetch_row': [ ref.refType(ref.types.void_ptr2), [ ref.types.void_ptr ] ],
//int taos_result_precision(TAOS_RES *res) //int taos_result_precision(TAOS_RES *res)
'taos_result_precision': [ ref.types.int, [ ref.types.void_ptr ] ], 'taos_result_precision': [ ref.types.int, [ ref.types.void_ptr ] ],
//void taos_free_result(TAOS_RES *res) //void taos_free_result(TAOS_RES *res)
...@@ -212,7 +219,13 @@ function CTaosInterface (config = null, pass = false) { ...@@ -212,7 +219,13 @@ function CTaosInterface (config = null, pass = false) {
//int taos_errno(TAOS *taos) //int taos_errno(TAOS *taos)
'taos_errno': [ ref.types.int, [ ref.types.void_ptr] ], 'taos_errno': [ ref.types.int, [ ref.types.void_ptr] ],
//char *taos_errstr(TAOS *taos) //char *taos_errstr(TAOS *taos)
'taos_errstr': [ ref.types.char, [ ref.types.void_ptr] ] 'taos_errstr': [ ref.types.char, [ ref.types.void_ptr] ],
// ASYNC
// void taos_query_a(TAOS *taos, char *sqlstr, void (*fp)(void *, TAOS_RES *, int), void *param)
'taos_query_a': [ ref.types.void, [ ref.types.void_ptr, ref.types.char_ptr, ref.types.void_ptr, ref.types.void_ptr ] ],
// void taos_fetch_rows_a(TAOS_RES *res, void (*fp)(void *param, TAOS_RES *, int numOfRows), void *param);
'taos_fetch_rows_a': [ ref.types.void, [ ref.types.void_ptr, ref.types.void_ptr, ref.types.void_ptr ]]
}); });
if (pass == false) { if (pass == false) {
if (config == null) { if (config == null) {
...@@ -293,20 +306,20 @@ CTaosInterface.prototype.useResult = function useResult(connection) { ...@@ -293,20 +306,20 @@ CTaosInterface.prototype.useResult = function useResult(connection) {
let fields = []; let fields = [];
let pfields = this.fetchFields(result); let pfields = this.fetchFields(result);
if (ref.isNull(pfields) == false) { if (ref.isNull(pfields) == false) {
let fullpfields = ref.reinterpret(pfields, this.fieldsCount(connection) * 68, 0); pfields = ref.reinterpret(pfields, this.fieldsCount(connection) * 68, 0);
for (let i = 0; i < fullpfields.length; i += 68) { for (let i = 0; i < pfields.length; i += 68) {
//0 - 63 = name //64 - 65 = bytes, 66 - 67 = type //0 - 63 = name //64 - 65 = bytes, 66 - 67 = type
fields.push( { fields.push( {
name: ref.readCString(ref.reinterpret(fullpfields,64,i)), name: ref.readCString(ref.reinterpret(pfields,64,i)),
bytes: fullpfields[i + 64], bytes: pfields[i + 64],
type: fullpfields[i + 66] type: pfields[i + 66]
}) })
} }
} }
return {result:result, fields:fields} return {result:result, fields:fields}
} }
CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) { CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) {
let pblock = ref.ref(ref.ref(ref.NULL)); let pblock = ref.ref(ref.ref(ref.NULL)); // equal to our raw data
let num_of_rows = this.libtaos.taos_fetch_block(result, pblock) let num_of_rows = this.libtaos.taos_fetch_block(result, pblock)
if (num_of_rows == 0) { if (num_of_rows == 0) {
return {block:null, num_of_rows:0}; return {block:null, num_of_rows:0};
...@@ -316,21 +329,30 @@ CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) { ...@@ -316,21 +329,30 @@ CTaosInterface.prototype.fetchBlock = function fetchBlock(result, fields) {
blocks.fill(null); blocks.fill(null);
num_of_rows = Math.abs(num_of_rows); num_of_rows = Math.abs(num_of_rows);
let offset = 0; let offset = 0;
pblock = pblock.deref()
for (let i = 0; i < fields.length; i++) { for (let i = 0; i < fields.length; i++) {
if (!convertFunctions[fields[i]['type']] ) { if (!convertFunctions[fields[i]['type']] ) {
throw new errors.DatabaseError("Invalid data type returned from database"); throw new errors.DatabaseError("Invalid data type returned from database");
} }
let data = ref.reinterpret(pblock.deref().deref(), fields[i]['bytes'], offset);
blocks[i] = convertFunctions[fields[i]['type']](pblock, num_of_rows, fields[i]['bytes'], offset, isMicro); blocks[i] = convertFunctions[fields[i]['type']](pblock, num_of_rows, fields[i]['bytes'], offset, isMicro);
offset += fields[i]['bytes'] * num_of_rows; offset += fields[i]['bytes'] * num_of_rows;
} }
return {blocks: blocks, num_of_rows:Math.abs(num_of_rows)} return {blocks: blocks, num_of_rows:Math.abs(num_of_rows)}
} }
CTaosInterface.prototype.fetchRow = function fetchRow(result, fields) {
let row = this.libtaos.taos_fetch_row(result);
return row;
}
CTaosInterface.prototype.freeResult = function freeResult(result) { CTaosInterface.prototype.freeResult = function freeResult(result) {
this.libtaos.taos_free_result(result); this.libtaos.taos_free_result(result);
result = null; result = null;
} }
/** Number of fields returned in this result handle, must use with async */
CTaosInterface.prototype.numFields = function numFields(result) {
return this.libtaos.taos_num_fields(result);
}
/** @deprecated */
CTaosInterface.prototype.fieldsCount = function fieldsCount(connection) { CTaosInterface.prototype.fieldsCount = function fieldsCount(connection) {
return this.libtaos.taos_field_count(connection); return this.libtaos.taos_field_count(connection);
} }
...@@ -341,5 +363,63 @@ CTaosInterface.prototype.errno = function errno(connection) { ...@@ -341,5 +363,63 @@ CTaosInterface.prototype.errno = function errno(connection) {
return this.libtaos.taos_errno(connection); return this.libtaos.taos_errno(connection);
} }
CTaosInterface.prototype.errStr = function errStr(connection) { CTaosInterface.prototype.errStr = function errStr(connection) {
return (this.libtaos.taos_errstr(connection)); return this.libtaos.taos_errstr(connection);
}
// Async
CTaosInterface.prototype.query_a = function query_a(connection, sql, callback, param = ref.ref(ref.NULL)) {
// void taos_query_a(TAOS *taos, char *sqlstr, void (*fp)(void *param, TAOS_RES *, int), void *param)
callback = ffi.Callback(ref.types.void, [ ref.types.void_ptr, ref.types.void_ptr, ref.types.int ], callback);
this.libtaos.taos_query_a(connection, ref.allocCString(sql), callback, param);
return param;
}
/** Asynchrnously fetches the next block of rows. Wraps callback and transfers a 4th argument to the cursor, the row data as blocks in javascript form
* Note: This isn't a recursive function, in order to fetch all data either use the TDengine cursor object, TaosQuery object, or implement a recrusive
* function yourself using the libtaos.taos_fetch_rows_a function
*/
CTaosInterface.prototype.fetch_rows_a = function fetch_rows_a(result, callback, param = ref.ref(ref.NULL)) {
// void taos_fetch_rows_a(TAOS_RES *res, void (*fp)(void *param, TAOS_RES *, int numOfRows), void *param);
var cti = this;
// wrap callback with a function so interface can access the numOfRows value, needed in order to properly process the binary data
let asyncCallbackWrapper = function (param2, result2, numOfRows2) {
// Data preparation to pass to cursor. Could be bottleneck in query execution callback times.
let row = cti.libtaos.taos_fetch_row(result2);
let fields = cti.fetchFields_a(result2);
let isMicro = (cti.libtaos.taos_result_precision(result) == FieldTypes.C_TIMESTAMP_MICRO);
let blocks = new Array(fields.length);
blocks.fill(null);
numOfRows2 = Math.abs(numOfRows2);
let offset = 0;
if (numOfRows2 > 0){
for (let i = 0; i < fields.length; i++) {
if (!convertFunctions[fields[i]['type']] ) {
throw new errors.DatabaseError("Invalid data type returned from database");
}
blocks[i] = convertFunctions[fields[i]['type']](row, numOfRows2, fields[i]['bytes'], offset, isMicro);
offset += fields[i]['bytes'] * numOfRows2;
}
}
callback(param2, result2, numOfRows2, blocks);
}
asyncCallbackWrapper = ffi.Callback(ref.types.void, [ ref.types.void_ptr, ref.types.void_ptr, ref.types.int], asyncCallbackWrapper);
this.libtaos.taos_fetch_rows_a(result, asyncCallbackWrapper, param);
return param;
}
// Fetch field meta data by result handle
CTaosInterface.prototype.fetchFields_a = function fetchFields_a (result) {
//
let pfields = this.fetchFields(result);
let pfieldscount = this.numFields(result);
let fields = [];
if (ref.isNull(pfields) == false) {
pfields = ref.reinterpret(pfields, 68 * pfieldscount , 0);
for (let i = 0; i < pfields.length; i += 68) {
//0 - 63 = name //64 - 65 = bytes, 66 - 67 = type
fields.push( {
name: ref.readCString(ref.reinterpret(pfields,64,i)),
bytes: pfields[i + 64],
type: pfields[i + 66]
})
}
}
return fields;
} }
const ref = require('ref');
require('./globalfunc.js') require('./globalfunc.js')
const CTaosInterface = require('./cinterface') const CTaosInterface = require('./cinterface')
const errors = require ('./error') const errors = require ('./error')
...@@ -22,6 +23,7 @@ module.exports = TDengineCursor; ...@@ -22,6 +23,7 @@ module.exports = TDengineCursor;
* @since 1.0.0 * @since 1.0.0
*/ */
function TDengineCursor(connection=null) { function TDengineCursor(connection=null) {
//All parameters are store for sync queries only.
this._description = null; this._description = null;
this._rowcount = -1; this._rowcount = -1;
this._connection = null; this._connection = null;
...@@ -94,6 +96,7 @@ TDengineCursor.prototype.query = function query(operation, execute = false) { ...@@ -94,6 +96,7 @@ TDengineCursor.prototype.query = function query(operation, execute = false) {
*/ */
TDengineCursor.prototype.execute = function execute(operation, options, callback) { TDengineCursor.prototype.execute = function execute(operation, options, callback) {
if (operation == undefined) { if (operation == undefined) {
throw new errors.ProgrammingError('No operation passed as argument');
return null; return null;
} }
...@@ -115,9 +118,8 @@ TDengineCursor.prototype.execute = function execute(operation, options, callback ...@@ -115,9 +118,8 @@ TDengineCursor.prototype.execute = function execute(operation, options, callback
}); });
obs.observe({ entryTypes: ['measure'] }); obs.observe({ entryTypes: ['measure'] });
performance.mark('A'); performance.mark('A');
performance.mark('B');
res = this._chandle.query(this._connection._conn, stmt); res = this._chandle.query(this._connection._conn, stmt);
performance.mark('B');
performance.measure('query', 'A', 'B'); performance.measure('query', 'A', 'B');
if (res == 0) { if (res == 0) {
...@@ -180,7 +182,6 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) { ...@@ -180,7 +182,6 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) {
let data = []; let data = [];
this._rowcount = 0; this._rowcount = 0;
let k = 0;
//let nodetime = 0; //let nodetime = 0;
let time = 0; let time = 0;
const obs = new PerformanceObserver((items) => { const obs = new PerformanceObserver((items) => {
...@@ -195,13 +196,12 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) { ...@@ -195,13 +196,12 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) {
obs2.observe({ entryTypes: ['measure'] }); obs2.observe({ entryTypes: ['measure'] });
performance.mark('nodea'); performance.mark('nodea');
*/ */
obs.observe({ entryTypes: ['measure'] });
performance.mark('A');
while(true) { while(true) {
k+=1;
obs.observe({ entryTypes: ['measure'] });
performance.mark('A');
let blockAndRows = this._chandle.fetchBlock(this._result, this._fields); let blockAndRows = this._chandle.fetchBlock(this._result, this._fields);
performance.mark('B');
performance.measure('query', 'A', 'B');
let block = blockAndRows.blocks; let block = blockAndRows.blocks;
let num_of_rows = blockAndRows.num_of_rows; let num_of_rows = blockAndRows.num_of_rows;
...@@ -217,7 +217,10 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) { ...@@ -217,7 +217,10 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) {
} }
data[data.length-1] = (rowBlock); data[data.length-1] = (rowBlock);
} }
} }
performance.mark('B');
performance.measure('query', 'A', 'B');
let response = this._createSetResponse(this._rowcount, time) let response = this._createSetResponse(this._rowcount, time)
console.log(response); console.log(response);
...@@ -226,13 +229,154 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) { ...@@ -226,13 +229,154 @@ TDengineCursor.prototype.fetchall = function fetchall(options, callback) {
this._reset_result(); this._reset_result();
this.data = data; this.data = data;
this.fields = fields; this.fields = fields;
//performance.mark('nodeb');
//performance.measure('querynode', 'nodea', 'nodeb');
//console.log('nodetime: ' + nodetime/1000);
wrapCB(callback, data); wrapCB(callback, data);
return data; return data;
} }
/**
* Asynchrnously execute a query to TDengine. NOTE, insertion requests must be done in sync if on the same table.
* @param {string} operation - The query operation to execute in the taos shell
* @param {Object} options - Execution options object. quiet : true turns off logging from queries
* @param {boolean} options.quiet - True if you want to surpress logging such as "Query OK, 1 row(s) ..."
* @param {function} callback - A callback function to execute after the query is made to TDengine
* @return {number | Buffer} Number of affected rows or a Buffer that points to the results of the query
* @since 1.0.0
*/
TDengineCursor.prototype.execute_a = function execute_a (operation, options, callback, param) {
if (operation == undefined) {
throw new errors.ProgrammingError('No operation passed as argument');
return null;
}
if (typeof options == 'function') {
//we expect the parameter after callback to be param
param = callback;
callback = options;
}
if (typeof options != 'object') options = {}
if (this._connection == null) {
throw new errors.ProgrammingError('Cursor is not connected');
}
if (typeof callback != 'function') {
throw new errors.ProgrammingError("No callback function passed to execute_a function");
}
// Async wrapper for callback;
var cr = this;
let asyncCallbackWrapper = function (param2, res2, resCode) {
if (typeof callback == 'function') {
callback(param2, res2, resCode);
}
if (resCode >= 0) {
let fieldCount = cr._chandle.numFields(res2);
if (fieldCount == 0) {
//get affect fields count
cr._chandle.freeResult(res2); //result will no longer be needed
}
else {
return res2;
}
}
else {
//new errors.ProgrammingError(this._chandle.errStr(this._connection._conn))
//how to get error by result handle?
throw new errors.ProgrammingError("Error occuring with use of execute_a async function. Status code was returned with failure");
}
}
this._connection._clearResultSet();
let stmt = operation;
let time = 0;
// Use ref module to write to buffer in cursor.js instead of taosquery to maintain a difference in levels. Have taosquery stay high level
// through letting it pass an object as param
var buf = ref.alloc('Object');
ref.writeObject(buf, 0, param);
const obs = new PerformanceObserver((items) => {
time = items.getEntries()[0].duration;
performance.clearMarks();
});
obs.observe({ entryTypes: ['measure'] });
performance.mark('A');
this._chandle.query_a(this._connection._conn, stmt, asyncCallbackWrapper, buf);
performance.mark('B');
performance.measure('query', 'A', 'B');
return param;
}
/**
* Fetches all results from an async query. It is preferable to use cursor.query_a() to create
* async queries and execute them instead of using the cursor object directly.
* @param {Object} options - An options object containing options for this function
* @param {function} callback - callback function that is callbacked on the COMPLETE fetched data (it is calledback only once!).
* Must be of form function (param, result, rowCount, rowData)
* @param {Object} param - A parameter that is also passed to the main callback function. Important! Param must be an object, and the key "data" cannot be used
* @return {{param:Object, result:buffer}} An object with the passed parameters object and the buffer instance that is a pointer to the result handle.
* @since 1.2.0
* @example
* cursor.execute('select * from db.table');
* var data = cursor.fetchall(function(results) {
* results.forEach(row => console.log(row));
* })
*/
TDengineCursor.prototype.fetchall_a = function fetchall_a(result, options, callback, param = {}) {
if (typeof options == 'function') {
//we expect the parameter after callback to be param
param = callback;
callback = options;
}
if (typeof options != 'object') options = {}
if (this._connection == null) {
throw new errors.ProgrammingError('Cursor is not connected');
}
if (typeof callback != 'function') {
throw new errors.ProgrammingError('No callback function passed to fetchall_a function')
}
if (param.data) {
throw new errors.ProgrammingError("You aren't allowed to set the key 'data' for the parameters object");
}
let buf = ref.alloc('Object');
param.data = [];
var cr = this;
// This callback wrapper accumulates the data from the fetch_rows_a function from the cinterface. It is accumulated by passing the param2
// object which holds accumulated data in the data key.
let asyncCallbackWrapper = function asyncCallbackWrapper(param2, result2, numOfRows2, rowData) {
param2 = ref.readObject(param2); //return the object back from the pointer
// Keep fetching until now rows left.
if (numOfRows2 > 0) {
let buf2 = ref.alloc('Object');
param2.data.push(rowData);
ref.writeObject(buf2, 0, param2);
cr._chandle.fetch_rows_a(result2, asyncCallbackWrapper, buf2);
}
else {
let finalData = param2.data;
let fields = cr._chandle.fetchFields_a(result2);
let data = [];
for (let i = 0; i < finalData.length; i++) {
let num_of_rows = finalData[i][0].length; //fetched block number i;
let block = finalData[i];
for (let j = 0; j < num_of_rows; j++) {
data.push([]);
let rowBlock = new Array(fields.length);
for (let k = 0; k < fields.length; k++) {
rowBlock[k] = block[k][j];
}
data[data.length-1] = rowBlock;
}
}
cr._chandle.freeResult(result2); // free result, avoid seg faults and mem leaks!
callback(param2, result2, numOfRows2, {data:data,fields:fields});
}
}
ref.writeObject(buf, 0, param);
param = this._chandle.fetch_rows_a(result, asyncCallbackWrapper, buf); //returned param
return {param:param,result:result};
}
TDengineCursor.prototype.nextset = function nextset() { TDengineCursor.prototype.nextset = function nextset() {
return; return;
} }
......
/* Wrap a callback, reduce code amount */ /* Wrap a callback, reduce code amount */
function wrapCB(callback,input) { function wrapCB(callback, input) {
if (typeof callback === 'function') { if (typeof callback === 'function') {
callback(input); callback(input);
} }
......
...@@ -10,12 +10,13 @@ module.exports = TaosQuery; ...@@ -10,12 +10,13 @@ module.exports = TaosQuery;
* functionality and save time whilst also making it easier to debug and enter less problems with the use of promises. * functionality and save time whilst also making it easier to debug and enter less problems with the use of promises.
* @param {string} query - Query to construct object from * @param {string} query - Query to construct object from
* @param {TDengineCursor} cursor - The cursor from which this query will execute from * @param {TDengineCursor} cursor - The cursor from which this query will execute from
* @param {boolean} execute - Whether or not to immedietely execute the query and fetch all results. Default is false. * @param {boolean} execute - Whether or not to immedietely execute the query synchronously and fetch all results. Default is false.
* @property {string} query - The current query in string format the TaosQuery object represents
* @return {TaosQuery} * @return {TaosQuery}
* @since 1.0.6 * @since 1.0.6
*/ */
function TaosQuery(query = "", cursor = null, execute = false) { function TaosQuery(query = "", cursor = null, execute = false) {
this._query = query; this.query = query;
this._cursor = cursor; this._cursor = cursor;
if (execute == true) { if (execute == true) {
return this.execute(); return this.execute();
...@@ -36,7 +37,7 @@ TaosQuery.prototype.execute = async function execute() { ...@@ -36,7 +37,7 @@ TaosQuery.prototype.execute = async function execute() {
let fields = []; let fields = [];
let result; let result;
try { try {
taosQuery._cursor.execute(taosQuery._query); taosQuery._cursor.execute(taosQuery.query);
if (taosQuery._cursor._fields) fields = taosQuery._cursor._fields; if (taosQuery._cursor._fields) fields = taosQuery._cursor._fields;
if (taosQuery._cursor._result != null) data = taosQuery._cursor.fetchall(); if (taosQuery._cursor._result != null) data = taosQuery._cursor.fetchall();
result = new TaosResult(data, fields) result = new TaosResult(data, fields)
...@@ -50,6 +51,42 @@ TaosQuery.prototype.execute = async function execute() { ...@@ -50,6 +51,42 @@ TaosQuery.prototype.execute = async function execute() {
return executionPromise; return executionPromise;
} }
/**
* Executes the query object asynchronously and returns a Promise. Completes query to completion.
* @memberof TaosQuery
* @param {Object} options - Execution options
* @return {Promise<TaosResult>} A promise that resolves with a TaosResult object, or rejects with an error
* @since 1.2.0
*/
TaosQuery.prototype.execute_a = async function execute_a(options = {}) {
var executionPromise = new Promise( (resolve, reject) => {
});
var fres;
var frej;
var fetchPromise = new Promise( (resolve, reject) => {
fres = resolve;
frej = reject;
});
let asyncCallbackFetchall = async function(param, res, numOfRows, blocks) {
//param is expected to be the fetchPromise variable;
//keep fetching until completion, possibly an issue though
if (numOfRows > 0) {
frej("cursor.fetchall_a didn't fetch all data properly");
}
else {
fres(new TaosResult(blocks.data, blocks.fields));
}
}
let asyncCallback = async function(param, res, code) {
//upon success, we fetchall results
this._cursor.fetchall_a(res, options, asyncCallbackFetchall, {});
}
this._cursor.execute_a(this.query, asyncCallback.bind(this), {});
return fetchPromise;
}
/** /**
* Bind arguments to the query and automatically parses them into the right format * Bind arguments to the query and automatically parses them into the right format
* @param {array | ...args} args - A number of arguments to bind to each ? in the query * @param {array | ...args} args - A number of arguments to bind to each ? in the query
...@@ -71,7 +108,7 @@ TaosQuery.prototype.bind = function bind(f, ...args) { ...@@ -71,7 +108,7 @@ TaosQuery.prototype.bind = function bind(f, ...args) {
if (arg.constructor.name == 'TaosTimestamp') arg = "\"" + arg.toTaosString() + "\""; if (arg.constructor.name == 'TaosTimestamp') arg = "\"" + arg.toTaosString() + "\"";
else if (arg.constructor.name == 'Date') arg = "\"" + toTaosTSString(arg) + "\""; else if (arg.constructor.name == 'Date') arg = "\"" + toTaosTSString(arg) + "\"";
else if (typeof arg == 'string') arg = "\"" + arg + "\""; else if (typeof arg == 'string') arg = "\"" + arg + "\"";
this._query = this._query.replace(/\?/,arg); this.query = this.query.replace(/\?/,arg);
}, this); }, this);
return this; return this;
} }
...@@ -42,7 +42,7 @@ TaosResult.prototype.pretty = function pretty() { ...@@ -42,7 +42,7 @@ TaosResult.prototype.pretty = function pretty() {
else { else {
sizing.push(Math.max(field.name.length, suggestedMinWidths[field._field.type])); sizing.push(Math.max(field.name.length, suggestedMinWidths[field._field.type]));
} }
fieldsStr +=fillEmpty(Math.floor(sizing[i]/2 - field.name.length / 2)) + field.name + fillEmpty(Math.ceil(sizing[i]/2 - field.name.length / 2)) + " | "; fieldsStr += fillEmpty(Math.floor(sizing[i]/2 - field.name.length / 2)) + field.name + fillEmpty(Math.ceil(sizing[i]/2 - field.name.length / 2)) + " | ";
}); });
var sumLengths = sizing.reduce((a,b)=> a+=b,(0)) + sizing.length * 3; var sumLengths = sizing.reduce((a,b)=> a+=b,(0)) + sizing.length * 3;
......
{ {
"name": "td-connector", "name": "td-connector",
"version": "1.1.1", "version": "1.2.0",
"lockfileVersion": 1, "lockfileVersion": 1,
"requires": true, "requires": true,
"dependencies": { "dependencies": {
......
{ {
"name": "td-connector", "name": "td-connector",
"version": "1.1.1", "version": "1.2.0",
"description": "A Node.js connector for TDengine.", "description": "A Node.js connector for TDengine.",
"main": "tdengine.js", "main": "tdengine.js",
"scripts": { "scripts": {
......
...@@ -106,7 +106,7 @@ promise.then(function(result) { ...@@ -106,7 +106,7 @@ promise.then(function(result) {
You can also query by binding parameters to a query by filling in the question marks in a string as so. The query will automatically parse what was binded and convert it to the proper format for use with TDengine You can also query by binding parameters to a query by filling in the question marks in a string as so. The query will automatically parse what was binded and convert it to the proper format for use with TDengine
```javascript ```javascript
var query = cursor.query('select * from meterinfo.meters where ts <= ? and areaid = ?').bind(new Date(), 5); var query = cursor.query('select * from meterinfo.meters where ts <= ? and areaid = ?;').bind(new Date(), 5);
query.execute().then(function(result) { query.execute().then(function(result) {
result.pretty(); result.pretty();
}) })
...@@ -114,7 +114,7 @@ query.execute().then(function(result) { ...@@ -114,7 +114,7 @@ query.execute().then(function(result) {
The TaosQuery object can also be immediately executed upon creation by passing true as the second argument, returning a promise instead of a TaosQuery. The TaosQuery object can also be immediately executed upon creation by passing true as the second argument, returning a promise instead of a TaosQuery.
```javascript ```javascript
var promise = cursor.query('select * from meterinfo.meters where v1 = 30', true) var promise = cursor.query('select * from meterinfo.meters where v1 = 30;', true)
promise.then(function(result) { promise.then(function(result) {
result.pretty(); result.pretty();
}) })
...@@ -122,7 +122,7 @@ promise.then(function(result) { ...@@ -122,7 +122,7 @@ promise.then(function(result) {
If you want to execute queries without objects being wrapped around the data, use ```cursor.execute()``` directly and ```cursor.fetchall()``` to retrieve data if there is any. If you want to execute queries without objects being wrapped around the data, use ```cursor.execute()``` directly and ```cursor.fetchall()``` to retrieve data if there is any.
```javascript ```javascript
cursor.execute('select count(*), avg(v1), min(v2) from meterinfo.meters where ts >= \"2019-07-20 00:00:00.000\"'); cursor.execute('select count(*), avg(v1), min(v2) from meterinfo.meters where ts >= \"2019-07-20 00:00:00.000\";');
var data = cursor.fetchall(); var data = cursor.fetchall();
console.log(cursor.fields); // Latest query's Field metadata is stored in cursor.fields console.log(cursor.fields); // Latest query's Field metadata is stored in cursor.fields
console.log(cursor.data); // Latest query's result data is stored in cursor.data, also returned by fetchall. console.log(cursor.data); // Latest query's result data is stored in cursor.data, also returned by fetchall.
...@@ -130,7 +130,20 @@ console.log(cursor.data); // Latest query's result data is stored in cursor.data ...@@ -130,7 +130,20 @@ console.log(cursor.data); // Latest query's result data is stored in cursor.data
### Async functionality ### Async functionality
Coming soon Async queries can be performed using the same functions such as `cursor.execute`, `cursor.query`, but now with `_a` appended to them.
Say you want to execute an two async query on two seperate tables, using `cursor.query_a`, you can do that and get a TaosQuery object, which upon executing with the `execute_a` function, returns a promise that resolves with a TaosResult object.
```javascript
var promise1 = cursor.query_a('select count(*), avg(v1), avg(v2) from meter1;').execute_a()
var promise2 = cursor.query_a('select count(*), avg(v1), avg(v2) from meter2;').execute_a();
promise1.then(function(result) {
result.pretty();
})
promise2.then(function(result) {
result.pretty();
})
```
## Example ## Example
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册