sphinxql
Version:
SphinxQL query builder for Node.JS. Supports Sphinx search(2.x and 3.x) and Manticore search
119 lines (98 loc) • 3.03 kB
text/typescript
require ('es7-object-polyfill');
import StatementBuilderBase from '../StatementBuilderBase';
export default class MatchStatement implements StatementBuilderBase {
protected parts: object[];
/**
* List of characters that must be escaped for full-text search in SphinxQL:
* ! " $ ' ( ) - / < @ \ ^ | ~
* More info at: https://docs.manticoresearch.com/latest/html/searching/escaping_in_queries.html
*/
protected escapeChars: object = {
'\\': '\\\\',
'(' : '\(',
')' : '\)',
'!' : '\!',
'@' : '\@',
'~' : '\~',
'&' : '\&',
'/' : '\/',
'^' : '\^',
'$' : '\$',
'=' : '\=',
'<' : '\<'
};
constructor() {
this.parts = [];
}
public getParts(): object[] {
return this.parts;
}
public escapeSpecialChars(value: string): string {
let newEscapedValue: string = value;
for (const [key, value] of Object.entries(this.escapeChars)) {
newEscapedValue = newEscapedValue.replace(key, value);
}
return newEscapedValue;
}
public match(fields: string[] | string, value: string, escapeValue: boolean = true) {
const part = {
logicalLeftRelation: ' ',
fields: fields,
value: value,
escapeValue: escapeValue
};
this.parts = [...this.parts, part];
}
public orMatch(fields: string[] | string | undefined, value: string, escapeValue: boolean = true) {
if (!this.parts.length) {
throw Error('OR statement can\'t be used at the beginning of a MATCH expression');
}
const part = {
logicalLeftRelation: ' | ',
fields: fields,
value: value,
escapeValue: escapeValue
};
this.parts = [...this.parts, part];
}
public build(): string {
let expression: string = '\'';
for (let i = 0, length = this.parts.length; i < length; i++) {
// @ts-ignore
const { logicalLeftRelation, fields, value, escapeValue } = this.parts[i];
if (i === 0) {
expression += `(${this.buildFields(fields)}${this.buildValues(value, escapeValue)})`;
} else {
expression += logicalLeftRelation;
expression += `(${this.buildFields(fields)}${this.buildValues(value, escapeValue)})`;
}
}
expression += '\'';
return expression;
}
/**
* Generates fields for the full-text query condition.
*/
protected buildFields(fields): string {
let expression: string = '';
if (fields !== undefined) {
if (typeof fields === 'string') {
expression += `@${fields} `;
} else { // if is an string[]
if (fields[0] === '!') {
expression += `@!(${fields.slice(1).join(',')}) `;
} else {
expression += `@(${fields.join(',')}) `;
}
}
}
return expression;
}
/**
* Generates the text to search. It escapes the text if escapeValue
* is true.
*/
protected buildValues(value, escapeValue: boolean): string {
return escapeValue ? this.escapeSpecialChars(value) : value;
}
}