diff --git a/Readme.md b/Readme.md index 33bd64f..e3b7e7d 100644 --- a/Readme.md +++ b/Readme.md @@ -2,19 +2,24 @@ # mysqli -> 本模块基于 node-mysql 模块二次封装,将SQL语法转为类似MongoDB的API。对常用的增删改查提供了简单的API, 并且进行了SQL注入过滤, 对新手非常友好。 +> 本模块基于 node-mysql 模块二次封装,将 SQL 语法转为类似 MongoDB 的 API。对常用的增删改查提供了简单的 API, 并且进行了 SQL 注入过滤, 对新手非常友好。 ## 使用 npm 安装 ```bash -npm install mysqli +# 3.x 版的安装 +npm i mysqli +# or +npm i mysqli@3.x + + +# 2.x 旧版的安装 +npm i mysqli@2.x ``` ## 实例化 -> 实例化可以传入一个数组,或单个object配置。只有一个数据库时,默认 -> 是主库 ; 多于 1 个数据库服务时,自动以第 1 个为主库,其他的从库,故实例化时 -> ,`注意顺序`。 +> 实例化可以传入一个数组,或单个 object 配置。只有一个数据库时,默认是主库 ; 多于 1 个数据库服务时,自动以第 1 个为主库,其他的从库,故实例化时,`注意顺序`。 ```javascript let Mysqli = require('mysqli') @@ -50,175 +55,7 @@ let conn = new Mysqli([ ]) ``` -## API 方法 +## 文档 -### 1. escape(val) - -> 这是`node-mysql`的内部方法,用于进行 SQL 安全过滤,这里只是做了搬运工,把它暴 -> 露出来给外部调用而已。 **这个是静态方法** - -```javascript -const Mysqli = require('mysqli') - -Mysqli.escape('这是文本') -``` - -### 2. emit(isSlave, dbName) - -* isSlave `` 可选 -* dbName `` 可选。 如果在实例化连接时 , 已经传入 db, 则这里可不传值。 - -> 触发一个数据库实例 , 可接受 2 个参数 , 第 1 个为 " 是否从库 ", 第 2 个为 " 数 -> 据库名称 " -> `这一步是必须要的, ` -```javascript -const Mysqli = require('mysqli') -let conn = new Mysqli({ - /*...*/ -}) -let db = conn.emit(true, 'test') -``` - -### 3. listDb() - -> 顾名思义,该方法即用于列举当前账号权限范围内的所有的数据库名称,返回值是一个数 -> 组 ; -> -> **注:**`方法返回的是一个 Promise 对象` - -```javascript -db.listDb().then(list => { - console.log(list) -}) -``` - -### 4. listTable() - -> 该方法用于列举当前数据库数据表的集合 - -```javascript -db.listTable().then(list => { - console.log(list) -}) -``` - -### 5. query(sql) - -* sql `` - -> 该方法用于当内置的方法满足不了需求时,可以自行编写`sql 语句`执行 ; 但要注意防 -> 止`sql 注入`,因为该方法是最基础的方法,模块不对传入的`sql 语句`进行任何的安全 -> 过滤。 - -```javascript -// 不使用await指令时,返回的是Promise对象 -db.query(`select * from users limit 10`).then(row => { - console.log(row) -}) -``` - -### 6. find(condition, select) - -* condition ``, 查询条件 -* select ``, 要返回字段 , 默认全部返回 - -> 该方法用于查询多条数据。无论结果是多少条,返回的都是`数组格式`; 详细请看下面代 -> 码示例: - -```javascript -db - .find( - { - table: '', // 要查询的表 - where: {foo: 'bar', goo: {$like: 'hello%'}}, //条件, 语法类似mongodb - sort: { - //排序, key是要排序的字段,value是排序方式, 1顺序,-1逆序 【可选】 - a: 1, - b: -1 - }, - limit: [0, 1] // 查询范围,可用于分页 【可选】 - }, - ['id', 'name'] //要select的字段 - ) - .then(row => { - console.log(row) - }) -``` - -### 7. findOne(condition) - -* condition `` - -> 该方法与上面的`filter`方法的使用一致,区别只是该方法只返回一条数据,且为`json -> 格式`。 - -### 8. count(condition) - -* condition `` - -> 该方法与上面的`filter`方法的使用一致,不过返回的是条目总数 (``) - -### 9. insert(condition, doc) - -* condition ``, 插入的条件 -* doc ``, 插入的数据 - -> 该方法与上面的`filter`方法的使用类似,手于插入一条数据,具体请看下面代码 ; -> -> **注:**`该方法一次只能插入一条数据` - -```javascript -// 如果主键是自增ID,则结果返回的是 刚插入的数据的自增ID -db - .insert( - { - table: 'xx' - }, - { xx: 1234 } //要插入的数据 - ) - .then(id => { - console.log(id) - }) -``` - -### 10. update(condition, doc) - -* condition ``, 条件 -* doc ``, 数据 - -> 该方法与上面的`filter`方法的使用类似,用于更新数据,具体请看下面代码 ; `该方法返 -> 回的是被修改的条目数量` - -```javascript -// 如果修改成功,则返回被修改的数量 -db - .update( - { - table: 'xx' - }, - { xx: 1234 } //要插入的数据 - ) - .then(num => { - console.log(num) - }) -``` - -### 11. remove(condition) - -* condition `` - -> 该方法与上面的`update`方法的使用类似,用于删除指定条件的数据 ; 具体请看下面代码 -> ; `该方法返回的是被删除的条目数量` - -```javascript -// 如果修改成功,返回的是被删除的条目数量 -db - .remove( - { - table: 'xx' - } - ) - .then(num => { - console.log(num) - }) -``` +* [2.x 版文档](docs/2.x.md) +* [3.x 版文档](docs/3.x.md) diff --git a/docs/2.x.md b/docs/2.x.md index 33bd64f..77fc1bd 100644 --- a/docs/2.x.md +++ b/docs/2.x.md @@ -1,15 +1,3 @@ -![module info](https://nodei.co/npm/mysqli.png?downloads=true&downloadRank=true&stars=true) - -# mysqli - -> 本模块基于 node-mysql 模块二次封装,将SQL语法转为类似MongoDB的API。对常用的增删改查提供了简单的API, 并且进行了SQL注入过滤, 对新手非常友好。 - -## 使用 npm 安装 - -```bash -npm install mysqli -``` - ## 实例化 > 实例化可以传入一个数组,或单个object配置。只有一个数据库时,默认 diff --git a/docs/3.x.md b/docs/3.x.md index e69de29..b792a20 100644 --- a/docs/3.x.md +++ b/docs/3.x.md @@ -0,0 +1,554 @@ +## 实例化 + +> 实例化可以传入一个数组,或单个 object 配置。只有 1 个数据库时,默认是主库; +> 多于 1 个数据库服务时,自动以第 1 个为主库,其他的从库,故实例化时`注意顺序`。 + +```javascript +let Mysqli = require('mysqli') + +//传入json +let conn = new Mysqli({ + host: '', // IP/域名 + post: 3306, //端口, 默认 3306 + user: '', //用户名 + passwd: '', //密码 + charset: '', // 数据库编码,默认 utf8 【可选】 + db: '' // 可指定数据库,也可以不指定 【可选】 +}) + +// 传入数组 +let conn = new Mysqli([ + { + host: 'host1', // IP/域名 + ... + }, + ... +]) +``` + +## 实例 API + +### 静态方法 escape(val) + +> 可以对 sql 的各种类型的值进行安全转义。 + +### emit([slave], [db]) + +* slave [可选]是否"从库", 默认为 false, +* db [可选]要连接的数据库名, 默认为空 + +> 返回一个 db 实例, 这个方法是必须要的, 所有的 sql 操作,都要先实例一个 db 对象。 + +```javascript +let db = conn.emit(false, 'test') +db + .tableList() + .then(list => { + log(list) + }) + .catch(err => { + log(err) + }) +``` + +## DB API + +> 是指直接对 db 进行操作的接口。如果没有特别说明,以下所有的方法, 返回的都是一个 Promise 对象。 + +### query(sql) + +* sql sql 语句, [必传] + +> 直接执行 sql 语句, 用于在内置的 API 不满足需求的情况下, 可以自己手写 sql 执行。 + +```javascript +db.query('select * from `student` limit 10').then(result => { + log(result) +}) + +// 前面没有指定数据库时, 也可以在这里写上 +db.query('select * from `test`.`student` limit 10').then(result => { + log(result) +}) +``` + +### drop(db) + +* db [可选] 要删除 db 名, 不传则删除当前连接的数据库 + +> 删除当前或指定数据库。 + +```javascript +db.drop() + +// 也可以删除指定的数据库 +db.drop('foo') +``` + +### dbList() + +> 返回当前账号权限下的所有的数据名(数组) + +```javascript +db.dbList().then(list => { + log(list) +}) +``` + +### tableList() + +> 返回当前连接的数据库下的所有的表名(数组) + +```javascript +db.tableList().then(list => { + log(list) +}) +``` + +### dbCreate(name, options) + +* name [必传], 数据库名字 +* options , [必传], 数据库的配置 + * charset 默认 utf8 + +> 创建新的数据库 + +```javascript +db.dbCreate('foo', { charset: 'utf8mb4' }) // 默认是utf8 +``` + +### tableCreate(name, columns ,options) + +* name [必传], 表名字 +* columns , [必传], 表字段的配置 + * name 字段名, 区分大小写, 建议全小写+下划线 + * type 字段类型, 不区分大小写 + * primary 是否主键(有且只能有 1 个主键) + * inc 是否自增(只允许主键设置,且为整型时才可设置) + * notnull 是否允许非空 + * index 是否设为索引 + * unique 是否为 唯一索引 + * default 设置默认值 + * update 是否自动更新(只有 datetime & timestamp 可以设) +* options , [必传], 表的配置 + * charset 默认 utf8 + * engine 默认 InnoDB + +> 创建新的数据表 + +```javascript +db.tableCreate('student', + [ + { + name: 'id', + type: 'int(5)', + primary: true, + inc: true + }, + { + name: 'name', + type: 'varchar(64)', + index: true + }, + { + name: 'age', + type: 'int(3)' + }, + { + name: 'sex', + type: 'tinyint(1)', + index: true + }, + ... + ] + {charset: 'utf8mb4'}) // 默认是utf8 +``` + +### table(name) + +* name [必传], 数据表名字 + +> 选择指定表。 返回一个表操作的 API 实例 + +```javascript +let table = db.table('student') // +``` + +## TABLE API + +> 是指直接对 table 进行操作的接口。 + +### leftJoin(tables) + +* tables 左联, 可以联多个表 + * table 表名 + * on 左联的条件 + +```javascript +db.table('student') + .leftJoin([ + { + table: 'classroom', + on: 'student.classroom = classroom.id' + }, + ... + ]) +``` + +### rightJoin(tables) + +> 参考 leftJoin() + +### join(tables) + +> 参考 leftJoin() + +### filter(options) + +* options + +> 查询过滤, 这个方法是增删改查中使用率最高的。 也是 Mysqli 模块的核心方法之一。也是参数最复杂的方法, 一条查询里, 只能出现一次 filter, 多次调用, 会覆盖之前的条件. +> options 里有几个特殊的关键字 +> +> * $and (逻辑"且") +> * $or (逻辑"或") +> * $like 模糊查询 +> * $sql 以某个 sql 语句的结果作为查询条件 +> * $in 包含 +> * $between 在某某值和某某值之间 +> * $lt 小于 +> * $lte 小于等于 +> * $gt 大于 +> * $gte 大于等于 +> * $eq 等于 + +```javascript +db + .table('student') + .filter({ id: 1234 }) // 最基本的过滤查询, 即条件为 找"id=1234"的 + + .filter({ name: { $like: '李%' } }) // 模糊查询, 找所有"李姓的学生" + + // 现有的API不满足时, 可以自己写sql条件, 更复杂自己根据需求写即可 + .filter({ name: { $sql: 'IS NULL' } }) + + .filter({ id: { $in: [11, 13, 29] } }) // 查询id在给定的这几个值的所有学生 + + .filter({ age: { $between: [15, 17] } }) // 查询年龄在 15~17岁的所有学生 + + .filter({ age: { $lt: 16 } }) // 查询年龄小于16岁的所有学生 + .filter({ age: { $lt: 16, $gt: 13 } }) // 查询年龄小于16岁且大于13岁的所有学生 + + // ***** 以下是多条件的示例 ********* + // 查询所有姓李的,年龄15岁以上, 且为男生(假设 男生是1,女生是2) + .filter({ name: { $like: '李%' }, age: { $gt: 15 }, sex: 1 }) + + // **** 从上面的示例可以看出,多个字段时, 自动为"AND" 查询 **** + // **** 所以下面该 $and 和 $or 出场了 **** + + // 查询所有 姓李的, 或 年龄在15岁以上的 + .filter({ + $or: [{ name: { $like: '李%' } }, { age: { $gt: 15 } }] + }) + + // 姓李的 或 年龄15岁以上且为男生的 + .filter({ + $or: [{ name: { $like: '李%' } }, { age: { $gt: 15 }, sex: 1 }] + }) + + // 姓李的或15岁以上的, 且必须是男生的 + .filter({ + $and: [ + { + $or: [{ name: { $like: '李%' } }, { age: { $gt: 15 } }] + }, + { sex: 1 } + ] + }) + +// filter基本上满足了你日常绝大部分的查询需求, 如果还有没满足的, 可以自己写sql语句. +``` + +### sort(keys) + +* keys + +> 对结果集排序 + +```javascript +db + .table('student') + .sort({ age: -1 }) // -1为逆序, 1为正序 + + // 先排年龄(逆序), 再排学号(正序) + .sort({ age: -1, id: 1 }) +``` + +### skip(num) + +* num + +> 跳过指定条数, 可用于分页。 + +```javascript +db.table('student').skip(10) // 跳过前9条, 即从第10条结果开始返回 +``` + +### limit(num) + +* num + +> 限制返回的结果总数, 做分页时, 配合 skip 方法使用。 + +```javascript +db.table('student').limit(10) // 限制只返回10条记录 +``` + +### slice(start, end) + +* start +* end + +> 截取指定范围的结果集, 这是 skip & limit 结合体。 + +```javascript +db + .table('student') + .slice(11, 20) // 限制只返回10条记录 + + // 等价于 + .skip(11) + .limit(10) +``` + +### withFields(fields) + +* fields [可选], 不传则返回所有的字段 + +> 指定要返回的字段 + +```javascript +db.table('student').withFields(['id', 'name', 'sex']) // 只取 学号,姓名,性别3个字段返回 +``` + +### getAll([ids]) + +* ids [可选],返回指定 id 的结果集; 不传则返回所有结果集。(仅当没有调用过 filter 的情况下,本参数才有效) + +> 返回符合条件的所有记录, 这个方法是 查询必须要调用的方法之一, 上面 .filter, .sort 等方法, 只是组件条件, 并不会返回结果, 只有调用了 getAll/get 之后, 才会返回结果集。 +> `这里有一个地方要注意的, 传入的参数ids, 即意味着, 该数据表里, 必须有一个字段为"id", 否则会出错, 没有id的表, 查询时请使用 .filter过滤。` + +```javascript +db + .table('student') + .withFields(['id', 'name', 'sex']) + .getAll() + .then(list => { + log(list) + }) + +// 以下是查询学号为 11,13,28的学生 +db + .table('student') + .withFields(['id', 'name', 'sex']) + .getAll([11, 13, 28]) + .then(list => { + log(list) + }) + +// 等价于 +db + .table('student') + .withFields(['id', 'name', 'sex']) + .filter({ id: { $in: [11, 13, 28] } }) + .getAll() + .then(list => { + log(list) + }) + +// 在getAll()方法里传参数, 只是一种快捷的filter, +// 没有"id"字段的表, 请使用 .filter()方法来查询 +``` + +### get(id) + +* id [可选], 传入单个 ID。 + +> 该方法是对 getAll 的补充, 它最终是调用 getAll 来实现, 目的在于返回 单个结果集(不是数组)。 + +```javascript +// 以下是查询学号为 11的学生 +db + .table('student') + .withFields(['id', 'name', 'sex']) + .getAll(11) + .then(doc => { + log(doc) + }) + +// 等价于 +db + .table('student') + .withFields(['id', 'name', 'sex']) + .filter({ id: 11 }) + .get() + .then(doc => { + log(doc) + }) +``` + + +### count() + +> 该方法同样是对 getAll 的补充, 它最终是调用 getAll 来实现, 返回结果集的总数。 + +```javascript +// 返回所有姓李的学生的总数 +db + .table('student') + .filter({name: {$like: '李%'}}) + .count() + .then(total => { + log(total) + }) +``` + + +### insert(doc) +* doc 要插入的记录 + +> 用于插入单条记录。如果有自增ID的话, 将返回刚插入的记录的自增ID。 + +```javascript +db + .table('student') + .insert({ + name: '张三', + age: 18, + sex: 1, + ... + }) + .then(lastId => { + log(lastId) // 如果表结构里没有自增ID, 则这个返回值永远为0 + }) +``` + + +### update(doc) +* doc 要插入的记录 + +> 用于更新指定条件的记录, 返回成功修改的总数 + +```javascript +// 将张三的年龄改为 17岁 +db + .table('student') + .filter({name: '张三'}) + .update({ + age: 17 + }) + .then(successNum => { + log(successNum) + }) +``` + + +### remove() + +> 删除记录。返回成功删除的总数。 + +```javascript +// 将姓名为空的记录全删除了。 +db + .table('student') + .filter({name: {$sql: 'IS NULL'}}) + .remove() + .then(successNum => { + log(successNum) + }) +``` + + +### drop() + +> 删除当前数据表, 属于`危险操作`哦。 成功返回true。 + +```javascript +// 将学生表给删除了 +db + .table('student') + .drop() + .then(result => { + log(result) + }) +``` + + +### renameTo(name) + +> 重命名表。成功返回true。 + +```javascript +db + .table('student') + .renameTo('student_bac') + .then(result => { + log(result) + }) +``` + + +### indexList() + +> 返回当前表的索引列表. + +```javascript +db + .table('student') + .indexList() + .then(list => { + log(list) + // { + // name, + // column, + // unique, + // cardinality, + // collation, + // } + }) +``` + + +### indexDrop(name) +* name 索引名 + +> 删除当前表的指定索引. 成功返回true。 + +```javascript +// 将学生表给删除了 +db + .table('student') + .indexDrop('name_idx') + .then(result => { + log(result) + }) +``` + + +### indexDrop(name, options) +* name 索引名 +* options 索引的配置 + - field 该索引绑定的字段 + - unique 是否是唯一索引 + +> 给当前表创建索引. 成功返回true。 + +```javascript +// 将学生表给删除了 +db + .table('student') + .indexCreate('name_idx', {field: 'name'}) + .then(result => { + log(result) + }) +``` diff --git a/lib/method.js b/lib/method.js index 95379e4..9c1f584 100644 --- a/lib/method.js +++ b/lib/method.js @@ -301,6 +301,25 @@ class Method { }) } + drop() { + let sql = `DROP TABLE ${this.cache.table} ` + + return this.connect().then(conn => { + const defer = Promise.defer() + + conn.query(sql, (err, result) => { + conn.release() + if (err) { + return defer.reject(new SqlErr(`Drop table ${err}`, sql)) + } + + defer.resolve(true) + }) + + return defer.promise + }) + } + // 重命名表 renameTo(name) { return this.connect().then(conn => { @@ -337,8 +356,7 @@ class Method { column: it.Column_name, unique: !it.Non_unique, cardinality: it.Cardinality, - collation: it.Collation, - index: it.Seq_in_index + collation: it.Collation } }) diff --git a/tsconfig.json b/tsconfig.json deleted file mode 100644 index 6437eab..0000000 --- a/tsconfig.json +++ /dev/null @@ -1,19 +0,0 @@ -{ - "compilerOptions": { - "target": "es2015", - "lib": ["es2015"], - "module": "commonjs", - "outDir": "dist/", - "noImplicitAny": true, - "removeComments": false, - "preserveConstEnums": true, - "noUnusedLocals": true, - "noUnusedParameters": true, - "pretty": true, - "types" : ["node"], - "noEmitOnError": true, - - }, - "include": ["src/**/*"], - -} \ No newline at end of file