概述
nvPress采用的是无数据库存储的方案:数据存储在json
文件中。
- 读写过程:服务端每次启动时将数据文件从硬盘读取到内存中进行快速的查询和修改。在发生修改行为后,再将数据写入到硬盘上。
- 延缓写入:为防止数据在短时间内发生连续多次的写入影响硬盘性能,在上一次写入后的
5秒内
,若有再次修改,则延缓5秒后再统一写入数据文件。因此在数据变更的5秒内,如果强制停止nvPress,将会导致这5秒的数据丢失。(后台重启、Docker正常关闭都不会影响)
::: tip 关于性能
nvPress适用于个人博客、内容作品展示网站。
经测试,单个 json
文件小于50M时无明显性能问题,大于50M时有轻微性能问题。超过100M时请勿使用nvPress。这种情况在上述使用场景内几乎不会出现。
1万篇文章以下不会有显著的性能问题。作为个人博客场景,可放心编写使用。
:::
NV_Database
在nvPress中负责管理上述行为的功能被称之为NV_Database
类
实例化
new NV_Database( path, {columns, index} )
path
(String) (Required)
json文件路径。文件不存在时自动创建,存在则读取
columns
(Array) (Required)
数据中可以存储的字段名。Javascript不在乎数据类型,只要是可以JSON.stringify的数据均可存储
index
(String) (Required)
数据序号名称。例如:id
threshold
(String) (Optional)
写入延迟(毫秒),默认4500。不建议自定义此参数
references
(Array[String]) (Optional)
==注意==标记可能存储Object、Array引用类型的字段名
:::: details references 参数详细说明
4.0
新增参数
为什么需要此参数
通常地,我们存储数据时候都是存储字符串、数字、布尔值等基础数据类型。这些数据类型在检索、读取的过程中并不会有什么异常。然而,在存储Object、Array的时候会出现一些意想不到的故障
何时出现故障
在使用find方法时,query参数使用function对结果过滤时将取出整行数据进行对比。如果在对比时,对数据进行了赋值操作。基础数据类型不会有影响,而引用数据类型将会污染数据到真实的数据中。
例子1:
// 定义数据格式
const path = require('path');
const db = new NV_Database (
path.join(process.cwd(),"./nv-content/data/db.json"),
{
columns: ['name','obj_col'],
index: 'id'
}
);
// 写入数据
db.insert({
name: "Panda",
obj_col: {hello: "world"},
});
// 查询数据
var result = db.find(r=>{
// 倘若在此处修改数据
r.name = "another Name"; // 由于name在上次存储的时候是基础数据类型,因此修改后不会影响到原始数据
r.obj_col.hello= "JOKER"; // 虽然这是查询的过滤操作,但这样会导致原始数据被修改
return true;
})
// 打印查看结果
console.log(result)
例子2:
// 直接查询全部结果
var result = db.find(r=>r.name == "Panda")[0];
// 对结果中的引用类型进行修改,虽然我们认为是对结果进行的修改,然而引用类型会导致原始数据被修改
result.obj_col.hello = "JOKER";
// 再次查询结果并打印
console.log(db.find(r=>r.name == "Panda")[0])
以上两个例子分别是在“过滤中”、“过滤后”对数据进行修改,引用类型都将会导致原始数据被篡改(不会立即保存到json中,在下一次使用了其他数据修改方法的时候才会保存)。这个行为容易让人感到困惑,因此使用了这个参数。
- 避免方法
在定义数据时就考虑好,将==可能==存储了“引用类型”的字段添加到references参数中,例如:
// 定义数据格式
const path = require('path');
const db = new NV_Database (
path.join(process.cwd(),"./nv-content/data/db.json"),
{
columns: ['name','obj_col'],
index: 'id',
references: ['obj_col'], // ← 看左边
}
);
这样,存储在上述字段中的类型就会在过滤时被自动解除引用(即使不是引用类型也可以添加此参数,例如你不清楚可能会存储基础类型或者引用类型的时候)。
其他问题
Q1:
如果将全部的字段都添加到references参数中是不是更好?
当然不是!在解除引用的过程中,会对性能造成影响。当数据量庞大的时候,查询效率会很低。因此,只需要将可能存储引用类型的列添加此参数即可。
Q2:
既然增加了references参数会对查询性能有影响,那么如果我能够保证【在某次查询时】不对数据修改,是否可以对单次不解除引用从而提高本次性能?
可以。查询方法query为Function时,给Function设置:_nvdb_reference = true
,即可在本次查询以及查询结果中不解除引用。但需【注意】:如果结果中包含的数据(有引用)会再次给外部使用,而外部不确定是否会对数据修改时,请务必手动使用方法nv_remove_reference
来解除引用。(详见method-post源代码)
::::
示例:
const path = require('path');
const db_customers = new NV_Database (
path.join(process.cwd(),"./nv-content/data/customers.json"),
{
columns: ['name','address','email','url'],
index: 'id'
}
);
这样就会在nvpress根目录的nv-content/data中创建一个customers.json数据文件了。每次启动的时候,也会自动读取这个文件。
实例方法
insert
插入1条数据。数据的字段只能是实例化时定义的column
中的字段名,额外的字段将会被忽略,缺少的数据会以null
替代。
用法: insert( row )
示例:
db_customers.insert({
name: "Panda",
address: "Chongqing",
email: "panda@sample-domain.com",
url: "https://panda.panda-studio.cn",
});
db_customers.insert({
name: "Bob",
address: "USA",
email: "bob@sample-domain.com",
age: 24, // 这条数据将会被忽略,不会存储
});
返回: 实例本身
find
查找数据。
用法: find( query, cols )
参数:
- query
(Function/Object) (Required)
查询条件,可以是方法,也可以是对象。
Function(row)
方法:参数是每一条数据,比对成功返回true,比对失败返回false
Object
对象:满足对象中的全部字段则匹配成功
- cols
(Array) (Optional)
输出字段,输出时仅输出指定字段。不指定时输出全部字段
返回: 包含所有满足条件结果组成的 Array
示例1:
// 查询包含 url 字段的客户
var results = db_customers.find(row=>!!row.url);
console.log(results);
// 运行结果:
[
{
id: 1,
name: 'Panda',
address: 'Chongqing',
email: 'panda@sample-domain.com',
url: 'https://panda.panda-studio.cn'
}
]
示例2:
// 查询名称是 Bob 的客户
var results = db_customers.find({ name: "Bob" });
console.log(results);
// 运行结果:
[
{
id: 2,
name: 'Bob',
address: 'USA',
email: 'bob@sample-domain.com',
url: null
}
]
setColumn
修改字段。
用法: setColumn( query, row )
参数:
- query
(Function/Object) (Required)
查询条件,可以是方法,也可以是对象。
Function(row)
方法:参数是每一条数据,比对成功返回true,比对失败返回false
Object
对象:满足对象中的全部字段则匹配成功
- row
(Function/Object) (Optional)
匹配成功的数据行字段修改为此内容(仅修改此对象中包含的字段)
Function(row)
方法v5.2
:参数是匹配成功的原数据,需返回Object
Object
想要修改的成为内容
返回: 实例本身
示例:
// 设置 Bob 的 url
db_customers.setColumn( row => {
return row.name == "Bob";
},{
url: "http://bob.domain.com"
});
var results = db_customers.find( { name: "Bob" } );
console.log(results);
// 运行结果:
[
{
id: 2,
name: 'Bob',
address: 'USA',
email: 'bob@sample-domain.com',
url: 'http://bob.domain.com'
}
]
示例:
// 把包含 Bob 的 email .com替换成.cn
db_customers.setColumn( row => {
return row.name == "Bob";
}, row=>({
email: row.email.replace('.com','.cn')
}));
var results = db_customers.find( { name: "Bob" } );
console.log(results);
// 运行结果:
[
{
id: 2,
name: 'Bob',
address: 'USA',
email: 'bob@sample-domain.com',
url: 'http://bob.domain.cn'
}
]
drop
移除数据行。
用法: drop( query )
参数:
- query
(Function/Object) (Required)
查询条件,可以是方法,也可以是对象。
Function(row)
方法:参数是每一条数据,比对成功返回true,比对失败返回false
Object
对象:满足对象中的全部字段则匹配成功
返回: 实例本身
示例:
// 移除 id 大于 2 的字段
db_customers.drop( row => row.id >= 2);
// 查询所有结果
var results = db_customers.find( row => true );
console.log(results);
// 运行结果:
[
{
id: 1,
name: 'Panda',
address: 'Chongqing',
email: 'panda@sample-domain.com',
url: 'https://panda.panda-studio.cn'
}
]