fix: 关于实体搜索的处理都移入parser.js,方便统一整理

This commit is contained in:
刘洪洪
2023-09-08 11:11:56 +08:00
parent b25d1d4619
commit f3cf542bef
5 changed files with 642 additions and 673 deletions

View File

@@ -7,75 +7,6 @@ import { format } from 'echarts'
import router from '@/router'
import indexedDBUtils from '@/indexedDB'
// columnList从'@/utils/static-data'引入的话会导致国际化i18n出错
const columnList1 = [
{
name: 'ip',
type: 'string',
label: 'IP',
doc: {
constraints: {
type: 'ip',
operator_functions: '=,in'
}
}
},
{
name: 'fqdn',
type: 'string',
label: 'Domain',
doc: {
constraints: {
type: 'domain',
operator_functions: '=,in'
}
}
},
{
name: 'app_name',
type: 'string',
label: 'App',
doc: {
constraints: {
operator_functions: '=,in'
}
}
},
{
name: 'region',
type: 'string',
label: 'City',
doc: {
constraints: {
operator_functions: '=,in'
}
}
},
{
name: 'country',
type: 'string',
label: 'Country',
doc: {
constraints: {
operator_functions: '=,in'
}
}
},
{
name: 'asn',
type: 'string',
label: 'ASN',
doc: {
constraints: {
operator_functions: '=,in'
}
}
}
]
let schemaEntityExplore = localStorage.getItem(storageKey.schemaEntityExplore)
schemaEntityExplore = schemaEntityExplore ? JSON.parse(schemaEntityExplore).entityMetadata.searchColumns : columnList1
const columnList = schemaEntityExplore
export const tableSort = {
// 是否需要排序
sortableShow (prop, from) {
@@ -1386,355 +1317,3 @@ export function switchStatus (status) {
return 'Enabled'
}
}
/**
* 将str传过来的值进行columnList规范化
* 步骤将key与columnList的label都进行转小写进行对比
* 如果一致返回columnList的label不一致则返回false并弹窗提示
* @param str
*/
export function comparedEntityKey (str) {
let q = JSON.parse(JSON.stringify(str))
if (q && q.indexOf('=') > -1) {
// =周围有空格,则去除空格
const regex = /\ = | =|= /g
q = q.replace(regex, '=')
if (q.indexOf(' AND ') > -1 || q.indexOf(' and ') > -1) {
if (checkStrIncludeAnd(q)) {
q = q.replace(/ and /g, ' AND ')
}
const arr = q.split(' AND ')
const returnObj = { key: '', isKey: false }
arr.forEach(item => {
let label = ''
let key = ''
if (item.indexOf('=') > -1) {
label = item.substring(0, item.indexOf('='))
key = '='
} else if (item.toLowerCase().indexOf(' like ') > -1) {
label = item.substring(0, item.toLowerCase().indexOf(' like '))
key = 'like'
} else if (item.toLowerCase().indexOf(' in ') > -1) {
label = item.substring(0, item.toLowerCase().indexOf(' in '))
key = 'in'
} else if (item.toLowerCase().indexOf('has(') > -1) {
label = item.substring(item.toLowerCase().indexOf('(') + 1, item.indexOf(','))
key = 'has'
}
const obj = columnList.find(t => t.label.toLowerCase() === label.toLowerCase())
if (obj) {
if (key === 'has') {
returnObj.key += 'has(' + obj.label + item.substring(item.indexOf(','), item.length) + ' AND '
} else {
returnObj.key += obj.label + ' ' + item.substring(item.toLowerCase().indexOf(key.toLowerCase()), item.length) + ' AND '
}
returnObj.isKey = true
} else {
return { key: '[' + key + ']', isKey: false }
}
})
returnObj.key = returnObj.key.substring(0, returnObj.key.length - 5)
return returnObj
} else if (q.indexOf(' LIKE ') > -1 || q.indexOf(' like ') > -1) {
return {
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('='))
const obj = columnList.find(t => t.label.toLowerCase() === key.toLowerCase())
if (obj) {
return { key: obj.label + q.substring(q.indexOf('='), q.length), isKey: true }
} else {
return { key: '[' + key + ']', isKey: false }
}
}
} else if (q && (q.indexOf(' IN ') > -1 || q.indexOf(' in ') > -1 || q.indexOf(' LIKE ') > -1 || q.indexOf(' like ') > -1)) {
return {
key: q,
isKey: true
}
} else if (q && (q.indexOf('has(') > -1)) {
return {
key: q,
isKey: true
}
} else {
return {
key: q,
isKey: false
}
}
}
/**
* 将模糊查询传过来的str转换为对应的实体类型不满足ip和domain格式的当成app
* @param str
* @returns {string}
*/
export const handleEntityTypeByStr = (str) => {
if (str) {
const arr = []
// 如果出现columnList中的字段如IP\Domain\App\Country等则不进行模糊搜索将str返回出去
columnList.forEach(item => {
arr.push(item.label.toLowerCase())
})
// 因为手动输入时可能会输入and所以将操作符的AND转换为and统一处理
let newStr = str.replace(/ AND /g, ' and ')
// 将str中的IP、Domain等替换为数组arr中的元素
for (let i = 0; i < arr.length; i++) {
newStr = newStr.replace(new RegExp(arr[i], 'g'), arr[i])
}
// 检查str字段在arr中是否出现,true为出现过
const result = arr.some(item => newStr.toLowerCase().includes(item))
if (newStr.indexOf(' and ') > -1) {
// 将单引号包裹的and拿出来放到数组tempList里原来的单引号包裹内容用temp即'it is test keyword{键值}'代替
// 再将字符串用and转换为数组遍历数组发现值为temp的获取键值根据键值获取tempList的值组合起来
// 最后将键值删除,后续再多次测试验证
const tempList = []
const regex = /'([^']*?)'/g
let match
// 将单引号包裹的and内容集合起来
while ((match = regex.exec(newStr)) !== null) {
if (match[1].includes('and')) {
tempList.push(match[1])
}
}
// 将单引号包裹的and内容用特殊值代替
tempList.forEach((item, index) => {
const regex = new RegExp(item, 'g')
newStr = newStr.replace(regex, `it is test keyword${index}`)
})
newStr = newStr.replace(/ and /g, ' AND ')
const noAndList = newStr.split(' AND ')
noAndList.forEach((item, index) => {
// 发现插入的特殊值,获取键值,根据键值替换成原来内容,删除键值
if (item.indexOf('it is test keyword') > -1) {
const regex = /\d+/g
const result1 = item.match(regex)
noAndList[index] = noAndList[index].replace(result1[0], '')
noAndList[index] = noAndList[index].replace('it is test keyword', tempList[result1[0]])
}
})
const newArr = noAndList
newArr.forEach((item, index) => {
if (!arr.some(ite => item.includes(ite))) {
newArr[index] = checkFormatByStr(item)
}
})
newStr = newArr.join(' AND ')
newStr = handleStrToUniteStr(newStr)
return newStr
} else if (result) {
// 不区分大小写用columnList里的label
arr.forEach(item => {
if (str.toLowerCase().indexOf(item.toLowerCase()) > -1) {
str = str.replace(new RegExp(item, 'gi'), item)
}
})
return str
} else if (!result) {
const regex = /^["']|["']$/
// 去除两侧引号,如'1.1.1.1',避免校验时被当作app
if (regex.test(str)) {
str = str.replace(/^['"]+|['"]+$/g, '')
}
}
return checkFormatByStr(str)
}
}
/**
* 校验字符串格式如ip、domain、app
*/
const checkFormatByStr = (str) => {
if (str[0] === "'" && str[str.length - 1] === "'") {
str = str.substring(1, str.length)
str = str.substring(0, str.length - 1)
}
if (str[0] === '%' && str[str.length - 1] === '%') {
str = str.substring(1, str.length)
str = str.substring(0, str.length - 1)
}
if (str[0] === '%' && str[str.length - 1] !== '%') {
str = str.substring(1, str.length)
}
// ipv4校验
const regexIPv4 = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
// ipv6校验
const regexIPv6 = /^(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:)|fe80:(?::[0-9A-Fa-f]{0,4}){0,4}%\w+|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:2[0-4]|1\d|[1-9])?\d|25[0-5])\.(?:(?:2[0-4]|1\d|[1-9])?\d|25[0-5])\.(?:(?:2[0-4]|1\d|[1-9])?\d|25[0-5])\.(?:(?:2[0-4]|1\d|[1-9])?\d|25[0-5])|(?:[0-9A-Fa-f]{1,4}:){1,4}:192\.88\.99\.(\d{1,3})|(?:[0-9A-Fa-f]{1,4}:){1,4}:192\.0\.2\.(\d{1,3})|(?:[0-9A-Fa-f]{1,4}:){1,4}:(?:[0-9A-Fa-f]{1,4}:){0,1}192\.0\.0\.(\d{1,3})|ff00:(?::[0-9A-Fa-f]{0,4}){0,4}|(?:[0-9A-Fa-f]{1,4}:){1,4}:255\.255\.255\.255)$/
// domain校验
const reg = /^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*$/
if (regexIPv4.test(str) || regexIPv6.test(str)) {
const obj = columnList.find(t => t.label.toLowerCase() === 'ip')
return `${obj.label}='${str}'`
} else if (reg.test(str)) {
// 只写作domain即可schema字段更改几次避免后续再更改直接拿columnList的label进行替换
const obj = columnList.find(t => t.label.toLowerCase() === 'domain')
return `${obj.label} LIKE '%${str}'`
} else {
const obj = columnList.find(t => t.label.toLowerCase() === 'app')
return `${obj.label} LIKE '%${str}%'`
}
}
export const getEntityTypeByValue = (str) => {
if (str[0] === "'" && str[str.length - 1] === "'") {
str = str.substring(1, str.length)
str = str.substring(0, str.length - 1)
}
// ipv4校验
const regexIPv4 = /^((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/
// ipv6校验
const regexIPv6 = /^(?:(?:[0-9A-Fa-f]{1,4}:){7}[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,7}:|(?:[0-9A-Fa-f]{1,4}:){1,6}:[0-9A-Fa-f]{1,4}|(?:[0-9A-Fa-f]{1,4}:){1,5}(?::[0-9A-Fa-f]{1,4}){1,2}|(?:[0-9A-Fa-f]{1,4}:){1,4}(?::[0-9A-Fa-f]{1,4}){1,3}|(?:[0-9A-Fa-f]{1,4}:){1,3}(?::[0-9A-Fa-f]{1,4}){1,4}|(?:[0-9A-Fa-f]{1,4}:){1,2}(?::[0-9A-Fa-f]{1,4}){1,5}|[0-9A-Fa-f]{1,4}:(?:(?::[0-9A-Fa-f]{1,4}){1,6})|:(?:(?::[0-9A-Fa-f]{1,4}){1,7}|:)|fe80:(?::[0-9A-Fa-f]{0,4}){0,4}%\w+|::(?:ffff(?::0{1,4}){0,1}:){0,1}(?:(?:2[0-4]|1\d|[1-9])?\d|25[0-5])\.(?:(?:2[0-4]|1\d|[1-9])?\d|25[0-5])\.(?:(?:2[0-4]|1\d|[1-9])?\d|25[0-5])\.(?:(?:2[0-4]|1\d|[1-9])?\d|25[0-5])|(?:[0-9A-Fa-f]{1,4}:){1,4}:192\.88\.99\.(\d{1,3})|(?:[0-9A-Fa-f]{1,4}:){1,4}:192\.0\.2\.(\d{1,3})|(?:[0-9A-Fa-f]{1,4}:){1,4}:(?:[0-9A-Fa-f]{1,4}:){0,1}192\.0\.0\.(\d{1,3})|ff00:(?::[0-9A-Fa-f]{0,4}){0,4}|(?:[0-9A-Fa-f]{1,4}:){1,4}:255\.255\.255\.255)$/
// domain校验
const reg = /^(?=^.{3,255}$)(http(s)?:\/\/)?(www\.)?[a-zA-Z0-9][-a-zA-Z0-9]{0,62}(\.[a-zA-Z0-9][-a-zA-Z0-9]{0,62})+(:\d+)*(\/\w+\.\w+)*$/
if (regexIPv4.test(str) || regexIPv6.test(str)) {
const obj = columnList.find(t => t.label.toLowerCase() === 'ip')
return obj.label
} else if (reg.test(str)) {
const obj = columnList.find(t => t.label.toLowerCase() === 'domain')
return obj.label
} else {
const obj = columnList.find(t => t.label.toLowerCase() === 'app')
return obj.label
}
}
/**
* 将字符串的同属性字段合并在一起
* @param str
* @returns {string}
*/
const handleStrToUniteStr = (str) => {
// 若str = "IP='1.1.1.1' AND Domain IN ('baidu.com','jd.com') AND IP='2.2.2.2'"
// 则将转换为 str = "IP IN ('1.1.1.1','2.2.2.2') AND Domain IN ('baidu.com','jd.com')"
const arr = str.split(' AND ')
const repeatArr = [] // 判断label是否重复的数组
// 如果字符串不存在相同字段则直接返回str如IP = '1' AND has(Tag,'1')
arr.forEach(item => {
if (item.indexOf('=') > -1) {
const label = item.substring(0, item.indexOf('='))
repeatArr.push(label)
} else if (item.indexOf(' IN ') > -1) {
const label = item.substring(0, item.indexOf(' IN '))
repeatArr.push(label)
}
})
const set = new Set(repeatArr)
if (set.size === repeatArr.length) {
return str
} else {
const hasArr = [] // has函数的操作不必合并在一起继续用and连接单独放一边
const commonArr = [] // 普通连接符数组,如连接符为=、in等
// 仿造成metaList形式如 [{ label: 'IP', value: '1.1.1.1' } ... ]
// 从而根据label判断是否是同一属性如是IP的话则将value进行拼接然后再使用in ()包裹
arr.forEach(item => {
if (item.indexOf('=') > -1) {
const label = item.substring(0, item.indexOf('='))
const value = item.substring(item.indexOf('=') + 1)
// 去除单引号
commonArr.push({ label: label, value: value.replace(/^'|'$/g, '') })
} else if (item.indexOf(' IN ') > -1) {
const label = item.substring(0, item.indexOf(' IN '))
let value = item.substring(item.indexOf(' IN ') + 4) // 去除()
value = value.replace(/[\(\)]/g, '')
commonArr.push({ label: label, value: value })
} else if (item.toLowerCase().indexOf('has(') > -1) {
hasArr.push(item)
}
})
const commonObj = combineLabel1(commonArr)
const lastObj = {}
for (const i in commonObj) {
if (commonObj[i].indexOf('(') > -1) {
// 原来为IN ()格式的直接保留
lastObj[i] = `${i} IN ${commonObj[i]}`
if (commonObj[i].indexOf('),') > -1) {
// 此时为ip"('1.1.1.1','2.2.2.2'), '3.3.3.3'",in后面还有同类型数据也放到in里
lastObj[i] = lastObj[i].replace('),', ',') + ')'
}
} else if (commonObj[i].indexOf(',') > -1) {
let str = commonObj[i]
str = str.replace(/(\d+\.\d+\.\d+\.\d+),(\d+\.\d+\.\d+\.\d+)/g, "'$1','$2'")
lastObj[i] = `${i} IN (${str})`
} else if (i.toLowerCase() === 'tag') {
lastObj[i] = `has(${i},${commonObj[i]})`
} else {
// 单独存在的,直接保留
lastObj[i] = `${i} = '${commonObj[i]}'`
}
}
let lastStr = ''
for (const i in lastObj) {
lastStr += lastObj[i] + ' AND '
}
const hasStr = hasArr.join(' AND ')
if (hasStr !== '') {
lastStr = lastStr + hasStr
} else {
lastStr = lastStr.slice(0, -5)
}
return lastStr
}
}
/**
* 根据label判断是否是同一属性是的话则将value进行拼接否则按原形式返回
*/
const combineLabel1 = (list) => {
return list.reduce((acc, cur) => {
if (acc[cur.label]) {
acc[cur.label] += `,${cur.value}`
} else {
acc[cur.label] = cur.value
}
return acc
}, {})
}
/**
* 检测传过来的字符串是否包含and
* 例如 app='Computer and Internet'这种情况,需要单独甄别
* @param str
*/
const checkStrIncludeAnd = (str) => {
let arr = []
if (str.indexOf(' and ')) {
arr = str.split(' and ')
}
let label = ''
arr.forEach((item, index) => {
// and前后的语句前面一段不需要甄别因为前一段可能是app='Computer但后一段肯定不属于columnList
// 如果后面一段属于columnList的关键字整段字符串具有and的连接效果
if (index % 2 !== 0) {
if (item.indexOf('=') > -1) {
label = item.substring(0, item.indexOf('='))
} else if (item.toLowerCase().indexOf(' in ') > -1) {
label = item.substring(0, item.toLowerCase().indexOf(' in '))
} else if (item.indexOf('has(') > -1) {
label = item.substring(4, item.indexOf(','))
}
}
})
return columnList.find(t => t.label.toLowerCase() === label.toLowerCase())
}