feat: 搜索框(全文搜索,缺搜索历史)

This commit is contained in:
chenjinsong
2022-01-25 22:58:23 +08:00
parent a1e3d88b96
commit 1169d8a2cc
5 changed files with 62 additions and 23 deletions

View File

@@ -87,7 +87,7 @@
<script>
import Meta, { connection, condition, columnType } from './meta/meta'
import _ from 'lodash'
import SqlParser, { stringInQuot } from '@/utils/sql-parser'
import SqlParser, { stringInQuot } from '@/components/advancedSearch/meta/sql-parser'
export default {
name: 'TagMode',
props: {
@@ -183,6 +183,7 @@ export default {
},
columnBlur (meta, index) {
setTimeout(() => {
meta.column.name = meta.column.name.replace(/"/g, '')
meta.column.isEditing = false
if (meta.isEmpty()) {
if (this.metaList.length > 1) {
@@ -211,6 +212,7 @@ export default {
meta.operator.isEditing = true
},
valueBlur (meta) {
meta.value.value = meta.value.value.replace(/"/g, "'")
meta.value.label = meta.value.value // label是显示value是实际值目前的需求label和value是相等的
meta.value.isEditing = !meta.isCompleteCondition()
},
@@ -235,7 +237,6 @@ export default {
changeMode () {
const parser = new SqlParser(this.metaList, this.columnList)
const { metaList, formatSql } = parser.formatMetaList()
console.info(formatSql)
this.metaList = metaList
this.$emit('changeMode', 'text', formatSql)
},

View File

@@ -18,7 +18,7 @@ import 'codemirror/addon/hint/show-hint'
import 'codemirror/addon/hint/show-hint.css'
import 'codemirror/addon/display/placeholder'
import 'codemirror/mode/sql/sql'
import SqlParser, { stringInQuot } from '@/utils/sql-parser'
import SqlParser, { stringInQuot } from '@/components/advancedSearch/meta/sql-parser'
import CodeMirror from 'codemirror'
import { toRaw } from 'vue'
import { columnType } from '@/components/advancedSearch/meta/meta'
@@ -43,15 +43,27 @@ export default {
placeholder: 'Enter...',
lineNumbers: false
})
this.codeMirror.setOption('extraKeys', {
Enter: (cm) => {
this.search()
}
})
},
search () {
const originalSql = this.codeMirror.getValue()
const parser = new SqlParser(originalSql, this.columnList)
const errorList = parser.validate()
if (this.$_.isEmpty(errorList)) {
const { metaList, formatSql } = parser.formatSql()
toRaw(this.codeMirror).setValue(formatSql)
this.$emit('search', metaList, formatSql)
let originalSql = this.codeMirror.getValue()
if (originalSql) {
originalSql = originalSql.replace(/"/g, '')
const parser = new SqlParser(originalSql, this.columnList)
const errorList = parser.validate()
if (this.$_.isEmpty(errorList)) {
const { metaList, formatSql } = parser.formatSql()
toRaw(this.codeMirror).setValue(formatSql)
this.$emit('search', metaList, formatSql)
} else {
console.info(errorList)
}
} else {
this.$emit('search')
}
},
changeMode () {

View File

@@ -0,0 +1,276 @@
import { GenericSQL, SqlParserVisitor } from 'dt-sql-parser'
import Meta, { connection, condition, columnType, cloneMeta } from '@/components/advancedSearch/meta/meta'
import _ from 'lodash'
// 补全语句,用于解析
const sqlPrev = 'select a from b where '
export default class SqlParser extends SqlParserVisitor {
constructor (init, columnList) {
super()
this.tempMeta = null
this.originalSql = ''
this.metaList = []
this.columnList = columnList
// 原始数据
if (_.isArray(init)) {
this.metaList = init
} else {
this.originalSql = init
}
// 工具类实例
this.dtSqlParser = new GenericSQL()
}
// 返回的数组是错误信息,为空即校验通过
validate () {
return this.dtSqlParser.validate(sqlPrev + this.originalSql)
}
// 规范化原始语句
formatSql () {
// 先使用originalSql走一遍parse获取metaList再将metaList转为sql
const tree = this.dtSqlParser.parse(sqlPrev + this.originalSql)
this.visit(tree)
if (this.metaList.length === 0 && this.tempMeta.column.name) {
this.tempMeta.column.type = columnType.fullText
this.tempMeta.column.label = this.tempMeta.column.name
this.metaList.push(cloneMeta(this.tempMeta))
}
this.tempMeta = null
return {
metaList: this.metaList,
formatSql: this.parseMetaToSql(this.metaList)
}
}
formatMetaList () {
const tempMetaList = cloneMeta(this.metaList)
this.metaList = []
tempMetaList.forEach(meta => {
if (meta.meta === condition) {
if (meta.column.type === columnType.fullText) {
const m = cloneMeta(meta)
m.column.name = meta.column.name
m.column.label = m.column.name
this.metaList.push(m)
} else if (meta.column.type === columnType.string) {
if (_.isArray(meta.value.value)) {
meta.value.value = meta.value.value.map(v => {
return stringInQuot(v)
})
meta.value.label = `(${meta.value.value.join(',')})`
} else {
meta.value.value = stringInQuot(meta.value.value)
meta.value.label = stringInQuot(meta.value.value)
}
this.metaList.push(meta)
} else {
if (_.isArray(meta.value.value)) {
meta.value.label = `(${meta.value.value.join(',')})`
} else {
meta.value.label = meta.value.value
}
this.metaList.push(meta)
}
} else {
this.metaList.push(meta)
}
})
return {
metaList: this.metaList,
formatSql: this.parseMetaToSql(this.metaList)
}
}
parseMetaToSql (metaList, isFullText) {
let sql = ''
metaList.forEach(meta => {
if (isFullText) {
if (meta.meta === condition) {
if (meta.column.type !== columnType.fullText) {
sql += `${meta.column.name}${meta.operator.value}${meta.value.value} `
} else {
sql += "QUERY('"
this.columnList.forEach(column => {
sql += `${column.name}:${meta.column.name.replace(/'/g, '')} `
})
sql += "') "
}
}
} else {
if (meta.meta === condition) {
sql += (meta.column.name)
if (meta.column.type !== columnType.fullText) {
sql += `${meta.operator.value}${meta.value.value} `
} else {
sql += ' '
}
}
}
if (meta.meta === connection) {
sql += `${meta.value} `
}
})
return sql
}
buildMeta (type, value) {
switch (type) {
case 'expression': {
// 如果tempMeta是空或已是完整的condition新建meta
if (!this.tempMeta || this.tempMeta.isCompleteCondition()) {
this.tempMeta = new Meta(condition)
}
// 如果column.name为空则参数值是column否则是value
if (!this.tempMeta.column.name) {
// 在columnList中的按columnList不在则先设为空串由之后value值来自动判断
const column = this.columnList.find(column => {
return column.name === value
})
this.tempMeta.column.name = value
this.tempMeta.column.label = column ? column.label : value
this.tempMeta.column.type = column ? column.type : ''
} else {
// 若column的type为空则根据value自动判断赋值
if (!this.tempMeta.column.type) {
this.tempMeta.column.type = handleType(value)
}
if (this.tempMeta.column.type === columnType.string) {
if (_.isArray(value)) {
this.tempMeta.value.value = value.map(v => {
return stringInQuot(v)
})
this.tempMeta.value.label = `(${this.tempMeta.value.value.join(',')})`
} else {
this.tempMeta.value.value = stringInQuot(value)
this.tempMeta.value.label = stringInQuot(value)
}
} else {
this.tempMeta.value.value = value
if (_.isArray(value)) {
this.tempMeta.value.label = `(${this.tempMeta.value.value.join(',')})`
} else {
this.tempMeta.value.label = value
}
}
this.tempMeta.value.show = true
this.metaList.push(cloneMeta(this.tempMeta))
}
break
}
case 'operator': {
this.tempMeta.operator.value = value
this.tempMeta.operator.label = value
this.tempMeta.operator.show = true
break
}
// (not)in和(not)like特殊处理
case 'like':
case 'in': {
const { column, operator, handleValue } = handleInOrLike(value)
this.buildMeta('expression', column)
this.buildMeta('operator', operator)
this.buildMeta('expression', handleValue)
break
}
case 'connection': {
// tempMeta的value为空则上个条件是全文搜索
if (!this.tempMeta.column.type && !this.tempMeta.value.value) {
this.tempMeta.column.type = columnType.fullText
this.tempMeta.column.label = this.tempMeta.column.name
this.metaList.push(cloneMeta(this.tempMeta))
this.tempMeta = null
}
const meta = new Meta(connection)
meta.value = value
this.metaList.push(meta)
break
}
}
}
// 字段或值
visitExpressionAtomPredicate (ctx) {
const constant = ctx.getText().toLowerCase()
this.buildMeta('expression', constant)
}
// 操作符
visitComparisonOperator (ctx) {
const comparisonOperator = ctx.getText().toLowerCase()
this.buildMeta('operator', comparisonOperator)
}
// 连接符
visitLogicalOperator (ctx) {
const logicalOperator = ctx.getText().toUpperCase()
this.buildMeta('connection', logicalOperator)
}
// in语句
visitInPredicate (ctx) {
const inPredicate = ctx.getText().toLowerCase()
this.buildMeta('in', inPredicate)
}
// like语句
visitLikePredicate (ctx) {
const likePredicate = ctx.getText().toLowerCase()
this.buildMeta('like', likePredicate)
}
}
function handleType (value) {
if (_.isInteger(value)) {
return columnType.long
} else if (_.isNumber(value)) {
return columnType.number
} else if (_.isString(value)) {
return columnType.string
} else if (_.isArray(value)) {
const arr = value.split(',')
const hasString = arr.some(v => {
return handleType(v) === columnType.string
})
return hasString ? columnType.string : columnType.number
}/* else if (isStringInQuot(value)) {
return columnType.string
} */
return null
}
// 使用单引号包裹
export function stringInQuot (value) {
const match = value.match(/^'.+?'$/)
return match ? value : `'${value}'`
}
function handleInOrLike (value, type) {
let sep
if (type === 'in') {
sep = `${type}(`
} else if (type === 'like') {
sep = `${type}'`
}
const notSep = `not${sep}`
let arr
let operator
if (value.split(notSep).length === 3) {
arr = value.split(notSep)
operator = `NOT ${type.toUpperCase()}`
} else {
arr = value.split(sep)
operator = type.toUpperCase()
}
const columnName = arr[0]
let v
if (type === 'in') {
v = arr[2].substring(0, arr[2].length - 2).split(',')
} else if (type === 'like') {
v = `'${arr[2]}'`
}
return {
column: columnName,
operator,
handleValue: v
}
}