diff --git a/src/components/advancedSearch/TagMode.vue b/src/components/advancedSearch/TagMode.vue index 1e4313fb..2b879b8c 100644 --- a/src/components/advancedSearch/TagMode.vue +++ b/src/components/advancedSearch/TagMode.vue @@ -40,7 +40,13 @@
- +
{{meta.value.label}}
@@ -107,7 +113,13 @@ import Meta, { connection, condition, columnType } from './meta/meta' import _ from 'lodash' import { handleErrorTip } from '@/components/advancedSearch/meta/error' import Parser, { handleMetaListToStr, stringInQuot } from '@/components/advancedSearch/meta/parser' -import { comparedEntityKey, handleEntityTypeByStr, overwriteUrl, urlParamsHandler } from '@/utils/tools' +import { + comparedEntityKey, + getEntityTypeByValue, + handleEntityTypeByStr, + overwriteUrl, + urlParamsHandler +} from '@/utils/tools' export default { name: 'TagMode', props: { @@ -233,6 +245,16 @@ export default { // 根据所选label在columnList中匹配label来确定操作符,一般为=,IN,tags操作符为has const obj = this.columnList.find(t => t.label === meta.column.label) this.operatorList = obj ? obj.doc.constraints.operator_functions.split(',') : ['=', 'IN'] + + // 用户模糊搜索,则自动匹配实体类型和结构 + if (meta.column.type === columnType.fullText) { + meta.operator.value = '=' + meta.operator.show = true + meta.value.value = `'${meta.column.label}'` + meta.value.label = `'${meta.column.label}'` + meta.column.label = getEntityTypeByValue(meta.column.label) + meta.column.type = 'string' + } }, 200) }, connectionClick (meta) { @@ -267,7 +289,7 @@ export default { operatorClick (meta) { meta.operator.isEditing = true }, - valueBlur (meta) { + valueFocus (meta) { if (meta.column.type === columnType.array) { let valueArr = [] if (!_.isArray(meta.value.value)) { @@ -314,6 +336,61 @@ export default { } meta.value.label = meta.value.value // label是显示,value是实际值 } + }, + valueBlur (meta) { + if (meta.column.type === columnType.array) { + let valueArr = [] + if (!_.isArray(meta.value.value)) { + let value = meta.value.value.trim().replace(/"/g, "'") + if (value.indexOf('(') === 0 && value.indexOf(')') === value.length - 1) { + value = value.substring(1, value.length) + value = value.substring(0, value.length - 1) + } + valueArr = value.split(',') + } else { + valueArr = meta.value.value + } + meta.value.value = valueArr.map(v => { + let str = v.trim().replace(/"/g, "'") + if (str[0] === "'" && str[str.length - 1] === "'") { + str = str.substring(1, str.length) + str = str.substring(0, str.length - 1) + } + // 如果此时参数为xi'an,处理为xi''an,后一个条件避免若参数为xi''an,则不处理 + if (str.indexOf("'") > -1 && str.indexOf("''") === -1) { + str = str.replace(/'/g, "''") + } + return str + }) + let label = '(' + meta.value.value.forEach(v => { + label += `'${v}',` + }) + label = label.substring(0, label.length - 1) + label += ')' + meta.value.label = label + } else { + if (!_.isNumber(meta.value.value) && meta.value.value !== '') { + // 判断是否被单引号包裹 + const regex = /^'[^']*'$/ + const isWrapped = regex.test(meta.value.value) + + let str = meta.value.value.trim().replace(/"/g, "'") + if (str[0] === "'" && str[str.length - 1] === "'") { + str = str.substring(1, str.length) + str = str.substring(0, str.length - 1) + } + // 如果此时参数为xi'an,处理为xi''an,后一个条件避免若参数为xi''an,则不处理 + if (str.indexOf("'") > -1 && str.indexOf("''") === -1) { + str = str.replace(/'/g, "''") + } + // meta.value.value = str + if (!isWrapped) { + meta.value.value = `'${str}'` + } + } + meta.value.label = meta.value.value // label是显示,value是实际值 + } meta.value.isEditing = !meta.isCompleteCondition() }, valueClick (meta) { @@ -392,7 +469,7 @@ export default { meta.column.label = column ? column.label : param.column meta.operator.value = param.operator meta.operator.show = true - meta.value.value = this.handleValue(param.value, column, column.operator, 'noQuotes') + meta.value.value = this.handleValue(param.value, column, column.operator) meta.value.show = true meta.value.label = meta.value.value this.addCondition(meta) diff --git a/src/components/advancedSearch/TextMode.vue b/src/components/advancedSearch/TextMode.vue index aad3b79f..5e801947 100644 --- a/src/components/advancedSearch/TextMode.vue +++ b/src/components/advancedSearch/TextMode.vue @@ -144,14 +144,18 @@ export default { return value } } else { - return column.type === columnType.string ? stringInQuot(value) : value + return (column.type.items ? column.type.items : column.type) === columnType.string ? stringInQuot(value) : value } }, addParams (params) { let current = this.codeMirror.getValue() params.forEach(param => { const column = this.columnList.find(c => c.label === param.column) - current = `${current ? current + ' AND ' : ''}${param.column}${handleOperatorSpace(param.operator)}${this.handleValue(param.value, column, param.operator)}` + if (param.operator === 'has') { + current = `${current ? current + ' AND ' : ''}${param.operator}(${param.column},${this.handleValue(param.value, column, param.operator)})` + } else { + current = `${current ? current + ' AND ' : ''}${param.column}${handleOperatorSpace(param.operator)}${this.handleValue(param.value, column, param.operator)}` + } }) toRaw(this.codeMirror).setValue(current.trim()) }, diff --git a/src/components/advancedSearch/meta/parser.js b/src/components/advancedSearch/meta/parser.js index 2130d552..3a4b9658 100644 --- a/src/components/advancedSearch/meta/parser.js +++ b/src/components/advancedSearch/meta/parser.js @@ -1,6 +1,6 @@ -import Meta, { connection, condition, columnType } from './meta' +import Meta, { columnType, condition, connection } from './meta' import Token, { types } from './token' -import ParserError, { errorTypes, errorDesc } from '@/components/advancedSearch/meta/error' +import ParserError, { errorDesc, errorTypes } from '@/components/advancedSearch/meta/error' import _ from 'lodash' import { columnList } from '@/utils/static-data' import { getEntityTypeByValue } from '@/utils/tools' @@ -97,10 +97,22 @@ export default class Parser { if (meta.operator.value.toLowerCase().indexOf('like') > -1 || meta.operator.value.toLowerCase().indexOf('in') > -1) { str += `${meta.column.label} ${meta.operator.value} '${meta.value.value}' ` } else if (meta.operator.value.toLowerCase().indexOf('has') > -1) { - // 操作符为has时,has函数需要提前,格式为has(label,value) - str += `${meta.operator.value}(${meta.column.label},'${meta.value.value}') ` + const isWrapped = isSingleQuoteWrapping(meta.value.value) + // 如果值被单引号包裹,则不需要再添加单引号包裹,true为单引号包裹 + if (isWrapped) { + // 操作符为has时,has函数需要提前,格式为has(label,value) + str += `${meta.operator.value}(${meta.column.label},${meta.value.value}) ` + } else { + str += `${meta.operator.value}(${meta.column.label},'${meta.value.value}') ` + } } else { - str += `${meta.column.label}${meta.operator.value}'${meta.value.value}' ` + const isWrapped = isSingleQuoteWrapping(meta.value.value) + // 如果值被单引号包裹,则不需要再添加单引号包裹,true为单引号包裹 + if (isWrapped || meta.value.value.indexOf("''") > -1) { + str += `${meta.column.label}${meta.operator.value}${meta.value.value} ` + } else { + str += `${meta.column.label}${meta.operator.value}'${meta.value.value}' ` + } } } else if (meta.column.type === columnType.number) { if (_.isNumber(Number(meta.value.value))) { @@ -147,10 +159,21 @@ export default class Parser { if (meta.operator.value.toLowerCase().indexOf('like') > -1 || meta.operator.value.toLowerCase().indexOf('in') > -1) { str += `${meta.column.label} ${meta.operator.value} '${meta.value.value}' ` } else if (meta.operator.value.toLowerCase().indexOf('has') > -1) { - // 操作符为has时,has函数需要提前,格式为has(label,value) - str += `${meta.operator.value}(${meta.column.label},'${meta.value.value}') ` + const isWrapped = isSingleQuoteWrapping(meta.value.value) + if (isWrapped) { + // 操作符为has时,has函数需要提前,格式为has(label,value) + str += `${meta.operator.value}(${meta.column.label},${meta.value.value}) ` + } else { + str += `${meta.operator.value}(${meta.column.label},'${meta.value.value}') ` + } } else { - str += `${meta.column.label}${meta.operator.value}'${meta.value.value}' ` + const isWrapped = isSingleQuoteWrapping(meta.value.value) + if (isWrapped || meta.value.value.indexOf("''") > -1) { + // 如xi''an这种情况,不需要再添加单引号 + str += `${meta.column.label}${meta.operator.value}${meta.value.value} ` + } else { + str += `${meta.column.label}${meta.operator.value}'${meta.value.value}' ` + } } } else if (meta.column.type === columnType.number) { if (_.isNumber(Number(meta.value.value))) { @@ -577,7 +600,8 @@ export default class Parser { // 前面是连接符或操作符或空后面是连接符或空,或在单引号内,是value if (isInApostrophe) { if (meta.column.label) { - meta.value.value = token.value + // meta.value.value = token.value + meta.value.value = isSingleQuoteWrapping(token.value) ? token.value : `'${token.value}'` meta.column.type = columnType.string } else { meta.column.type = columnType.fullText @@ -747,6 +771,12 @@ export default class Parser { } else { meta.value.label = '()' } + } else if (meta.column.type === columnType.string) { + if (meta.value.value.indexOf("''") > -1) { + meta.value.label = meta.value.value + } else { + meta.value.label = isSingleQuoteWrapping(meta.value.value) ? meta.value.value : `'${meta.value.value}'` + } } else { meta.value.label = meta.value.value } @@ -819,8 +849,21 @@ export function handleMetaListToStr (metaList) { if (result) { if (metaList[0].operator.value.toLowerCase() === 'has') { return `${metaList[0].operator.value}(${metaList[0].column.label},'${metaList[0].value.label}')` - } else { + } else if (metaList[0].value.label.indexOf('(') > -1) { + // 避免如IN后面带()的,不添加单引号 return `${metaList[0].column.label} ${metaList[0].operator.value} ${metaList[0].value.label}` + } else if (metaList[0].value.label.indexOf("''") > -1) { + // 如xi''an这种情况,直接返回 + return `${metaList[0].column.label} ${metaList[0].operator.value} ${metaList[0].value.label}` + } else { + const isWrapped = isSingleQuoteWrapping(metaList[0].value.label) + // 如果值被单引号包裹,则不需要再添加单引号包裹,true为单引号包裹 + if (isWrapped) { + // 操作符为has时,has函数需要提前,格式为has(label,value) + return `${metaList[0].column.label} ${metaList[0].operator.value} ${metaList[0].value.label}` + } else { + return `${metaList[0].column.label} ${metaList[0].operator.value} '${metaList[0].value.label}'` + } } } else { const regex = /^["']|["']$/ @@ -838,7 +881,7 @@ export function handleMetaListToStr (metaList) { metaList.forEach(item => { if (item.value !== 'AND') { if (item.column.label.toLowerCase() === 'tag') { - hasStr += `${item.operator.value}(${item.column.label},'${item.value.value}') AND ` + hasStr += `${item.operator.value}(${item.column.label},${item.value.value}) AND ` } else { newMetaList.push(item) } @@ -858,10 +901,16 @@ export function handleMetaListToStr (metaList) { lastObj[i] = `${i} IN (${str})` } else if (i.toLowerCase() === 'tag') { // lastObj[i] = `${i} = '${newObj[i]}'` - lastObj[i] = `has(${i},'${newObj[i]}')` + lastObj[i] = `has(${i},${newObj[i]})` } else { - // 单独存在的,直接保留 - lastObj[i] = `${i} = '${newObj[i]}'` + const isWrapped = isSingleQuoteWrapping(newObj[i]) + // 如果值被单引号包裹,则不需要再添加单引号包裹,true为单引号包裹 + if (isWrapped || newObj[i].indexOf("''") > -1) { + // 操作符为has时,has函数需要提前,格式为has(label,value) + lastObj[i] = `${i} = ${newObj[i]}` + } else { + lastObj[i] = `${i} = '${newObj[i]}'` + } } } @@ -894,3 +943,10 @@ const combineLabel = (list) => { return acc }, {}) } +/** + * 判断字符串是否为单引号包裹 + */ +const isSingleQuoteWrapping = (str) => { + const regex = /^'[^']*'$/ + return regex.test(str) +} diff --git a/src/utils/tools.js b/src/utils/tools.js index 363afd91..3a9ae468 100644 --- a/src/utils/tools.js +++ b/src/utils/tools.js @@ -1391,14 +1391,13 @@ export function switchStatus (status) { */ export function comparedEntityKey (str) { let q = JSON.parse(JSON.stringify(str)) - q = q.replace(/ and /g, ' AND ').replace(/ in /g, ' IN ') - if (q && q.indexOf('=') > -1) { // =周围有空格,则去除空格 const regex = /\ = | =|= /g q = q.replace(regex, '=') if (q.indexOf('AND') > -1 && checkStrIncludeAnd(q)) { + q = q.replace(/ and /g, ' AND ').replace(/ in /g, ' IN ') const arr = q.split(' AND ') let newQ = '' let errorQ = '' @@ -1451,6 +1450,11 @@ export function comparedEntityKey (str) { key: q, isKey: true } + } else if (q.indexOf(' IN ') > -1 || q.indexOf(' in ') > -1) { + return { + key: q, + isKey: true + } } else { const key = q.substring(0, q.indexOf('=')) q = q.replace(/ AND /g, ' and ') @@ -1491,6 +1495,7 @@ export const handleEntityTypeByStr = (str) => { columnList.forEach(item => { arr.push(item.label.toLowerCase()) }) + // 因为手动输入时可能会输入and,所以将操作符的AND转换为and,统一处理 let newStr = str.replace(/ AND /g, ' and ') // 将str中的IP、Domain等替换为数组arr中的元素 @@ -1520,7 +1525,8 @@ export const handleEntityTypeByStr = (str) => { newStr = newStr.replace(regex, `it is test keyword${index}`) }) - const noAndList = newStr.split(' and ') + newStr = newStr.replace(/ and /g, ' AND ') + const noAndList = newStr.split(' AND ') noAndList.forEach((item, index) => { // 发现插入的特殊值,获取键值,根据键值替换成原来内容,删除键值 if (item.indexOf('it is test keyword') > -1) { @@ -1534,12 +1540,11 @@ export const handleEntityTypeByStr = (str) => { const newArr = noAndList newArr.forEach((item, index) => { if (!arr.some(ite => item.includes(ite))) { - // item = checkFormatByStr(item) newArr[index] = checkFormatByStr(item) } }) - newStr = newArr.join(' and ') + newStr = newArr.join(' AND ') return newStr } else if (result) { return str diff --git a/src/views/entityExplorer/EntityExplorer.vue b/src/views/entityExplorer/EntityExplorer.vue index fe41fcd0..a8e46c97 100644 --- a/src/views/entityExplorer/EntityExplorer.vue +++ b/src/views/entityExplorer/EntityExplorer.vue @@ -36,7 +36,7 @@
- {{ summaryCount.total }} results,IP + {{ summaryCount.totalCount }} results,IP {{ summaryCount.ipCount }},Domain {{ summaryCount.domainCount }},APP {{ summaryCount.appCount }} @@ -369,11 +369,10 @@ export default { this.reloadUrl({ listMode: this.listMode, - q: param.str, + q: param.str || '', mode: mode, - startTime: getSecond(this.timeFilter.startTime), - endTime: getSecond(this.timeFilter.endTime), - range: this.timeFilter.dateRangeValue + pageNo: this.pageObj.pageNo, + pageSize: this.pageObj.pageSize }) if (!this.showList) { @@ -382,11 +381,11 @@ export default { path: '/entityExplorer', query: { listMode: this.listMode, - q: param.str, + q: param.str || '', mode: mode, - startTime: getSecond(this.timeFilter.startTime), - endTime: getSecond(this.timeFilter.endTime), - range: this.timeFilter.dateRangeValue + range: this.timeFilter.dateRangeValue, + pageNo: this.pageObj.pageNo, + pageSize: this.pageObj.pageSize } }) this.showList = true