Skip to content

Commit c7fccfb

Browse files
committed
JSON dialect on columns and sort
1 parent a597b3e commit c7fccfb

7 files changed

Lines changed: 195 additions & 121 deletions

File tree

packages/inquire/package.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,10 @@
6969
"require": "./cjs/builder/Update.js",
7070
"import": "./esm/builder/Update.js"
7171
},
72+
"./Json": {
73+
"require": "./cjs/dialect/Json.js",
74+
"import": "./esm/dialect/Json.js"
75+
},
7276
"./Mysql": {
7377
"require": "./cjs/dialect/Mysql.js",
7478
"import": "./esm/dialect/Mysql.js"
@@ -96,6 +100,7 @@
96100
"Select": [ "./cjs/builder/Select.d.ts" ],
97101
"Update": [ "./cjs/builder/Update.d.ts" ],
98102
"Mysql": [ "./cjs/dialect/Mysql.d.ts" ],
103+
"Json": [ "./cjs/dialect/Json.d.ts" ],
99104
"Pgsql": [ "./cjs/dialect/Pgsql.d.ts" ],
100105
"Sqlite": [ "./cjs/dialect/Sqlite.d.ts" ]
101106
}
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
export const pattern = '([a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)'
2+
+ '{0,1}%s[a-zA-Z0-9_]+(%s[a-zA-Z0-9_]+)*)';
3+
4+
export default abstract class JsonTrait {
5+
//Recommended quote character
6+
public abstract q: string;
7+
8+
//used for json notation
9+
protected _separator: string = '.';
10+
protected _splitter: string = ':';
11+
//jsonic pattern
12+
protected _pattern = pattern
13+
.replace('%s', this._splitter)
14+
.replace('%s', this._separator);
15+
// - ex. data:info.name
16+
// - ex. profile.data:info
17+
// - ex. profile.data:info.name
18+
protected _replace = new RegExp(this._pattern, 'g');
19+
protected _jsonic = new RegExp(`^${this._pattern}$`, 'g');
20+
21+
/**
22+
* Returns json separator (.)
23+
*/
24+
public get separator() {
25+
return this._separator;
26+
}
27+
28+
/**
29+
* Returns json splitter (:)
30+
*/
31+
public get splitter() {
32+
return this._splitter;
33+
}
34+
35+
/**
36+
* Sets json separator and updates jsonic regex pattern
37+
*/
38+
public set separator(separator: string) {
39+
this._separator = separator;
40+
this._pattern = pattern
41+
.replace('%s', this._splitter)
42+
.replace('%s', this._separator);
43+
}
44+
45+
/**
46+
* Sets json splitter and updates jsonic regex pattern
47+
*/
48+
public set splitter(splitter: string) {
49+
this._splitter = splitter;
50+
this._pattern = pattern
51+
.replace('%s', this._splitter)
52+
.replace('%s', this._separator);
53+
}
54+
55+
protected _isJsonic(selector: string) {
56+
const regexp = new RegExp(`^${this._pattern}$`, 'g');
57+
return regexp.test(selector);
58+
}
59+
60+
/**
61+
* Replaces JSON selectors in the given clause
62+
* with the corresponding json sql syntax.
63+
*/
64+
protected abstract _jsonReplace(clause: string): string;
65+
};

packages/inquire/src/dialect/Mysql.ts

Lines changed: 35 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ import type Delete from '../builder/Delete.js';
55
import type Insert from '../builder/Insert.js';
66
import type Select from '../builder/Select.js';
77
import type Update from '../builder/Update.js';
8+
//dialect
9+
import JsonTrait from './Json.js';
810
//common
911
import type {
1012
Column,
@@ -41,25 +43,12 @@ export const typemap: Record<string, string> = {
4143
time: 'TIME'
4244
};
4345

44-
export class MysqlDialect implements Dialect {
46+
export class MysqlDialect extends JsonTrait implements Dialect {
4547
//The name of the dialect, used for logging and error messages.
4648
public readonly name = 'mysql';
4749
//Recommended quote character
4850
public readonly q = q;
4951

50-
//used for json notation
51-
public separator: string = '.';
52-
public splitter: string = ':';
53-
//jsonic pattern
54-
// - ex. data:info.name
55-
// - ex. profile.data:info
56-
// - ex. profile.data:info.name
57-
public jsonic = new RegExp(
58-
`([a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+){0,1}\\${this.splitter}`
59-
+ `[a-zA-Z0-9_]+(\\${this.separator}[a-zA-Z0-9_]+)*)`,
60-
'g'
61-
);
62-
6352
/**
6453
* Converts alter builder to query and values
6554
*/
@@ -489,11 +478,21 @@ export class MysqlDialect implements Dialect {
489478
? `${this.q}${selector.name}${this.q}`
490479
: '*';
491480
return selector.table && selector.alias
492-
? `${this.q}${selector.table}${this.q}.${name} AS ${this.q}${selector.alias}${this.q}`
481+
? [
482+
`${this.q}${selector.table}${this.q}.${name}`,
483+
`${this.q}${selector.alias}${this.q}`
484+
].join(' AS ')
493485
: selector.table
494486
? `${this.q}${selector.table}${this.q}.${name}`
487+
: selector.alias && this._isJsonic(selector.name)
488+
? [
489+
this._jsonReplace(selector.name),
490+
`${this.q}${selector.alias}${this.q}`
491+
].join(' AS ')
495492
: selector.alias
496493
? `${name} AS ${this.q}${selector.alias}${this.q}`
494+
: this._isJsonic(selector.name)
495+
? this._jsonReplace(selector.name)
497496
: name
498497
});
499498

@@ -531,22 +530,15 @@ export class MysqlDialect implements Dialect {
531530
filters.push(...build.where.map(filter => {
532531
values.push(...filter.values);
533532
//then replace with XJsonDialect.parse().extract
534-
return filter.clause.replace(this.jsonic, match => {
535-
const json = MysqlJsonDialect.parse(
536-
match,
537-
this.splitter,
538-
this.separator
539-
);
540-
return json ? json.extract : match;
541-
});
533+
return this._jsonReplace(filter.clause);
542534
}));
543535
}
544536
build.json.forEach(filter => {
545537
const { query, replace } = filter;
546538
const json = MysqlJsonDialect.parse(
547539
filter.selector,
548-
this.splitter,
549-
this.separator
540+
this._splitter,
541+
this._separator
550542
);
551543
//if invalid JSON selector, skip it
552544
if (!json) return;
@@ -585,15 +577,8 @@ export class MysqlDialect implements Dialect {
585577
if (build.sort.length) {
586578
const sort = build.sort.map(sort => {
587579
//if the sort column is using the selector ":" notation
588-
if (sort.column.name.includes(this.splitter)) {
589-
const json = MysqlJsonDialect.parse(
590-
sort.column.name,
591-
this.splitter,
592-
this.separator
593-
);
594-
//if invalid JSON selector, skip it
595-
if (!json) return '';
596-
return `${json.extract} ${sort.direction.toUpperCase()}`;
580+
if (this._isJsonic(sort.column.name)) {
581+
return `${this._jsonReplace(sort.column.name)} ${sort.direction.toUpperCase()}`;
597582
}
598583
const column = sort.column.table
599584
? `${this.q}${sort.column.table}${this.q}.${this.q}${sort.column.name}${this.q}`
@@ -689,6 +674,22 @@ export class MysqlDialect implements Dialect {
689674
}
690675
return { type, length };
691676
}
677+
678+
/**
679+
* Replaces JSON selectors in the given clause
680+
* with the corresponding json sql syntax.
681+
*/
682+
protected _jsonReplace(clause: string) {
683+
const regexp = new RegExp(this._pattern, 'g');
684+
return clause.replace(regexp, match => {
685+
const json = MysqlJsonDialect.parse(
686+
match,
687+
this._splitter,
688+
this._separator
689+
);
690+
return json ? json.extract : match;
691+
});
692+
}
692693
};
693694

694695
export class MysqlJsonDialect implements JsonDialect {

0 commit comments

Comments
 (0)