3.4.1 • Published 2 years ago

@qtk/orm-framework v3.4.1

Weekly downloads
4
License
ISC
Repository
github
Last release
2 years ago

qtk-orm-framework

qtk-orm-framework是一个Node版的K-V型数据操作框架,支持一对一,一对多两种关系,存储目前支持MysqlRedis两种类型.框架致力于提供一套简单的API去操作K-V结构数据,使用者只需定义一份数据描述文件数据库配置文件,框架将会帮你实现建库建表(Mysql)建索引(Mysql)增删改查数据分表分库数据校验数据缓存数据热迁移. 框架有两个核心概念ObjectRelation

  • Object: 存储一对一关系的实体,可以将其理解为Document(文档),数据中必须包含id字段,主键为id
  • Relation: 存储一对多关系的实体,可以将其理解为Collection(容器),一个容器里面有多个Document(文档),数据中必须包含subjectobject字段,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

提供一套标准的查询、排序、分页语法,在Objectfindcount,Relationlistcount中可以使用

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))
whereNotitemwhereNot(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)
fieldselect 单个字段(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的getfind,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 : 根据实例不同的情况可以拥有不同的propertiespatternPropertiesrequirerequireAll定义
    • string/number/integer
      • index() : 给对应的Key加索引.目前支持stringintegernumberarray里的元素类型,对于非数组元素里的string类型,必须设置length或者maxLength. 由于Objectkeyword的id,Relationkeyword的subjectobject默认设置了索引,故当其为string类型时,必须设置长度.而对于数组元素里的string类型则不用设置长度,因为采用的是全文索引
  • 语法糖

sugarequivalent
1integer().enum(1)
1.1number().enum(1.1)
'foo'string().enum('foo')
/^foo|bar$/string().pattern(/^foo|bar$/)
trueboolean().enum(true)
nullNULL() or empty()
{foo: 1}object().properties({foo: 1}).requiredAll().additionalProperties(false)
1, 2, 3integer().enum(1, 2, 3)
1.1, 2.2, 3number().enum(1.1, 2.2, 3)
'foo', 'bar'string().enum('foo', 'bar')
true, falseboolean().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).框架主力使用当前路由配置,万一同级目录下存在同名废弃路由的话,使用过程中会对数据进行热迁移. 每份路由可配置persistencecache节点,每个节点下配置分片信息shards及分片规则hash.persistence顾名思义就持久化存储,目前框架内置支持mysql,而cache内置支持redis. shards: 数组,每个元素表示一个分片信息 hash: 哈希函数,输入参数是id.在对某条数据进行操作时,框架将给哈希函数传入数据的id(Object是id字段,Relation是subject字段),函数根据id映射到shards某个分片上 若persistencecache同时配置的话,那么读操作会优先操作cache(除了Objectfindcount,Relationlistcount),若有数据则立即返回,若无则会继续查询persistence,若有数据,在返回数据同时也同步一份到cache中.写操作则是等待两边操作完毕后才返回

具体热迁移逻辑如下:

模式方法处理逻辑
Objectget优先查当前,若无则查废弃,有结果则同步该数据到当前
Objectset只写当前
Objecthas同Object.get
Objectdel当前、废弃同时删除
ObjectarrayNodeAppend首先Object.has检查,其顺带数据迁移,之后只写当前
ObjectarrayNodeUnshift首先Object.has检查,其顺带数据迁移,之后只写当前
ObjectarrayNodeInsert首先Object.has检查,其顺带数据迁移,之后只写当前
ObjectarrayNodeDel首先Object.has检查,其顺带数据迁移,之后只写当前
ObjectarrayNodePop首先Object.has检查,其顺带数据迁移,之后只写当前
ObjectarrayNodeShift首先Object.has检查,其顺带数据迁移,之后只写当前
Objectfind优先查当前,若无则查废弃,有结果则返回但不做同步
ObjectfieldFind优先查当前,若无则查废弃,有结果则返回但不做同步
Objectcount同Object.find
Relationfetch同Object.get
Relationput同Object.set
Relationhas同Object.has
Relationremove同Object.del
Relationclear同Object.del
Relationlist同Object.find

注意(坑)

  • schema定义文件若给字段设置默认值, 那么该字段也应是必须的,否则不会自动补上默认值
  • schema定义文件若给字段设置默认值且数据库里该字段值为undefined时,在进行排序、查找操作时,使用的值是数据库里的值,即undefined而不是默认值
  • Objectfindcount,Relationlist,count考虑到可能会涉及多条数据情况,为了不影响性能,故上述方法不支持数据热迁移
  • 当Router里配置cachepersistence时,即开启数据缓存功能(加速读),此情况存在有些数据还未加载到cache情况,故Objectfindcount、Relationlistcount将会直接读取persistence来保证数据的准确性
  • Objectfind提供的是Object搜索功能,若Router里存在多个shards时,即开启数据分片功能,由于无法根据id定位到具体的分片,故会查询所有的分片信息,并合并结果返回.在此种情况下无法进行分页排序,故不支持sortlimit
  • 使用whereContain时,其实现原理是全文搜索,故需要保证字段内容长度 >=ft_min_word_len ,数据库默认ft_min_word_len为4

自定义存储介质

框架内部支持mysql与redis两种存储介质,若想换一种存储媒介或者重新设计底层数据结构的话,那么可以继承框架的BackendMedia基类,实现指定的函数功能后,以插件的形式注册到框架上.那么即可无痛更换,而不用修改任何代码.

构造函数

负责初始化存储媒介的链接.构造函数有两个参数:

connParam: 经过哈希函数计算得到的分片信息

indexes: 该ObjectRelation的字段索引列表

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)objectundefined获取对象记录
objectSet(id, value)添加/更新对象记录
objectHas(id)truefalse查询对象记录是否存在
objectDel(id)删除对象记录
objectArrayNodeAppend(id, path, items)给某条记录下的数组节点尾部添加一个或多个元素
objectArrayNodeUnshift(id, path, items)给某条记录下的数组节点尾部添加一个或多个元素
objectArrayNodeInsert(id, path, index, item)给某条记录下的数组节点某个索引位置插入一个元素
objectArrayNodeUnshift(id, path, items)objectundefined给某条记录下的数组节点头部添加一个或多个元素
objectArrayNodeDel(id, path, index)删除某条记录下的数组节点里指定位置的元素
objectArrayNodePop(id, path)objectundefined弹出某条记录下的数组节点尾部一个元素
objectArrayNodeShift(id, path)objectundefined弹出某条记录下的数组节点头部一个元素
objectArrayNodeShift(id, path)objectundefined弹出某条记录下的数组节点头部一个元素
objectFind({where, sort, limit, group})array[]查找所有符合规则的对象,支持排序、分页、分组
objectFieldFind({field, where, sort, limit, group})array[]查找所有符合规则的对象,支持排序、分页、分组、查询单个字段
objectCount(where,group)integer0统计所有符合规则的对象数量
objectQuery(sql)array0执行原生sql
relationFetch(subject, object)objectundefined返回关系中某个对象
relationPut(relation)将某个对象放入关系中
relationRemove(subject, object)从关系中移除某个对象
relationHas(subject, object)truefalse判断关系中含有某个对象
relationList(subject, sort?, limit?, filter?)array[]返回关系中的对象,可筛选、排序、分页
relationCount(subject, filter?)integer0统计关系中的对象,可筛选
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定义

标准对象属性示例(按照属性栏的顺序)
WhereAnditems标准逻辑对象数组
WhereOritems标准逻辑对象数组
WhereNotitem标准逻辑对象
WhereInfield, items'.a', 1, 2, 3
WhereBetweenfield, from, to'.a', 1, 2
WhereLikefield, value'.a', '%a%'
WhereEqfield, value'.a', 1
WhereNqfield, value'.a', 1
WhereGtfield, value'.a', 1
WhereGefield, value'.a', 1
WhereLtfield, value'.a', 1
WhereLefield, value'.a', 1
WhereIsUndeffield'.a'
WhereIsDeffield'.a'
WhereContainfield, value'.arr*', 1
WhereContainBooleanfield, value'.arr*', 1
Sortfield, order'.a', 'DESC'
Limitlimit, skip1,1
Fieldfield, alias'.name','nameS'
Groupfield'.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

致谢

schema语法引用的是semantic-schema项目代码,感谢Magnus同学的支持

3.3.9

2 years ago

3.4.0

2 years ago

3.4.1

2 years ago

3.3.8

2 years ago

3.3.7

2 years ago

3.3.6

2 years ago

3.3.5

3 years ago

3.3.4

3 years ago

3.3.3

4 years ago

3.3.2

4 years ago

3.3.1

4 years ago

3.3.0

4 years ago

3.2.9

4 years ago

3.2.8

4 years ago

3.2.7

5 years ago

3.2.6

5 years ago

3.2.5

5 years ago

3.2.4

5 years ago

3.2.3

5 years ago

3.2.2

5 years ago

3.2.1

5 years ago

3.2.0

5 years ago

3.1.7

5 years ago

3.1.6

5 years ago

3.1.5

5 years ago

3.1.4

5 years ago

3.1.3

5 years ago

3.1.2

5 years ago

3.1.1

5 years ago

3.1.0

5 years ago

3.0.0

5 years ago

2.0.5

6 years ago

2.0.4

6 years ago

2.0.3

6 years ago

2.0.2

6 years ago

2.0.1

6 years ago

2.0.0

6 years ago

1.0.15

6 years ago

1.0.14

6 years ago

1.0.13

6 years ago

1.0.12

6 years ago

1.0.11

6 years ago

1.0.10

6 years ago

1.0.9

6 years ago

1.0.8

6 years ago

1.0.7

6 years ago

1.0.6

6 years ago

1.0.5

6 years ago

1.0.3

6 years ago

1.0.2

6 years ago