乐趣区

关于javascript:loki-数据库详解

介绍

LokiJS 是一个面向文档的 javascript 数据库,与 MongoDB 有点类似。

它反对索引,查问和过滤数据汇合。LokiJS 还反对更高级的性能,例如 mapReduce,事务,并容许您实现自定义近程同步以将数据保留到服务器(或挪动设施上的本地文件)。
磁盘的持久性曾经在诸如 nodejs 之类的 CommonJS 环境中实现,
在挪动设施上,您只须要申请文件系统并将 lokijs 的 serialize()作为内容传递即可。

创立

创立一个 db:

var db = new loki('Example');

除了 Example 这个数据库的名称外, 还能够传递一个选项参数:

interface LokiConfigOptions {
    adapter: LokiPersistenceAdapter | null;
    autoload: boolean;
    autoloadCallback: (err: any) => void;
    autosave: boolean;
    autosaveCallback: (err?: any) => void;
    autosaveInterval: string | number;
    persistenceMethod: "fs" | "localStorage" | "memory" | null;
    destructureDelimiter: string;
    serializationMethod: "normal" | "pretty" | "destructured" | null;
    throttledSaves: boolean;
}

对于参数: persistenceMethod 须要留神的点:

  • “fs”: 只能在 node 环境中应用(包含 electron)
  • “localStorage” : web 中能够应用, 并且数据会存在 localStorage 中, 进行长久存储
  • “memory”: 只是简略的存在内容中, 如果刷新页面, 数据就不存在了

在不同的环境中, 他创立的长久层是不同的, 比方
在浏览器中, 是基于 web 数据库或者 localStorage(默认) 的
在 node 环境中, 能够基于 fs, 创立文件数据库
对于他们的变动是须要一个 option.adapter 来进行帮助

在 web 中, 如果想要将数据存到 web 数据库中,
那么就须要 option.adapter 了 (当传递了 adapter 参数时, ‘persistenceMethod’ 参数就会被有效)
然而如果应用了 option.adapter, 那么只能应用主动加载数据库和字段保留数据库的计划

减少数据

针对 loki 里的 Collection 如果理解数据库, 能够将它当成 这种构造

想要增加数据, 须要先获取 collection 对象

能够在增加 collection 的时候获取:

const users = db.addCollection('users', {indices: ['email']});

能够间接获取:

const coll = db.getCollection('users')

留神 addCollection 有 2 个参数能够传递:

  • 第一个参数是 name 是 collection 的名称
  • 第二个是可选项参数, 它领有很多参数:
名称 类型 属性 默认值 形容
unique 数组 < 可选 > [] 属性名称数组,用于定义惟一束缚
exact 数组 < 可选 > [] 属性名称数组,用于定义确切的束缚
indices 数组 < 可选 > [] 用于定义二进制索引的数组属性名称
adaptiveBinaryIndices 布尔值 < 可选 > true 收集索引将被从新建设而不是懒加载
asyncListeners 布尔值 < 可选 > false 侦听器是否异步调用
disableMeta 布尔值 < 可选 > false 设置为 true 以禁用文档的元属性
disableChangesApi 布尔值 < 可选 > true 设置为 false 以启用 Changes API
disableDeltaChangesApi 布尔值 < 可选 > true 设置为 false 以启用 Delta 更改 API(须要更改 API,强制克隆)
autoupdate 布尔值 < 可选 > false 应用 Object.observe 自动更新对象
clone 布尔值 < 可选 > false 指定是否向用户插入或从用户克隆查问
serializableIndices 布尔值 < 可选 > true[] 将二进制索引属性上的日期值转换为纪元工夫
cloneMethod 字符串 < 可选 > ‘parse-stringify’ ‘parse-stringify’, ‘jquery-extend-deep’, ‘shallow’, ‘shallow-assign’
ttl int < 可选 > 文件被认为是古老 / 过期之前的文件工夫(以毫秒为单位)。
ttlInterval int < 可选 > 革除“古老”文件的工夫距离;默认状况下未设置。

应用 inert 增加数据

const coll = db.getCollection('users')
var odin = users.insert({name: 'odin', email: 'odin.soap@lokijs.org', age: 38});
var thor = users.insert({name: 'thor', email: 'thor.soap@lokijs.org', age: 25});
var stan = users.insert({name: 'stan', email: 'stan.soap@lokijs.org', age: 29});

// 也能够同时插入多个数据
// users.insert([{name: 'Thor', age: 35}, {name: 'Loki', age: 30}]);

这里要留神的是: 分分明数据的存储状态, 当咱们不应用主动保留和手动保留的时候 insert, 会将数据插入 collection 中, 然而当咱们刷新页面的时候, 数据会重置会原来的数据,
如果咱们要将数据全副存下来(即便刷新也会存在的话), 就须要保留:

// var db = new loki('Example'); 这是 db 的由来
db.saveDatabase(error => {console.log('保留数据')
  error && console.log(error)
})

获取数据:

获取数据是比拟灵便的, 我这里说两种办法:

办法一:


const dv = coll.addDynamicView('test');

const results = dv.data();

console.log(results)
// 这是 results 打印后果
// 0: {name: "odin", email: "odin.soap@lokijs.org", age: 38, meta: {…}, $loki: 1}
// 1: {name: "thor", email: "thor.soap@lokijs.org", age: 25, meta: {…}, $loki: 2}
// 2: {name: "stan", email: "stan.soap@lokijs.org", age: 29, meta: {…}, $loki: 3}
// 3: {name: "oliver", email: "oliver.soap@lokijs.org", age: 31, meta: {…}, $loki: 4}
// 4: {name: "hector", email: "hector.soap@lokijs.org", age: 15, meta: {…}, $loki: 5}
// 5: {name: "achilles", email: "achilles.soap@lokijs.org", age: 31, meta: {…}, $loki: 6}

办法二:

const resultsLine = coll.chain().data();

console.log(resultsLine)
// 后果与办法一雷同

获取数据时筛选想要的数据:

办法 1 的筛选:

find
// 通过 coll 间接获取
const results4 = coll.find({'age': {'$aeq': 15}});
console.log('获取数据 4',results4)

// 可应用不同的指令:
// 指令名 作用
// $eq  ===
// $ne  !==
// $aeq  ==
// $dteq 工夫上的相等
// $gt  >
// $gte >=
// $lt  <
// $lte  <=
// $between   介于 2 个数之间

// 如果不心愿应用二进制索引,并且心愿简略的 javascript 比拟是能够承受的,咱们提供以下操作,因为它们的简化比拟,它们能够提供更好的执行速度。// $gt -> $jgt
// $gte -> $jgte
// $lt -> $jlt
// $lte -> $jlte
// $between -> $jbetween
// $regex 应用正则
applyFind
const dv = coll.addDynamicView('test');

dv.applyFind({'name' : 'odin'});
const results = dv.data();
applyWhere

dv2.applyWhere(function(obj) {return obj.name === 'oliver';});
// 作用与上述办法雷同
applySimpleSort
// 依据年龄进行排序
const dv3 = coll.addDynamicView('test3');

dv3.applySimpleSort("age");

const results3 = dv3.data();

console.log(results3)
findOne
const results5 = coll.findOne({'age': {'$aeq': 31}});
// 获取到的是对象  而不是一个数组
console.log('获取数据 5',results5)
findObject
const results6 = coll.findObject({'age': 38});
// 应用的后果和 findOne 相似
console.log('获取数据 6',results6)
findObjects
const results7 = coll.findObjects({'age': 31});
// 返回的是一个数组
console.log('获取数据 7',results7)

比拟举荐的是应用 addDynamicView 的形式来筛选, 而不是通过 collection 间接获取

须要留神的是 DynamicView 是一个数据格式, 他能够 add 能够 get 也能够 remove

办法 2 的筛选:

// 简略的筛选
const resultsLine2 = coll.chain().find({ 'name' : 'odin'}).data();

// 排序:
const resultsLine3 = coll.chain().simplesort('age').data();

当然 chain 里还有其余操作, 如: limit, map, findAnd, eqJoin, count 等等,
我是更举荐应用第一种办法, 这里的几种应用计划我就不具体举例了

还有不倡议应用 chain 的 update,remove 等操作, 因为监听器外面会监听不到事件,
这个问题不晓得是成心这么做 还是 bug

批改数据:

update

与 insert 同理:

// 要批改 就须要先获取要批改的货色是什么 
const item = coll.findOne({'age': {'$aeq': 31}});

item.age = 18
coll.update(item);

console.log(coll.chain().data())
// 打印发现名字为 odin 的年龄曾经改成了 18

// 当然想要长久化就得保留数据库:
db.saveDatabase(error => {console.log('保留数据')
  error && console.log(error)
})
findAndUpdate
coll.findAndUpdate({'age': {'$aeq': 25}}, data => {
    // 原名 "thor"
    data.name = 'grewer'
    return data
})

// 获取并且批改 集中在同一个办法外面
console.log('批改后果 2', coll.chain().data())
updateWhere

coll.updateWhere(data => {return data.name === 'grewer';}, data => {
    data.age = '999'
    return data
})

// 与下面的相似, 然而更加自在, 而且还能够是用 `{'age': {'$aeq': 15}}` 这种办法来获取

删除数据:

remove

删除数据也是非常简单的(与更新相似):

const item2 = coll.findOne({'age': {'$aeq': 31}});

coll.remove(item2);

console.log(coll.chain().data())
findAndRemove
coll.findAndRemove({'age': {'$aeq': 15}})
// 同 findAndUpdate, 集中了 find 和 remove
console.log('批改后果 2', coll.chain().data())
removeWhere
// 同 updateWhere
coll.removeWhere((value,index)=>{return index === 1})

console.log('删除后果 3', coll.chain().data())

增加操作的监听:

Loki 的 DB 反对自定义事件, 应用如下:

// 增加自定义 grewer 事件
db.addListener('grewer',(data) => {console.log('grewer 事件', data)
})

// 触发事件
db.emit('grewer','qwerty')

Loki 反对对 collection 增加操作的监听, 监听的事件反对以下事件

close
delete
error
flushbuffer
insert
pre-insert
pre-update
update
warning

应用:

coll.on('update', (event) => {console.log('coll change 事件', event)
})
// inset, delete 等其余事件同理

在咱们应用 update/findAndUpdate/updateWhere 的时候就会主动触发此回调了

对于 Collection transforms

他的官网介绍是这样的:

转换背地的根本思维是容许将后果集“链”过程转换为该过程的对象定义。而后能够抉择命名该数据定义,并将其与汇合一起保留在数据库中。

一个简略的应用:

var tx = [
    {
        type: 'find',
        value: {'name': 'oliver'}
    }
];
console.log(coll.chain(tx).data())

// 打印后果:
[{
    $loki: 4
    age: 18
    email: "oliver.soap@lokijs.org"
    meta: {...}
    name: "oliver"
}]

对于他的应用, 感觉像是其余数据库外面的schema, 我这里也没碰到过具体的状况, 所以理解不够粗浅

其余数据库性能:

连表查问:

public eqJoin(joinData: Collection<any> | Resultset<any> | any[],
    leftJoinProp: string | ((obj: any) => string),
    rightJoinProp: string | ((obj: any) => string),
    mapFun?: (left: any, right: any) => any,
    dataOptions?: Partial<GetDataOptions>
): Resultset<any>;

一个简略的应用例子:

// 创立另一个 collection(表)


var collection = db.addCollection("test", {unique: ["name"]
});

collection.insert({owner: 0, name: 'Betsy'});
collection.insert({owner: 1, name: 'Bingo'});
collection.insert({owner: 2, name: 'Fifi'});
collection.insert({owner: 3, name: 'Fuzzy'});
collection.insert({owner: 4, name: 'Gizmo'});

// 这是另一个表

// 进行查问:

const resultSet = coll.eqJoin(collection.chain(), 'id', 'owner')
// 当 id 和 owner 相等时 数据会被连贯
console.log('连表', resultSet.data())
// 打印一下 console
[{
$loki: 1
left: {name: "odin", id: 0, email: "odin.soap@lokijs.org", age: 38, meta: {…}, …}
meta: {revision: 0, created: 1597421406034, version: 0}
right: {owner: 0, name: "Betsy", meta: {…}, $loki: 1}
},
{
$loki: 2
left: {name: "grewer", id: 1, email: "thor.soap@lokijs.org", age: "999", meta: {…}, …}
meta: {revision: 0, created: 1597421406034, version: 0}
right: {owner: 1, name: "Bingo", meta: {…}, $loki: 2}
},
{
$loki: 3
left: {name: "stan", email: "stan.soap@lokijs.org", age: 29, meta: {…}, $loki: 3}
meta: {revision: 0, created: 1597421406034, version: 0}
right: {}},
{
$loki: 4
left: {name: "oliver", email: "oliver.soap@lokijs.org", age: 18, meta: {…}, $loki: 4}
meta: {revision: 0, created: 1597421406034, version: 0}
right: {}},
{
$loki: 5
left: {name: "hector", email: "hector.soap@lokijs.org", age: 15, meta: {…}, $loki: 5}
meta: {revision: 0, created: 1597421406034, version: 0}
right: {}},
{
$loki: 6
left: {name: "achilles", email: "achilles.soap@lokijs.org", age: 31}
meta: {revision: 0, created: 1597421406034, version: 0}
right: {}}]

这就是最简略的连表应用

还有一些没说到的, 然而也就是边边角角的货色了, 根本就是这些办法的应用

写在最初

Loki 领有 adapter 使得他的适用性特地高, 然而绝对具体的应用却比拟少, 所以我写了这篇绝对具体一点的文章来记录此数据库的相干操作

对于官网文档地址:

http://techfort.github.io/Lok…

还有这篇文章外面的所有例子的地址:

https://github.com/Grewer/JsD…

退出移动版