@axiosleo/orm-mysql 0.14.4 → 0.15.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +57 -416
- package/bin/orm-mysql.js +1 -1
- package/commands/skills.js +223 -0
- package/package.json +1 -1
- package/skills/SKILL.md +159 -0
- package/skills/crud-operations.md +225 -0
- package/skills/pagination.md +301 -0
- package/skills/query-building.md +159 -0
- package/skills/transactions.md +171 -0
- package/skills/where-conditions.md +190 -0
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const fs = require('fs');
|
|
4
|
+
const path = require('path');
|
|
5
|
+
const { Command, printer } = require('@axiosleo/cli-tool');
|
|
6
|
+
const { _exists, _copy, _read, _read_json, _mkdir } = require('@axiosleo/cli-tool/src/helper/fs');
|
|
7
|
+
|
|
8
|
+
const SKILL_FILES = [
|
|
9
|
+
'SKILL.md',
|
|
10
|
+
'query-building.md',
|
|
11
|
+
'where-conditions.md',
|
|
12
|
+
'crud-operations.md',
|
|
13
|
+
'transactions.md',
|
|
14
|
+
];
|
|
15
|
+
|
|
16
|
+
const SUPPORTED_TARGETS = ['cursor', 'claude', 'windsurf'];
|
|
17
|
+
|
|
18
|
+
const PKG_NAME = '@axiosleo/orm-mysql';
|
|
19
|
+
|
|
20
|
+
function getTargetDir(cwd, target) {
|
|
21
|
+
const map = {
|
|
22
|
+
cursor: path.join(cwd, '.cursor', 'skills', 'orm-mysql-usage'),
|
|
23
|
+
windsurf: path.join(cwd, '.windsurf', 'skills', 'orm-mysql-usage'),
|
|
24
|
+
};
|
|
25
|
+
return map[target] || null;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async function resolveSkillsSource(cwd) {
|
|
29
|
+
const localPkgJson = path.join(cwd, 'node_modules', PKG_NAME, 'package.json');
|
|
30
|
+
let localPkgFound = false;
|
|
31
|
+
let localVersion = null;
|
|
32
|
+
|
|
33
|
+
if (await _exists(localPkgJson)) {
|
|
34
|
+
localPkgFound = true;
|
|
35
|
+
const pkg = await _read_json(localPkgJson);
|
|
36
|
+
localVersion = pkg.version;
|
|
37
|
+
const skillsDir = path.join(cwd, 'node_modules', PKG_NAME, 'skills');
|
|
38
|
+
if (await _exists(skillsDir)) {
|
|
39
|
+
return { dir: skillsDir, version: localVersion, local: true, outdated: false };
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
const fallbackDir = path.join(__dirname, '..', 'skills');
|
|
44
|
+
if (await _exists(fallbackDir)) {
|
|
45
|
+
const fallbackPkg = path.join(__dirname, '..', 'package.json');
|
|
46
|
+
let version = 'unknown';
|
|
47
|
+
if (await _exists(fallbackPkg)) {
|
|
48
|
+
const pkg = await _read_json(fallbackPkg);
|
|
49
|
+
version = pkg.version;
|
|
50
|
+
}
|
|
51
|
+
return {
|
|
52
|
+
dir: fallbackDir,
|
|
53
|
+
version,
|
|
54
|
+
local: false,
|
|
55
|
+
outdated: localPkgFound,
|
|
56
|
+
localVersion,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
return null;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
class SkillsCommand extends Command {
|
|
63
|
+
constructor() {
|
|
64
|
+
super({
|
|
65
|
+
name: 'skills',
|
|
66
|
+
desc: 'Install or uninstall AI skills for coding assistants (Cursor, Claude Code, Windsurf)',
|
|
67
|
+
});
|
|
68
|
+
this.addOption('install', 'i', 'Install skills for target tool (cursor, claude, windsurf)', 'optional', '');
|
|
69
|
+
this.addOption('uninstall', 'u', 'Uninstall skills for target tool (cursor, claude, windsurf)', 'optional', '');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
async exec(args, options) {
|
|
73
|
+
const install = options.install;
|
|
74
|
+
const uninstall = options.uninstall;
|
|
75
|
+
|
|
76
|
+
if (!install && !uninstall) {
|
|
77
|
+
this.printUsage();
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
if (install) {
|
|
82
|
+
await this.install(install);
|
|
83
|
+
} else if (uninstall) {
|
|
84
|
+
await this.uninstall(uninstall);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
printUsage() {
|
|
89
|
+
printer.println();
|
|
90
|
+
printer.println('Usage:');
|
|
91
|
+
printer.println(' orm-mysql skills --install=<target> Install AI skills');
|
|
92
|
+
printer.println(' orm-mysql skills --uninstall=<target> Uninstall AI skills');
|
|
93
|
+
printer.println();
|
|
94
|
+
printer.println('Supported targets: ' + SUPPORTED_TARGETS.join(', '));
|
|
95
|
+
printer.println();
|
|
96
|
+
printer.println('Examples:');
|
|
97
|
+
printer.println(' npx @axiosleo/orm-mysql skills --install=cursor');
|
|
98
|
+
printer.println(' npx @axiosleo/orm-mysql skills --install=claude');
|
|
99
|
+
printer.println(' npx @axiosleo/orm-mysql skills --uninstall=cursor');
|
|
100
|
+
printer.println();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async install(target) {
|
|
104
|
+
if (!SUPPORTED_TARGETS.includes(target)) {
|
|
105
|
+
printer.error(`Unsupported target: "${target}". Supported targets: ${SUPPORTED_TARGETS.join(', ')}`);
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const cwd = process.cwd();
|
|
110
|
+
const source = await resolveSkillsSource(cwd);
|
|
111
|
+
|
|
112
|
+
if (!source) {
|
|
113
|
+
printer.error('Could not find skills files. Please reinstall @axiosleo/orm-mysql.');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (source.local) {
|
|
118
|
+
printer.info(`Found ${PKG_NAME}@${source.version} in node_modules`);
|
|
119
|
+
} else if (source.outdated) {
|
|
120
|
+
printer.warning(`${PKG_NAME}@${source.localVersion} is installed locally but does not include skills files.`);
|
|
121
|
+
printer.warning('Skills files are available since v0.15.1. Please update:');
|
|
122
|
+
printer.warning(` npm install ${PKG_NAME}@latest`);
|
|
123
|
+
printer.println();
|
|
124
|
+
printer.info(`Using skills from npx ${PKG_NAME}@${source.version} instead.`);
|
|
125
|
+
} else {
|
|
126
|
+
printer.warning(`${PKG_NAME} is not installed locally in this project.`);
|
|
127
|
+
printer.warning(`Consider running: npm install ${PKG_NAME}`);
|
|
128
|
+
printer.println();
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
printer.info(`Installing skills for ${target} from ${PKG_NAME}@${source.version}...`);
|
|
132
|
+
|
|
133
|
+
if (target === 'claude') {
|
|
134
|
+
await this.installClaude(cwd, source);
|
|
135
|
+
} else {
|
|
136
|
+
await this.installCopyTarget(cwd, target, source);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
async installCopyTarget(cwd, target, source) {
|
|
141
|
+
const targetDir = getTargetDir(cwd, target);
|
|
142
|
+
await _mkdir(targetDir);
|
|
143
|
+
await _copy(source.dir, targetDir, true);
|
|
144
|
+
printer.success(`Skills installed to ${path.relative(cwd, targetDir)}/`);
|
|
145
|
+
printer.println();
|
|
146
|
+
printer.println('Files installed:');
|
|
147
|
+
for (const file of SKILL_FILES) {
|
|
148
|
+
printer.println(` - ${file}`);
|
|
149
|
+
}
|
|
150
|
+
printer.println();
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async installClaude(cwd, source) {
|
|
154
|
+
const claudeFile = path.join(cwd, 'CLAUDE.md');
|
|
155
|
+
const header = `<!-- ${PKG_NAME}@${source.version} skills -->\n`;
|
|
156
|
+
const separator = '\n---\n\n';
|
|
157
|
+
|
|
158
|
+
let content = header;
|
|
159
|
+
for (const file of SKILL_FILES) {
|
|
160
|
+
const filePath = path.join(source.dir, file);
|
|
161
|
+
if (await _exists(filePath)) {
|
|
162
|
+
const fileContent = await _read(filePath);
|
|
163
|
+
content += separator + fileContent.trim() + '\n';
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (await _exists(claudeFile)) {
|
|
168
|
+
const existing = await _read(claudeFile);
|
|
169
|
+
if (existing.includes(`<!-- ${PKG_NAME}`) && existing.includes('skills -->')) {
|
|
170
|
+
printer.warning('CLAUDE.md already contains @axiosleo/orm-mysql skills.');
|
|
171
|
+
printer.warning('Please remove the existing skills section first, or run:');
|
|
172
|
+
printer.warning(' npx @axiosleo/orm-mysql skills --uninstall=claude');
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
fs.appendFileSync(claudeFile, '\n' + content);
|
|
178
|
+
printer.success(`Skills appended to ${path.relative(cwd, claudeFile)}`);
|
|
179
|
+
printer.println();
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
async uninstall(target) {
|
|
183
|
+
if (!SUPPORTED_TARGETS.includes(target)) {
|
|
184
|
+
printer.error(`Unsupported target: "${target}". Supported targets: ${SUPPORTED_TARGETS.join(', ')}`);
|
|
185
|
+
return;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const cwd = process.cwd();
|
|
189
|
+
|
|
190
|
+
if (target === 'claude') {
|
|
191
|
+
this.uninstallClaude(cwd);
|
|
192
|
+
return;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
const targetDir = getTargetDir(cwd, target);
|
|
196
|
+
if (await _exists(targetDir)) {
|
|
197
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
198
|
+
printer.success(`Skills removed from ${path.relative(cwd, targetDir)}/`);
|
|
199
|
+
} else {
|
|
200
|
+
printer.warning(`No skills found at ${path.relative(cwd, targetDir)}/`);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
uninstallClaude(cwd) {
|
|
205
|
+
const claudeFile = path.join(cwd, 'CLAUDE.md');
|
|
206
|
+
if (!fs.existsSync(claudeFile)) {
|
|
207
|
+
printer.warning('CLAUDE.md not found.');
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const content = fs.readFileSync(claudeFile, 'utf8');
|
|
211
|
+
const startMarker = `<!-- ${PKG_NAME}`;
|
|
212
|
+
const idx = content.indexOf(startMarker);
|
|
213
|
+
if (idx === -1) {
|
|
214
|
+
printer.warning('No @axiosleo/orm-mysql skills section found in CLAUDE.md.');
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
const cleaned = content.substring(0, idx).trimEnd() + '\n';
|
|
218
|
+
fs.writeFileSync(claudeFile, cleaned);
|
|
219
|
+
printer.success('Skills section removed from CLAUDE.md');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
module.exports = SkillsCommand;
|
package/package.json
CHANGED
package/skills/SKILL.md
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: orm-mysql-usage
|
|
3
|
+
description: Build MySQL queries, perform CRUD operations, and manage transactions using @axiosleo/orm-mysql. Use when writing database queries, building where conditions, inserting/updating/deleting rows, managing transactions, or working with the ORM query builder in this project.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# @axiosleo/orm-mysql Usage Guide
|
|
7
|
+
|
|
8
|
+
## Installation
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
npm install @axiosleo/orm-mysql
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
|
|
16
|
+
### Create a Connection
|
|
17
|
+
|
|
18
|
+
```javascript
|
|
19
|
+
const { createClient, QueryHandler } = require("@axiosleo/orm-mysql");
|
|
20
|
+
|
|
21
|
+
const conn = createClient({
|
|
22
|
+
host: "localhost",
|
|
23
|
+
port: 3306,
|
|
24
|
+
user: "root",
|
|
25
|
+
password: "password",
|
|
26
|
+
database: "my_db",
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
const db = new QueryHandler(conn);
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### Create a Connection Pool (recommended for production)
|
|
33
|
+
|
|
34
|
+
```javascript
|
|
35
|
+
const { createPool, QueryHandler } = require("@axiosleo/orm-mysql");
|
|
36
|
+
|
|
37
|
+
const pool = createPool({
|
|
38
|
+
host: "localhost",
|
|
39
|
+
port: 3306,
|
|
40
|
+
user: "root",
|
|
41
|
+
password: "password",
|
|
42
|
+
database: "my_db",
|
|
43
|
+
connectionLimit: 10,
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
const db = new QueryHandler(pool);
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### Using MySQLClient
|
|
50
|
+
|
|
51
|
+
```javascript
|
|
52
|
+
const { MySQLClient } = require("@axiosleo/orm-mysql");
|
|
53
|
+
|
|
54
|
+
const client = new MySQLClient({
|
|
55
|
+
host: "localhost",
|
|
56
|
+
port: 3306,
|
|
57
|
+
user: "root",
|
|
58
|
+
password: "password",
|
|
59
|
+
database: "my_db",
|
|
60
|
+
}, null, "pool"); // "default" | "promise" | "pool"
|
|
61
|
+
|
|
62
|
+
const rows = await client.table("users").select();
|
|
63
|
+
await client.close();
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
## Class Hierarchy
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
QueryCondition -- where clauses (where, whereIn, whereLike, whereBetween...)
|
|
70
|
+
└── Query -- query building (table, attr, join, orderBy, limit, page...)
|
|
71
|
+
└── QueryOperator -- execution (select, find, insert, update, delete...)
|
|
72
|
+
└── TransactionOperator -- adds append() for row locking
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
- `QueryHandler` wraps a connection/pool and creates `QueryOperator` via `.table(name)`
|
|
76
|
+
- `TransactionHandler` wraps a promise connection and creates `TransactionOperator` via `.table(name)`
|
|
77
|
+
|
|
78
|
+
## Quick Start
|
|
79
|
+
|
|
80
|
+
```javascript
|
|
81
|
+
const db = new QueryHandler(conn);
|
|
82
|
+
|
|
83
|
+
// SELECT
|
|
84
|
+
const users = await db.table("users")
|
|
85
|
+
.where("age", ">", 18)
|
|
86
|
+
.orderBy("name", "asc")
|
|
87
|
+
.limit(10)
|
|
88
|
+
.select("id", "name", "age");
|
|
89
|
+
|
|
90
|
+
// INSERT
|
|
91
|
+
await db.table("users").insert({ name: "Joe", age: 25 });
|
|
92
|
+
|
|
93
|
+
// UPDATE
|
|
94
|
+
await db.table("users").where("id", 1).update({ age: 26 });
|
|
95
|
+
|
|
96
|
+
// DELETE
|
|
97
|
+
await db.table("users").where("id", 1).delete();
|
|
98
|
+
|
|
99
|
+
// COUNT
|
|
100
|
+
const total = await db.table("users").where("age", ">", 18).count();
|
|
101
|
+
// IMPORTANT: for paginated lists, reuse the SAME builder for count() and select() -- see pagination.md
|
|
102
|
+
|
|
103
|
+
// FIND single row
|
|
104
|
+
const user = await db.table("users").where("id", 1).find();
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Dry Run with notExec()
|
|
108
|
+
|
|
109
|
+
Call `notExec()` before any CRUD method to get a `Builder` object with `.sql` and `.values` instead of executing:
|
|
110
|
+
|
|
111
|
+
```javascript
|
|
112
|
+
const builder = await db.table("users")
|
|
113
|
+
.where("age", ">", 18)
|
|
114
|
+
.notExec()
|
|
115
|
+
.select("id", "name");
|
|
116
|
+
|
|
117
|
+
console.log(builder.sql); // "SELECT `id`, `name` FROM `users` WHERE `age` > ?"
|
|
118
|
+
console.log(builder.values); // [18]
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## Reference Files
|
|
122
|
+
|
|
123
|
+
| Scenario | File |
|
|
124
|
+
|----------|------|
|
|
125
|
+
| Building queries (table, join, orderBy, limit, groupBy, attr) | [query-building.md](query-building.md) |
|
|
126
|
+
| Where conditions (where, whereIn, whereLike, whereBetween...) | [where-conditions.md](where-conditions.md) |
|
|
127
|
+
| CRUD operations (select, find, count, insert, update, delete, incrBy, upsertRow) | [crud-operations.md](crud-operations.md) |
|
|
128
|
+
| Pagination (reuse the same builder for count() and select(), avoid duplicated where clauses) | [pagination.md](pagination.md) |
|
|
129
|
+
| Transactions (beginTransaction, commit, rollback, FOR UPDATE) | [transactions.md](transactions.md) |
|
|
130
|
+
|
|
131
|
+
## Hooks
|
|
132
|
+
|
|
133
|
+
Register pre/post hooks for query operations:
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
const { Hook } = require("@axiosleo/orm-mysql");
|
|
137
|
+
|
|
138
|
+
Hook.pre(async (options) => {
|
|
139
|
+
console.log("Before:", options.operator, options.tables);
|
|
140
|
+
}, { table: "users", opt: "insert" });
|
|
141
|
+
|
|
142
|
+
Hook.post(async (options, result) => {
|
|
143
|
+
console.log("After:", result);
|
|
144
|
+
}, { table: "users", opt: "insert" });
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
## Schema Helpers
|
|
148
|
+
|
|
149
|
+
```javascript
|
|
150
|
+
const exists = await db.existTable("users");
|
|
151
|
+
const dbExists = await db.existDatabase("my_db");
|
|
152
|
+
const fields = await db.getTableFields("my_db", "users", "COLUMN_NAME", "DATA_TYPE");
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Raw SQL
|
|
156
|
+
|
|
157
|
+
```javascript
|
|
158
|
+
const result = await db.query({ sql: "SELECT * FROM users WHERE id = ?", values: [1] });
|
|
159
|
+
```
|
|
@@ -0,0 +1,225 @@
|
|
|
1
|
+
# CRUD Operations
|
|
2
|
+
|
|
3
|
+
The `QueryOperator` class provides all execution methods. You get an instance via `db.table("name")`.
|
|
4
|
+
|
|
5
|
+
All CRUD methods return a `Promise`. Write operations resolve to `MySQLQueryResult` (OkPacket | ResultSetHeader). If `notExec()` was called, they resolve to a `Builder` object instead.
|
|
6
|
+
|
|
7
|
+
## Read Operations
|
|
8
|
+
|
|
9
|
+
### select(...attrs)
|
|
10
|
+
|
|
11
|
+
Returns an array of rows. Optionally pass column names to override previously set `attr()`.
|
|
12
|
+
|
|
13
|
+
```javascript
|
|
14
|
+
const rows = await db.table("users").select();
|
|
15
|
+
const rows = await db.table("users").select("id", "name", "email");
|
|
16
|
+
|
|
17
|
+
// With TypeScript generics
|
|
18
|
+
interface User { id: number; name: string; email: string; }
|
|
19
|
+
const users = await db.table("users").select<User>("id", "name", "email");
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### find()
|
|
23
|
+
|
|
24
|
+
Returns a single row (the first match). Automatically applies `LIMIT 1`.
|
|
25
|
+
|
|
26
|
+
```javascript
|
|
27
|
+
const user = await db.table("users").where("id", 1).find();
|
|
28
|
+
|
|
29
|
+
// With TypeScript generics
|
|
30
|
+
const user = await db.table("users").where("id", 1).find<User>();
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### count()
|
|
34
|
+
|
|
35
|
+
Returns the total number of matching rows as a number.
|
|
36
|
+
|
|
37
|
+
```javascript
|
|
38
|
+
const total = await db.table("users").where("status", "active").count();
|
|
39
|
+
// total is a number
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
For paginated list endpoints, do NOT create a separate count builder with duplicated `where` conditions. Reuse the same builder for both `count()` and `select()` -- `count()` ignores `attrs`/`limit`/`offset`/`orderBy`, and `select()` resets the operator. See [pagination.md](pagination.md) for the full pattern.
|
|
43
|
+
|
|
44
|
+
### explain(operator)
|
|
45
|
+
|
|
46
|
+
Returns the MySQL EXPLAIN result for the query.
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
const plan = await db.table("users")
|
|
50
|
+
.where("status", "active")
|
|
51
|
+
.explain("select");
|
|
52
|
+
// plan is ExplainResult[]
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Write Operations
|
|
56
|
+
|
|
57
|
+
### insert(row)
|
|
58
|
+
|
|
59
|
+
Insert a single row.
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
const result = await db.table("users").insert({
|
|
63
|
+
name: "Joe",
|
|
64
|
+
age: 25,
|
|
65
|
+
email: "joe@example.com",
|
|
66
|
+
});
|
|
67
|
+
// result.insertId -- the auto-increment ID
|
|
68
|
+
// result.affectedRows -- number of rows inserted
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### Insert with ON DUPLICATE KEY UPDATE
|
|
72
|
+
|
|
73
|
+
Use `keys()` to specify unique columns. If a duplicate is found, the operation becomes an update.
|
|
74
|
+
|
|
75
|
+
```javascript
|
|
76
|
+
const result = await db.table("users").keys("email").insert({
|
|
77
|
+
email: "joe@example.com",
|
|
78
|
+
name: "Joe Updated",
|
|
79
|
+
age: 26,
|
|
80
|
+
});
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### insertAll(rows)
|
|
84
|
+
|
|
85
|
+
Insert multiple rows. Returns an array of results.
|
|
86
|
+
|
|
87
|
+
```javascript
|
|
88
|
+
const results = await db.table("users").insertAll([
|
|
89
|
+
{ name: "Alice", age: 30 },
|
|
90
|
+
{ name: "Bob", age: 28 },
|
|
91
|
+
{ name: "Charlie", age: 35 },
|
|
92
|
+
]);
|
|
93
|
+
// results is MySQLQueryResult[]
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### update(row)
|
|
97
|
+
|
|
98
|
+
Update rows matching the current where conditions.
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
const result = await db.table("users")
|
|
102
|
+
.where("id", 1)
|
|
103
|
+
.update({ name: "Joe Updated", age: 26 });
|
|
104
|
+
// result.affectedRows -- number of rows updated
|
|
105
|
+
// result.changedRows -- number of rows actually changed
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### delete(id?, index_field_name?)
|
|
109
|
+
|
|
110
|
+
Delete rows. Can be called with conditions or directly with an ID.
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
// Delete by conditions
|
|
114
|
+
const result = await db.table("users").where("status", "banned").delete();
|
|
115
|
+
|
|
116
|
+
// Delete by primary key (defaults to "id" field)
|
|
117
|
+
const result = await db.table("users").delete(1);
|
|
118
|
+
|
|
119
|
+
// Delete by a custom index field
|
|
120
|
+
const result = await db.table("users").delete(1, "user_id");
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### incrBy(attr, increment?)
|
|
124
|
+
|
|
125
|
+
Increment a column value atomically. Default increment is 1.
|
|
126
|
+
|
|
127
|
+
```javascript
|
|
128
|
+
// Increment by 1 (default)
|
|
129
|
+
await db.table("users").where("id", 1).incrBy("login_count");
|
|
130
|
+
|
|
131
|
+
// Increment by a specific number
|
|
132
|
+
await db.table("products").where("id", 1).incrBy("views", 5);
|
|
133
|
+
|
|
134
|
+
// Increment by string value
|
|
135
|
+
await db.table("users").where("id", 1).incrBy("score", "10");
|
|
136
|
+
|
|
137
|
+
// Conditional increment with callback
|
|
138
|
+
await db.table("users").where("id", 1).incrBy("error_count", (current) => {
|
|
139
|
+
return shouldIncrement ? 1 : 0;
|
|
140
|
+
});
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
### upsertRow(row, condition)
|
|
144
|
+
|
|
145
|
+
Insert a row or update it if a matching row exists based on the condition.
|
|
146
|
+
|
|
147
|
+
```javascript
|
|
148
|
+
// Using QueryCondition
|
|
149
|
+
const condition = new QueryCondition();
|
|
150
|
+
condition.where("email", "joe@example.com");
|
|
151
|
+
|
|
152
|
+
await db.table("users").upsertRow(
|
|
153
|
+
{ email: "joe@example.com", name: "Joe", age: 25 },
|
|
154
|
+
condition
|
|
155
|
+
);
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Dry Run with notExec()
|
|
159
|
+
|
|
160
|
+
Call `notExec()` before any CRUD method to get the generated SQL without executing it. Returns a `Builder` with `.sql` and `.values`.
|
|
161
|
+
|
|
162
|
+
```javascript
|
|
163
|
+
const builder = await db.table("users")
|
|
164
|
+
.where("status", "active")
|
|
165
|
+
.orderBy("name", "asc")
|
|
166
|
+
.limit(10)
|
|
167
|
+
.notExec()
|
|
168
|
+
.select("id", "name");
|
|
169
|
+
|
|
170
|
+
console.log(builder.sql); // The generated SQL string
|
|
171
|
+
console.log(builder.values); // The parameter values array
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
This works with all CRUD methods:
|
|
175
|
+
|
|
176
|
+
```javascript
|
|
177
|
+
const insertBuilder = await db.table("users")
|
|
178
|
+
.notExec()
|
|
179
|
+
.insert({ name: "Joe", age: 25 });
|
|
180
|
+
|
|
181
|
+
const updateBuilder = await db.table("users")
|
|
182
|
+
.where("id", 1)
|
|
183
|
+
.notExec()
|
|
184
|
+
.update({ age: 26 });
|
|
185
|
+
|
|
186
|
+
const deleteBuilder = await db.table("users")
|
|
187
|
+
.where("id", 1)
|
|
188
|
+
.notExec()
|
|
189
|
+
.delete();
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Complete Example
|
|
193
|
+
|
|
194
|
+
```javascript
|
|
195
|
+
const db = new QueryHandler(pool);
|
|
196
|
+
|
|
197
|
+
// Create user
|
|
198
|
+
const insertResult = await db.table("users").insert({
|
|
199
|
+
name: "Joe",
|
|
200
|
+
email: "joe@example.com",
|
|
201
|
+
age: 25,
|
|
202
|
+
});
|
|
203
|
+
const userId = insertResult.insertId;
|
|
204
|
+
|
|
205
|
+
// Read back
|
|
206
|
+
const user = await db.table("users").where("id", userId).find();
|
|
207
|
+
|
|
208
|
+
// Update
|
|
209
|
+
await db.table("users").where("id", userId).update({ age: 26 });
|
|
210
|
+
|
|
211
|
+
// Increment login count
|
|
212
|
+
await db.table("users").where("id", userId).incrBy("login_count");
|
|
213
|
+
|
|
214
|
+
// Count active users
|
|
215
|
+
const activeCount = await db.table("users").where("status", "active").count();
|
|
216
|
+
|
|
217
|
+
// Bulk insert
|
|
218
|
+
await db.table("logs").insertAll([
|
|
219
|
+
{ user_id: userId, action: "login" },
|
|
220
|
+
{ user_id: userId, action: "view_profile" },
|
|
221
|
+
]);
|
|
222
|
+
|
|
223
|
+
// Delete
|
|
224
|
+
await db.table("sessions").where("expired_at", "<", new Date()).delete();
|
|
225
|
+
```
|