mirror of
https://github.com/dbgate/dbgate
synced 2024-11-07 20:26:23 +00:00
mongo filtering via sql tree
This commit is contained in:
parent
9fedfcbb0e
commit
303bd659ad
@ -67,6 +67,57 @@ const negateCondition = condition => {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const numberTestCondition = () => value => {
|
||||||
|
return {
|
||||||
|
conditionType: 'or',
|
||||||
|
conditions: [
|
||||||
|
{
|
||||||
|
conditionType: 'like',
|
||||||
|
left: {
|
||||||
|
exprType: 'placeholder',
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
exprType: 'value',
|
||||||
|
value: `.*${value}.*`,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
conditionType: 'binary',
|
||||||
|
operator: '=',
|
||||||
|
left: {
|
||||||
|
exprType: 'placeholder',
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
exprType: 'value',
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const idRegex = /[('"]([0-9a-f]{24})['")]/;
|
||||||
|
|
||||||
|
const objectIdTestCondition = () => value => ({
|
||||||
|
conditionType: 'binary',
|
||||||
|
operator: '=',
|
||||||
|
left: {
|
||||||
|
exprType: 'placeholder',
|
||||||
|
},
|
||||||
|
right: {
|
||||||
|
exprType: 'value',
|
||||||
|
value: { $oid: value.match(idRegex)[1] },
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const specificPredicateCondition = predicate => () => ({
|
||||||
|
conditionType: 'specificPredicate',
|
||||||
|
predicate,
|
||||||
|
expr: {
|
||||||
|
exprType: 'placeholder',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const sqlTemplate = templateSql => {
|
const sqlTemplate = templateSql => {
|
||||||
return {
|
return {
|
||||||
conditionType: 'rawTemplate',
|
conditionType: 'rawTemplate',
|
||||||
@ -104,6 +155,8 @@ const createParser = (filterBehaviour: FilterBehaviour) => {
|
|||||||
.map(Number)
|
.map(Number)
|
||||||
.desc('number'),
|
.desc('number'),
|
||||||
|
|
||||||
|
objectid: () => token(P.regexp(/ObjectId\(['"]?[0-9a-f]{24}['"]?\)/)).desc('ObjectId'),
|
||||||
|
|
||||||
hexstring: () =>
|
hexstring: () =>
|
||||||
token(P.regexp(/0x(([0-9a-fA-F][0-9a-fA-F])+)/, 1))
|
token(P.regexp(/0x(([0-9a-fA-F][0-9a-fA-F])+)/, 1))
|
||||||
.map(x => ({
|
.map(x => ({
|
||||||
@ -123,13 +176,22 @@ const createParser = (filterBehaviour: FilterBehaviour) => {
|
|||||||
valueTestEq: r => r.value.map(binaryCondition('=')),
|
valueTestEq: r => r.value.map(binaryCondition('=')),
|
||||||
hexTestEq: r => r.hexstring.map(binaryCondition('=')),
|
hexTestEq: r => r.hexstring.map(binaryCondition('=')),
|
||||||
valueTestStr: r => r.value.map(likeCondition('like', '%#VALUE#%')),
|
valueTestStr: r => r.value.map(likeCondition('like', '%#VALUE#%')),
|
||||||
|
valueTestNum: r => r.number.map(numberTestCondition()),
|
||||||
|
valueTestObjectId: r => r.objectid.map(objectIdTestCondition()),
|
||||||
|
|
||||||
|
notExists: r => r.not.then(r.exists).map(specificPredicateCondition('notExists')),
|
||||||
|
notEmptyArray: r => r.not.then(r.empty).then(r.array).map(specificPredicateCondition('notEmptyArray')),
|
||||||
|
emptyArray: r => r.empty.then(r.array).map(specificPredicateCondition('emptyArray')),
|
||||||
|
exists: () => word('EXISTS').map(specificPredicateCondition('exists')),
|
||||||
|
|
||||||
comma: () => word(','),
|
comma: () => word(','),
|
||||||
not: () => word('NOT'),
|
not: () => word('NOT'),
|
||||||
|
empty: () => word('EMPTY'),
|
||||||
|
array: () => word('ARRAY'),
|
||||||
notNull: r => r.not.then(r.null).map(unaryCondition('isNotNull')),
|
notNull: r => r.not.then(r.null).map(unaryCondition('isNotNull')),
|
||||||
null: () => word('NULL').map(unaryCondition('isNull')),
|
null: () => word('NULL').map(unaryCondition('isNull')),
|
||||||
empty: () => word('EMPTY').map(unaryCondition('isEmpty')),
|
isEmpty: r => r.empty.map(unaryCondition('isEmpty')),
|
||||||
notEmpty: r => r.not.then(r.empty).map(unaryCondition('isNotEmpty')),
|
isNotEmpty: r => r.not.then(r.empty).map(unaryCondition('isNotEmpty')),
|
||||||
true: () => P.regexp(/true/i).map(binaryFixedValueCondition('1')),
|
true: () => P.regexp(/true/i).map(binaryFixedValueCondition('1')),
|
||||||
false: () => P.regexp(/false/i).map(binaryFixedValueCondition('0')),
|
false: () => P.regexp(/false/i).map(binaryFixedValueCondition('0')),
|
||||||
trueNum: () => word('1').map(binaryFixedValueCondition('1')),
|
trueNum: () => word('1').map(binaryFixedValueCondition('1')),
|
||||||
@ -155,6 +217,7 @@ const createParser = (filterBehaviour: FilterBehaviour) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const allowedValues = []; // 'string1', 'string2', 'number', 'noQuotedString'];
|
const allowedValues = []; // 'string1', 'string2', 'number', 'noQuotedString'];
|
||||||
|
|
||||||
if (filterBehaviour.allowStringToken) {
|
if (filterBehaviour.allowStringToken) {
|
||||||
allowedValues.push('string1', 'string2', 'noQuotedString');
|
allowedValues.push('string1', 'string2', 'noQuotedString');
|
||||||
}
|
}
|
||||||
@ -164,6 +227,14 @@ const createParser = (filterBehaviour: FilterBehaviour) => {
|
|||||||
|
|
||||||
const allowedElements = [];
|
const allowedElements = [];
|
||||||
|
|
||||||
|
if (filterBehaviour.supportExistsTesting) {
|
||||||
|
allowedElements.push('exists', 'notExists');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterBehaviour.supportArrayTesting) {
|
||||||
|
allowedElements.push('emptyArray', 'notEmptyArray');
|
||||||
|
}
|
||||||
|
|
||||||
if (filterBehaviour.supportNullTesting) {
|
if (filterBehaviour.supportNullTesting) {
|
||||||
allowedElements.push('null', 'notNull');
|
allowedElements.push('null', 'notNull');
|
||||||
}
|
}
|
||||||
@ -181,7 +252,7 @@ const createParser = (filterBehaviour: FilterBehaviour) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (filterBehaviour.supportEmpty) {
|
if (filterBehaviour.supportEmpty) {
|
||||||
allowedElements.push('empty', 'notEmpty');
|
allowedElements.push('isEmpty', 'isNotEmpty');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (filterBehaviour.allowHexString) {
|
if (filterBehaviour.allowHexString) {
|
||||||
@ -199,6 +270,14 @@ const createParser = (filterBehaviour: FilterBehaviour) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (filterBehaviour.allowNumberDualTesting) {
|
||||||
|
allowedElements.push('valueTestNum');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (filterBehaviour.allowObjectIdTesting) {
|
||||||
|
allowedElements.push('valueTestObjectId');
|
||||||
|
}
|
||||||
|
|
||||||
// must be last
|
// must be last
|
||||||
if (filterBehaviour.allowStringToken) {
|
if (filterBehaviour.allowStringToken) {
|
||||||
allowedElements.push('valueTestStr');
|
allowedElements.push('valueTestStr');
|
||||||
|
@ -79,6 +79,11 @@ export interface TestCondition extends UnaryCondition {
|
|||||||
conditionType: 'isNull' | 'isNotNull' | 'isEmpty' | 'isNotEmpty';
|
conditionType: 'isNull' | 'isNotNull' | 'isEmpty' | 'isNotEmpty';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SpecificPredicateCondition extends UnaryCondition {
|
||||||
|
conditionType: 'specificPredicate';
|
||||||
|
predicate: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface CompoudCondition {
|
export interface CompoudCondition {
|
||||||
conditionType: 'and' | 'or';
|
conditionType: 'and' | 'or';
|
||||||
conditions: Condition[];
|
conditions: Condition[];
|
||||||
@ -135,7 +140,8 @@ export type Condition =
|
|||||||
| InCondition
|
| InCondition
|
||||||
| NotInCondition
|
| NotInCondition
|
||||||
| RawTemplateCondition
|
| RawTemplateCondition
|
||||||
| AnyColumnPassEvalOnlyCondition;
|
| AnyColumnPassEvalOnlyCondition
|
||||||
|
| SpecificPredicateCondition;
|
||||||
|
|
||||||
export interface Source {
|
export interface Source {
|
||||||
name?: NamedObjectInfo;
|
name?: NamedObjectInfo;
|
||||||
|
@ -48,6 +48,8 @@ export const mongoFilterBehaviour: FilterBehaviour = {
|
|||||||
supportExistsTesting: true,
|
supportExistsTesting: true,
|
||||||
|
|
||||||
allowStringToken: true,
|
allowStringToken: true,
|
||||||
|
allowNumberDualTesting: true,
|
||||||
|
allowObjectIdTesting: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const evalFilterBehaviour: FilterBehaviour = {
|
export const evalFilterBehaviour: FilterBehaviour = {
|
||||||
|
2
packages/types/filter-type.d.ts
vendored
2
packages/types/filter-type.d.ts
vendored
@ -18,4 +18,6 @@ export interface FilterBehaviour {
|
|||||||
allowStringToken?: boolean;
|
allowStringToken?: boolean;
|
||||||
allowNumberToken?: boolean;
|
allowNumberToken?: boolean;
|
||||||
allowHexString?: boolean;
|
allowHexString?: boolean;
|
||||||
|
allowNumberDualTesting?: boolean;
|
||||||
|
allowObjectIdTesting?: boolean;
|
||||||
}
|
}
|
||||||
|
@ -270,10 +270,11 @@ const driver = {
|
|||||||
return res.databases;
|
return res.databases;
|
||||||
},
|
},
|
||||||
async readCollection(pool, options) {
|
async readCollection(pool, options) {
|
||||||
const mongoCondition = convertToMongoCondition(options.condition);
|
|
||||||
console.log('******************* mongoCondition *****************')
|
|
||||||
console.log(JSON.stringify(mongoCondition, undefined, 2));
|
|
||||||
try {
|
try {
|
||||||
|
const mongoCondition = convertToMongoCondition(options.condition);
|
||||||
|
// console.log('******************* mongoCondition *****************');
|
||||||
|
// console.log(JSON.stringify(mongoCondition, undefined, 2));
|
||||||
|
|
||||||
const collection = pool.__getDatabase().collection(options.pureName);
|
const collection = pool.__getDatabase().collection(options.pureName);
|
||||||
if (options.countDocuments) {
|
if (options.countDocuments) {
|
||||||
const count = await collection.countDocuments(convertObjectId(mongoCondition) || {});
|
const count = await collection.countDocuments(convertObjectId(mongoCondition) || {});
|
||||||
|
@ -89,6 +89,38 @@ function convertToMongoCondition(filter) {
|
|||||||
$options: 'i',
|
$options: 'i',
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case 'specificPredicate':
|
||||||
|
switch (filter.predicate) {
|
||||||
|
case 'exists':
|
||||||
|
return {
|
||||||
|
[convertLeftOperandToMongoColumn(filter.expr)]: {
|
||||||
|
$exists: true,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case 'notExists':
|
||||||
|
return {
|
||||||
|
[convertLeftOperandToMongoColumn(filter.expr)]: {
|
||||||
|
$exists: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case 'emptyArray':
|
||||||
|
return {
|
||||||
|
[convertLeftOperandToMongoColumn(filter.expr)]: {
|
||||||
|
$exists: true,
|
||||||
|
$eq: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
case 'notEmptyArray':
|
||||||
|
return {
|
||||||
|
[convertLeftOperandToMongoColumn(filter.expr)]: {
|
||||||
|
$exists: true,
|
||||||
|
$type: 'array',
|
||||||
|
$ne: [],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
throw new Error(`Unknown condition type ${filter.conditionType}`);
|
throw new Error(`Unknown condition type ${filter.conditionType}`);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user