@qtk/orm-framework
v3.4.1
Published
orm
Downloads
11
Readme
qtk-orm-framework
qtk-orm-framework是一个Node版的K-V型数据操作框架,支持一对一,一对多两种关系,存储目前支持Mysql、Redis两种类型.框架致力于提供一套简单的API去操作K-V结构数据,使用者只需定义一份数据描述文件及数据库配置文件,框架将会帮你实现建库建表(Mysql)、建索引(Mysql)、增删改查、数据分表分库、数据校验、数据缓存、数据热迁移.
框架有两个核心概念Object
、Relation
- Object: 存储一对一关系的实体,可以将其理解为Document(文档),数据中必须包含
id
字段,主键为id
- Relation: 存储一对多关系的实体,可以将其理解为Collection(容器),一个容器里面有多个Document(文档),数据中必须包含
subject
、object
字段,subject
为容器id,object
为文档id,主键为${subject}_${object}
安装
#in your project
npm install @qtk/orm-framework --save
# install global
npm install @qtk/orm-framework -g
文档
建库建表建索引
orm_build_mysql -s <schema path> -r <router path> -t <object or relation> <module name>
# example
orm_build_mysql -s ./test/config/object/schema -r ./test/config/object/router -t object user
orm_build_mysql -s ./test/config/relation/schema -r ./test/config/relation/router -t relation user.message
删库删表删索引
orm_destroy_mysql -r <router path> <module name>
#example
orm_destroy_mysql -r ./test/config/object/router user
orm_destroy_mysql -r ./test/config/relation/router user.message
清库
orm_purge_data -r <router path> <module name>
#example
orm_purge_data -r ./test/config/object/router user
orm_purge_data -r ./test/config/object/router user.message
API
初始化
const ORM = require('@qtk/orm-framework');
ORM.setup({
objectSchemaPath: `${__dirname}/config/object/schema`,
objectRouterPath: `${__dirname}/config/object/router`,
relationSchemaPath: `${__dirname}/config/relation/schema`,
relationRouterPath: `${__dirname}/config/relation/router`,
strict: true
});
const ObjectUser = new ORM.Object('user');
const RelationUserMessage = new ORM.Relation('user.message');
- objectSchemaPath: object的schema文件夹路径
- objectRouterPath: object的router文件夹路径
- relationSchemaPath: relation的schema文件夹路径
- relationRouterPath: relation的router文件夹路径
- strict: 严格模式,默认
true
.当数据库中的值结构跟schema数据描述不相符时,为false
时会依照schema描述重新构造拷贝数据后,再进行schema校验.而true
时则直接进入schema校验,此情况抛数据错误.
Object
提供对象操作方法
|方法名|输入|返回|作用|
|--|--|--|--|
|set|(object)|无|添加/更新一条数据,object里必须含有id
字段|
|get|(id)|object|读取某条记录|
|has|(id)|boolean|查询某条数据是否存在|
|del|(id)|无|删除某条数据|
|arrayNodeAppend|(id, path, ...items)|无|给某条记录下的数组节点尾部添加一个或多个元素|
|arrayNodeUnshift|(id, path, ...items)|无|给某条记录下的数组节点头部添加一个或多个元素|
|arrayNodeInsert|(id, path, item)|无|给某条记录下的数组节点某个索引位置插入一个元素|
|arrayNodeUnshift|(id, path, ...items)|无|给某条记录下的数组节点头部添加一个或多个元素|
|arrayNodeDel|(id, path)|无|删除某条记录下的数组节点里指定位置的元素|
|arrayNodePop|(id, path)|object|弹出某条记录下的数组节点尾部一个元素|
|arrayNodeShift|(id, path)|object|弹出某条记录下的数组节点头部一个元素|
|find|({where?, sort?, limit?, group?})|array|查找所有符合规则的object,支持排序、分页、分组|
|fieldFind|({field?,where?, sort?, limit?,group?})|array|查找所有符合规则的object,支持排序、分页、分组、取单个字段|
|count|(where,group)|integer|统计所有符合规则的object数量|
|query|(sql)|array|执行原生sql|
Relation
提供关系操作方
|方法名|输入|返回|作用| |--|--|--|--| |fetch|(subject, object)|object|返回关系中某个对象| |put|(relation)|无|将某个对象放入关系中,relation必须包含subject, object两个字段| |has|(subject, object)|boolean|判断关系中含有某个对象| |remove|(subject, object)|无|从关系中移除某个对象| |clear|(subject)|无|清空关系里所有对象| |list|(subject, sort?, limit?, filter?)|array|返回关系中的对象,可筛选、排序、分页| |count|(subject, filter?)|integer|统计关系中的对象,可筛选|
Logic
提供一套标准的查询、排序、分页语法,在Objectfind
、count
,Relationlist
、count
中可以使用
const {whereEq, whereNq, whereGt, whereGe, whereLt, whereLe, whereContain, whereContainBoolean,whereIn, whereBetween, whereLike, whereAnd, whereOr, whereNot, sort, limit, field , group } = require('@qtk/orm-framework').Logic;
|方法|作用|参数|示例(按照属性栏的顺序)| |--|--|--|--| |whereEq|等于|(field, value)|whereEq('.a', 1)| |whereNq|不等于|(field, value)|whereNq('.a', 1)| |whereGt|大于|(field, value)|whereGt('.a', 1)| |whereGe|大于等于|(field, value)|whereGe('.a', 1)| |whereLt|小于|(field, value)|whereLt('.a', 1)| |whereLe|小于等于|(field, value)|whereLe('.a', 1)| |whereContain|数组包含|(field, value)|whereContain('.arr[]', 1)| |whereContainBoolean|数组包含|(field, value)|whereContainBoolean('.arr[]', 1)| |whereIn|枚举|(field, ...values)|whereIn('.a', 1, 2, 3)| |whereBetween|区段之间|(field, from, to)|whereBetween('.a', 1, 2)| |whereLike|模糊匹配|(field, value)|whereLike('.a', '%a%')| |whereAnd|与|(...items)|whereAnd(whereEq('.a', 1), whereLt('.b', 1))| |whereOr|或|(...items)|whereOr(whereEq('.a', 1), whereLt('.b', 1))| |whereNot|非|item|whereNot(whereEq('.a', 1))| |WhereIsUndef|是否为非NULL (判断是否为非空数组也可以使用这个方法)|(field)|WhereIsUndef('.a')| |WhereIsDef|是否为NULL (判断是否为空数组也可以使用这个方法)|(field)|WhereIsDef('.a')| |sort|排序|(field, order = "ASC")|sort('.a', 'DESC')| |limit|分页|(limit, skip = 0)|limit(1,1)| |field|select 单个字段 |(field, alias)|field('.name','nameS'),field('count(1)','count')| |group|分组|(field)|group('.name')|
JSON化
Logic对象是一个类对象,不支持通过网络传输。故Logic对象提供toJson
方法,允许将Logic对象转换为JSON对象.同时在原本使用Logic对象的地方支持使用JSON化后对象进行操作
let result = await ObjectUser.find({where: ORM.Logic.whereEq('.id', Users[0].id)});
assert(result.length == 1 && result[0].id == Users[0].id, `object find where [.find(id where operator =)] failed`);
//等同于下面
result = await ObjectUser.find({where: ORM.Logic.whereEq('.id', Users[0].id).toJson()});
assert(result.length == 1 && result[0].id == Users[0].id, `[toJson]object find where [.find(id where operator =)] failed`);
例子
const ORM = require('@qtk/orm-framework');
ORM.setup({
objectSchemaPath: `${__dirname}/config/object/schema`,
objectRouterPath: `${__dirname}/config/object/router`,
relationSchemaPath: `${__dirname}/config/relation/schema`,
relationRouterPath: `${__dirname}/config/relation/router`,
});
const ObjectUser = new ORM.Object('user');
const ObjectMessage = new ORM.Object('message');
const RelationUserMessage = new ORM.Relation('user.message');
const user = {
id: '0000000000000001',
name: 'Cindy',
gender: 0,
money: 110,
null: null,
location: {
lng: '113.46',
lat: '22.27'
},
isVip: false,
friends: [],
extraObject: {
count: 1
},
maybeUndef: 1
}
const user2 = {
id: '0000000000000002',
name: 'Jessica',
gender: 0,
money: 120,
null: null,
location: {
lng: '122.67',
lat: '23.45'
},
isVip: true,
friends: [{
fid: '0000000000000003',
time: 1516538014
}],
extraArray: ["1","2","3"],
extraInteger: 0
}
const message = {
id: 1,
title: "hello",
content: "hey",
sendTime: 1516538014
}
const userMessage = {
subject: '0000000000000001',
object: 1,
status: 1,
readTime: 1516538014,
maybeUndef: 1
}
const friend1 = {fid: uuid().replace(/-/g, ""), time: parseInt(Math.random() * 100)};
const friend2 = {fid: uuid().replace(/-/g, ""), time: parseInt(Math.random() * 100)};
await ObjectUser.set(user);
await ObjectUser.set(user2);
await ObjectMessage.set(message);
console.log(await ObjectUser.has(user.id));
console.log(await ObjectUser.get(user.id));
await ObjectUser.arrayNodeAppend(user.id, '.friends', friend1, friend2);
await ObjectUser.arrayNodeUnshift(user.id, '.friends', friend1, friend2);
await ObjectUser.arrayNodeInsert(user.id, '.friends', 1, friend2);
await ObjectUser.arrayNodeDel(user.id, '.friends', 1);
console.log(await ObjectUser.arrayNodePop(user.id, '.friends'));
console.log(await ObjectUser.arrayNodeShift(user.id, '.friends'));
console.log(await ObjectUser.find());
console.log(await ObjectUser.find({where: ORM.Logic.whereEq('.id', user.id)}));
console.log(await ObjectUser.find({where: ORM.Logic.whereEq('.name', user.name)}));
console.log(await ObjectUser.find({where: ORM.Logic.whereNot(ORM.Logic.whereEq('.name', user.name))}));
console.log(await ObjectUser.find({where: ORM.Logic.whereIn('.name', user.name, user2.name)}));
console.log(await ObjectUser.find({where: ORM.Logic.whereAnd(
ORM.Logic.whereEq('.name', user.name),
ORM.Logic.whereEq('.id', user.id)
)}));
console.log(await ObjectUser.find({where: ORM.Logic.whereOr(
ORM.Logic.whereEq('.name', user.name),
ORM.Logic.whereEq('.name', user2.name)
)}));
console.log(await ObjectUser.find({where: ORM.Logic.whereLike('.name', `%${user.name.substr(1, 3)}%`)}));
console.log(await ObjectUser.find({where: ORM.Logic.whereBetween('.money', 1, 111)}));
console.log(await ObjectUser.find({where: ORM.Logic.whereContain('.friends[*].fid', '0000000000000003')}));
console.log(await ObjectUser.find({where: ORM.Logic.whereIsUndef('.maybeUndef')}));
console.log(await ObjectUser.find({where: ORM.Logic.whereIsDef('.maybeUndef')}));
console.log(await ObjectUser.find({sort: ORM.Logic.sort('.id', "desc")}));
console.log(await ObjectUser.find({limit: ORM.Logic.limit(1), sort: ORM.Logic.sort('.id')}));
console.log(await ObjectUser.find({limit: ORM.Logic.limit(1, 1), sort: ORM.Logic.sort('.id', 'DESC')}));
console.log(await ObjectUser.find({limit: ORM.Logic.limit(1, 1), sort: [ORM.Logic.sort('.id', 'DESC'), ORM.Logic.sort('.money', 'ASC')]}));
console.log(await ObjectUser.count());
console.log(await ObjectUser.count(ORM.Logic.whereEq('.id', user.id)));
console.log(await ObjectUser.count(ORM.Logic.whereEq('.name', user.name)}));
console.log(await ObjectUser.count(ORM.Logic.whereNot(ORM.Logic.whereEq('.name', user.name))));
console.log(await ObjectUser.count(ORM.Logic.whereIn('.name', user.name, user2.name)));
console.log(await ObjectUser.count(ORM.Logic.whereAnd(
ORM.Logic.whereEq('.name', user.name),
ORM.Logic.whereEq('.id', user.id)
)));
console.log(await ObjectUser.count(ORM.Logic.whereOr(
ORM.Logic.whereEq('.name', user.name),
ORM.Logic.whereEq('.name', user2.name)
)));
console.log(await ObjectUser.count(ORM.Logic.whereLike('.name', `%${user.name.substr(1, 3)}%`)));
console.log(await ObjectUser.count(ORM.Logic.whereBetween('.money', 1, 111)}));
console.log(await ObjectUser.count(ORM.Logic.whereContain('.friends[*].fid', '0000000000000003')));
console.log(await ObjectUser.count(ORM.Logic.whereIsUndef('.maybeUndef')));
console.log(await ObjectUser.count(ORM.Logic.whereIsDef('.maybeUndef')));
console.log(await ObjectUser.count(RM.Logic.sort('.id', "desc")));
console.log(await ObjectUser.count(ORM.Logic.limit(1), sort: ORM.Logic.sort('.id')));
console.log(await ObjectUser.count(ORM.Logic.limit(1, 1), sort: ORM.Logic.sort('.id', 'DESC')));
console.log(await ObjectUser.count(ORM.Logic.limit(1, 1), sort: [ORM.Logic.sort('.id', 'DESC'), ORM.Logic.sort('.money', 'ASC')]));
console.log(await ObjectUser.del(user.id));
await RelationUserMessage.put(userMessage);
console.log(await RelationUserMessage.has(user.id, message.id));
console.log(await RelationUserMessage.fetch(user.id, message.id));
console.log(await RelationUserMessage.count(user.id));
console.log(await RelationUserMessage.count(Users[0].id, ORM.Logic.whereEq('.status', 2)));
console.log(await RelationUserMessage.count(Users[0].id, ORM.Logic.whereIsUndef('.maybeUndef')));
console.log(await RelationUserMessage.count(Users[0].id, ORM.Logic.whereIsDef('.maybeUndef')));
console.log(await RelationUserMessage.list(user.id, ORM.Logic.sort('.status', 'DESC'), ORM.Logic.limit(1, 1)));
console.log(await RelationUserMessage.list(user.id, [ORM.Logic.sort('.status', 'DESC'), ORM.Logic.sort('.readTime', 'DESC')], ORM.Logic.limit(1, 1)));
console.log(await RelationUserMessage.list(user.id, [ORM.Logic.sort('.status', 'DESC'), ORM.Logic.sort('.readTime', 'DESC')], ORM.Logic.limit(1, 1)), ORM.Logic.whereEq('.status', 2));
console.log(await RelationUserMessage.remove(user.id, message.id));
console.log(await RelationUserMessage.clear(user.id));
更详细的操作,请看测试用例[github才能支持]
Schema定义(数据描述)
关键字
- id : object的主键
- subject : relation键之一
- object : relation键之一,relation的主键为 ${subject}_${object}
数据类型
- string : 字符串
- boolean : 布尔型
- integer : 整形
- number : 数字
- object : 对象
- array : 数组
- empty : 空对象,值为 null
方法
- 通用
- desc(value) : 字段描述
- example(value) : 字段值例子
- default(value) : 字段默认值, 如果字段是必须的,但数据库里的值是
undefined
, 那么object的get
、find
,relation的fetch
,list
操作将会在返回数据时候自动给其加上默认值.注意:对于设置了默认值且数据库里值为undefined的字段,在进行排序、查找时,使用的值是数据库里的值,即undefined
而不是默认值 - enum(value1,value2...) : 值枚举
- number/integer
- max(value) : 最大值
- min(value) : 最小值
- exclusiveMin(value) : 字段值不小于
- exclusiveMax(value) : 字段值不大于
- multipleOf(value) : 字段值是定义值的整数倍
- array
- minItems(value) : 元素最少个数
- maxItems(value) : 元素最多个数
- length(value) : 元素个数
- contains(value) : 数组必须包含的值
- uniqueItems() : 每个元素必须唯一,支持对象元素(比较每个对象的key && value是否相同)
- item(value/array) : 定义数组里每个元素的结构
- string
- maxLength(value) : 最大长度
- minLength(value) : 最小长度
- length(value) : 长度
- pattern(value) : 字符串必须匹配的正则
- object
- properties(object) : 定义对象的结构
- patternProperties(object) : 使用正则来定义对象的结构
- additionalProperties(boolean) : 是否允许实例拥有非[properties]里定义的节点
- require(value1, value2...) : 实例必须拥有[properties]里定义的节点
- requireAll() : [properties]里定义的节点全是必须的
- if...then...elseIf...else...endIf : 根据实例不同的情况可以拥有不同的
properties
、patternProperties
、require
、requireAll
定义
- string/number/integer
- index() : 给对应的Key加索引.目前支持
string
、integer
、number
、array里的元素
类型,对于非数组元素里的string
类型,必须设置length
或者maxLength
. 由于Object
keyword的id
,Relation
keyword的subject
与object
默认设置了索引,故当其为string
类型时,必须设置长度.而对于数组元素里的string
类型则不用设置长度,因为采用的是全文索引
- index() : 给对应的Key加索引.目前支持
- 通用
语法糖
| sugar | equivalent | | -------------- | -------------------------------------------- | | 1 | integer().enum(1) | | 1.1 | number().enum(1.1) | | 'foo' | string().enum('foo') | | /^foo|bar$/ | string().pattern(/^foo|bar$/) | | true | boolean().enum(true) | | null | NULL() or empty() | | {foo: 1} | object().properties({foo: 1}).requiredAll().additionalProperties(false) | | [1, 2, 3] | integer().enum(1, 2, 3) | | [1.1, 2.2, 3] | number().enum(1.1, 2.2, 3) | | ['foo', 'bar'] | string().enum('foo', 'bar') | | [true, false] | boolean().enum(true, false) |
module.exports = {
id: string().length(16), //如果id是字符串类型, 那么必须设置length或者maxLength
name: string(),
gender: integer().enum(0, 1).index(),
money: number().min(0),
null: empty(),
location: {
lng: string().length(6).index(), //对象里某个节点做索引
lat: string().desc('lat').default("2")
},
isVip: boolean(),
friends: array().item({
fid: string().pattern(/^[A-Za-z0-9]{1,}$/).index(), //可以针对数组里每个元素的某个值做索引
time: integer()
}),
record: array(string().index()), //可以针对数组里每个元素做索引
extraObject: object({
count: integer()
}).default({count: 10}),
extraArray: array(string()).default(['default array']),
extraInteger: integer().default(0),
extraNumber: number().default(0.9).index(),
extraBoolean: boolean().default(false),
extraString: string().default("default").index().maxLength(32)
};
module.exports = object({
subject: string().length(32), //string类型必须设置长度
object: integer(), //若是string类型也应设置长度
status: integer().enum(0, 1, 2).desc('0:未读; 1:已读; 2:已删'),
readTime: integer().default(0),
deletedTime: integer().default(0)
})
.if.properties({status: 1})
.then.require('subject', 'object', 'status', 'readTime').additionalProperties(false)
.elseIf.properties({status: 2})
.then.require('subject', 'object', 'status', 'deletedTime').additionalProperties(false)
.else
.require('subject', 'object', 'status').additionalProperties(false)
.endIf
Router定义(数据库配置)
user.js //当前使用的数据库路由文件
user.deprecated.js //废弃的数据库路由文件
module.exports = {
persistence: {
shards: [
{
media: "mysql",
host: "localhost",
port: 3306,
user: "root",
password: "",
database: "db_test_game",
table: "o_user",
}
],
hash: function (id) {
return this.shards[0];
}
},
cache: {
shards: [
{
media: "redis",
host: "localhost",
port: 6379,
bucket: 'o_user_'
}
],
hash: function (id) {
return this.shards[0];
}
}
};
路由的文件名跟schema定义的文件名一一对应,路由文件有两种状态:当前
(xxx.js)及废弃
(xxx.deprecated.js).框架主力使用当前
路由配置,万一同级目录下存在同名废弃路由的话,使用过程中会对数据进行热迁移.
每份路由可配置persistence
、cache
节点,每个节点下配置分片信息shards
及分片规则hash
.persistence顾名思义就持久化存储,目前框架内置支持mysql,而cache内置支持redis.
shards
: 数组,每个元素表示一个分片信息
hash
: 哈希函数,输入参数是id
.在对某条数据进行操作时,框架将给哈希函数传入数据的id(Object是id
字段,Relation是subject
字段),函数根据id映射到shards
某个分片上
若persistence
、cache
同时配置的话,那么读操作会优先操作cache
(除了Objectfind
、count
,Relationlist
、count
),若有数据则立即返回,若无则会继续查询persistence
,若有数据,在返回数据同时也同步一份到cache
中.写操作则是等待两边操作完毕后才返回
具体热迁移逻辑如下:
|模式|方法|处理逻辑| |--|--|--| |Object|get|优先查当前,若无则查废弃,有结果则同步该数据到当前| |Object|set|只写当前| |Object|has|同Object.get| |Object|del|当前、废弃同时删除| |Object|arrayNodeAppend|首先Object.has检查,其顺带数据迁移,之后只写当前| |Object|arrayNodeUnshift|首先Object.has检查,其顺带数据迁移,之后只写当前| |Object|arrayNodeInsert|首先Object.has检查,其顺带数据迁移,之后只写当前| |Object|arrayNodeDel|首先Object.has检查,其顺带数据迁移,之后只写当前| |Object|arrayNodePop|首先Object.has检查,其顺带数据迁移,之后只写当前| |Object|arrayNodeShift|首先Object.has检查,其顺带数据迁移,之后只写当前| |Object|find|优先查当前,若无则查废弃,有结果则返回但不做同步| |Object|fieldFind|优先查当前,若无则查废弃,有结果则返回但不做同步| |Object|count|同Object.find| |Relation|fetch|同Object.get| |Relation|put|同Object.set| |Relation|has|同Object.has| |Relation|remove|同Object.del| |Relation|clear|同Object.del| |Relation|list|同Object.find|
注意(坑)
- schema定义文件若给字段设置默认值, 那么该字段也应是必须的,否则不会自动补上默认值
- schema定义文件若给字段设置默认值且数据库里该字段值为undefined时,在进行排序、查找操作时,使用的值是数据库里的值,即
undefined
而不是默认值 - Object
find
、count
,Relationlist
,count
考虑到可能会涉及多条数据情况,为了不影响性能,故上述方法不支持数据热迁移 - 当Router里配置
cache
与persistence
时,即开启数据缓存功能(加速读),此情况存在有些数据还未加载到cache
情况,故Objectfind
、count
、Relationlist
、count
将会直接读取persistence
来保证数据的准确性 - Object
find
提供的是Object搜索功能,若Router里存在多个shards
时,即开启数据分片功能,由于无法根据id定位到具体的分片,故会查询所有的分片信息,并合并结果返回.在此种情况下无法进行分页排序,故不支持sort
与limit
. - 使用whereContain时,其实现原理是全文搜索,故需要保证字段内容长度 >=ft_min_word_len ,数据库默认ft_min_word_len为4
自定义存储介质
框架内部支持mysql与redis两种存储介质,若想换一种存储媒介或者重新设计底层数据结构的话,那么可以继承框架的BackendMedia
基类,实现指定的函数功能后,以插件的形式注册到框架上.那么即可无痛更换,而不用修改任何代码.
构造函数
负责初始化存储媒介的链接.构造函数有两个参数:
connParam
: 经过哈希函数计算得到的分片信息
indexes
: 该Object
/Relation
的字段索引列表
constructor(connParam, indexes) {
super(connParam, indexes);
}
connParam值例子:
{
"media":"mysql",
"host":"localhost",
"port":3306,
"user":"root",
"password":"",
"database":"db_orm",
"table":"o_user"
}
indexes值例子:
[
'.id',
'.gender',
'.location.lng', //对象里某个字段索引
'.extraNumber',
'.arr[*]', //数组索引
'.friends[*].fid' //数组索引
]
插件名
实现静态media
,返回值与路由文件里shards
里配置的media
字符值相同
static get media() { //set media name
return 'redis';
}
方法
|方法|入参|有结果返回|无结果返回|必须实现|作用| |--|--|--|--|--|--| |objectGet|(id)|object|undefined|是|获取对象记录| |objectSet|(id, value)|无|无|是|添加/更新对象记录| |objectHas|(id)|true|false|是|查询对象记录是否存在| |objectDel|(id)|无|无|是|删除对象记录| |objectArrayNodeAppend|(id, path, items)|无|无|否|给某条记录下的数组节点尾部添加一个或多个元素| |objectArrayNodeUnshift|(id, path, items)|无|无|否|给某条记录下的数组节点尾部添加一个或多个元素| |objectArrayNodeInsert|(id, path, index, item)|无|无|否|给某条记录下的数组节点某个索引位置插入一个元素| |objectArrayNodeUnshift|(id, path, items)|object|undefined|否|给某条记录下的数组节点头部添加一个或多个元素| |objectArrayNodeDel|(id, path, index)|无|无|否|删除某条记录下的数组节点里指定位置的元素| |objectArrayNodePop|(id, path)|object|undefined|否|弹出某条记录下的数组节点尾部一个元素| |objectArrayNodeShift|(id, path)|object|undefined|否|弹出某条记录下的数组节点头部一个元素| |objectArrayNodeShift|(id, path)|object|undefined|否|弹出某条记录下的数组节点头部一个元素| |objectFind|({where, sort, limit, group})|array|[]|否|查找所有符合规则的对象,支持排序、分页、分组| |objectFieldFind|({field, where, sort, limit, group})|array|[]|否|查找所有符合规则的对象,支持排序、分页、分组、查询单个字段| |objectCount|(where,group)|integer|0|否|统计所有符合规则的对象数量| |objectQuery|(sql)|array|0|否|执行原生sql| |relationFetch|(subject, object)|object|undefined|是|返回关系中某个对象| |relationPut|(relation)|无|无|是|将某个对象放入关系中| |relationRemove|(subject, object)|无|无|是|从关系中移除某个对象| |relationHas|(subject, object)|true|false|是|判断关系中含有某个对象| |relationList|(subject, sort?, limit?, filter?)|array|[]|是|返回关系中的对象,可筛选、排序、分页| |relationCount|(subject, filter?)|integer|0|是|统计关系中的对象,可筛选| |relationClear|(subject)|无|无|是|清空关系里所有对象|
支持的高级数据操作配置
媒介可以不实现上一小节某些数据高级操作功能,此时必须在介质配置里时关闭该功能支持,否则框架调用该函数会导致报错.默认都不支持
get support() {
return {
objectFind: false,
objectCount: false,
objectArrayNodeAppend: false,
objectArrayNodeUnshift: false,
objectArrayNodeInsert: false,
objectArrayNodeDel: false,
objectArrayNodePop: false,
objectArrayNodeShift: false
}
}
解析Logic
逻辑分where
,sort
,limit
三类,objectFind
,objectCount
,relationList
, relationCount
函数传入的是标准逻辑对象,需要开发者自行转化成对应媒介数据的操作逻辑.所有标准逻辑对象都在ORM.Logic.Type
定义
|标准对象|属性|示例(按照属性栏的顺序)| |--|--|--| |WhereAnd|items|标准逻辑对象数组| |WhereOr|items|标准逻辑对象数组| |WhereNot|item|标准逻辑对象| |WhereIn|field, items|'.a', [1, 2, 3]| |WhereBetween|field, from, to|'.a', 1, 2| |WhereLike|field, value|'.a', '%a%'| |WhereEq|field, value|'.a', 1| |WhereNq|field, value|'.a', 1| |WhereGt|field, value|'.a', 1| |WhereGe|field, value|'.a', 1| |WhereLt|field, value|'.a', 1| |WhereLe|field, value|'.a', 1| |WhereIsUndef|field|'.a'| |WhereIsDef|field|'.a'| |WhereContain|field, value|'.arr[]', 1| |WhereContainBoolean|field, value|'.arr[]', 1| |Sort|field, order|'.a', 'DESC'| |Limit|limit, skip|1,1| |Field|field, alias|'.name','nameS'| |Group|field|'.name'|
Redis媒介插件代码
请移步这里[github才能支持]
更新日志
- 2019-06-08: Logic对象支持转换为json对象。同时在原使用Logic对象的地方支持直接传入json对象进行查询
- 2019-09-30:
- 重写数据默认值填充器
- 更新数据校验器为qtk-validator,提示更加友好
- core部分代码整理,优化执行速度
- 去除数据取操作时做数据校验
- bug修复
- 增加orm_rebuild_column_index重建表列/索引命令
- 2020-04-26:
- Logic增加
WhereIsUndef
,WhereIsDef
- Logic增加
致谢
schema语法引用的是semantic-schema项目代码,感谢Magnus同学的支持