Skip to content

Search improvements #572

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jan 29, 2018
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion shells/dev/target/NativeTypes.vue
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ export default {
theStore () {
return this.$store
},
}
},
mounted () {
Expand Down
16 changes: 5 additions & 11 deletions src/devtools/components/DataField.vue
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,8 @@ import {
isPlainObject,
sortByKey,
openInEditor,
escape
escape,
specialTokenToString
} from 'src/util'

import DataFieldEdit from '../mixins/data-field-edit'
Expand Down Expand Up @@ -269,18 +270,11 @@ export default {

formattedValue () {
const value = this.field.value
let result
if (this.fieldOptions.abstract) {
return ''
} else if (value === null) {
return 'null'
} else if (value === UNDEFINED) {
return 'undefined'
} else if (value === NAN) {
return 'NaN'
} else if (value === INFINITY) {
return 'Infinity'
} else if (value === NEGATIVE_INFINITY) {
return '-Infinity'
} else if ((result = specialTokenToString(value))) {
return result
} else if (this.valueType === 'custom') {
return value._custom.display
} else if (this.valueType === 'array') {
Expand Down
136 changes: 111 additions & 25 deletions src/util.js
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,21 @@ export const SPECIAL_TOKENS = {
'NaN': NAN
}

export function specialTokenToString (value) {
if (value === null) {
return 'null'
} else if (value === UNDEFINED) {
return 'undefined'
} else if (value === NAN) {
return 'NaN'
} else if (value === INFINITY) {
return 'Infinity'
} else if (value === NEGATIVE_INFINITY) {
return '-Infinity'
}
return false
}

/**
* Needed to prevent stack overflow
* while replacing complex objects
Expand Down Expand Up @@ -313,45 +328,116 @@ function isPrimitive (data) {
)
}

/**
* Searches a key or value in the object, with a maximum deepness
* @param {*} obj Search target
* @param {string} searchTerm Search string
* @returns {boolean} Search match
*/
export function searchDeepInObject (obj, searchTerm) {
var match = false
const seen = new Map()
const result = internalSearchObject(obj, searchTerm.toLowerCase(), seen, 0)
seen.clear()
return result
}

const SEARCH_MAX_DEPTH = 10

/**
* Executes a search on each field of the provided object
* @param {*} obj Search target
* @param {string} searchTerm Search string
* @param {Map<any,boolean>} seen Map containing the search result to prevent stack overflow by walking on the same object multiple times
* @param {number} depth Deep search depth level, which is capped to prevent performance issues
* @returns {boolean} Search match
*/
function internalSearchObject (obj, searchTerm, seen, depth) {
if (depth > SEARCH_MAX_DEPTH) {
return false
}
let match = false
const keys = Object.keys(obj)
let key, value
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const value = obj[key]
if (compare(key, searchTerm) || compare(value, searchTerm)) {
match = true
key = keys[i]
value = obj[key]
match = interalSearchCheck(searchTerm, key, value, seen, depth + 1)
if (match) {
break
}
if (isPlainObject(value)) {
match = searchDeepInObject(value, searchTerm)
if (match) {
break
}
}
}
return match
}

function compare (mixedValue, stringValue) {
if (Array.isArray(mixedValue) && searchInArray(mixedValue, stringValue.toLowerCase())) {
return true
/**
* Executes a search on each value of the provided array
* @param {*} array Search target
* @param {string} searchTerm Search string
* @param {Map<any,boolean>} seen Map containing the search result to prevent stack overflow by walking on the same object multiple times
* @param {number} depth Deep search depth level, which is capped to prevent performance issues
* @returns {boolean} Search match
*/
function internalSearchArray (array, searchTerm, seen, depth) {
if (depth > SEARCH_MAX_DEPTH) {
return false
}
if (('' + mixedValue).toLowerCase().indexOf(stringValue.toLowerCase()) !== -1) {
return true
let match = false
let value
for (let i = 0; i < array.length; i++) {
value = array[i]
match = interalSearchCheck(searchTerm, null, value, seen, depth + 1)
if (match) {
break
}
}
return false
return match
}

function searchInArray (arr, searchTerm) {
let found = false
for (let i = 0; i < arr.length; i++) {
if (('' + arr[i]).toLowerCase().indexOf(searchTerm) !== -1) {
found = true
break
}
/**
* Checks if the provided field matches the search terms
* @param {string} searchTerm Search string
* @param {string} key Field key (null if from array)
* @param {*} value Field value
* @param {Map<any,boolean>} seen Map containing the search result to prevent stack overflow by walking on the same object multiple times
* @param {number} depth Deep search depth level, which is capped to prevent performance issues
* @returns {boolean} Search match
*/
function interalSearchCheck (searchTerm, key, value, seen, depth) {
let match = false
let result
if (key === '_custom') {
key = value.display
value = value.value
}
return found
(result = specialTokenToString(value)) && (value = result)
if (key && compare(key, searchTerm)) {
match = true
seen.set(value, true)
} else if (seen.has(value)) {
match = seen.get(value)
} else if (Array.isArray(value)) {
seen.set(value, null)
match = internalSearchArray(value, searchTerm, seen, depth)
seen.set(value, match)
} else if (isPlainObject(value)) {
seen.set(value, null)
match = internalSearchObject(value, searchTerm, seen, depth)
seen.set(value, match)
} else if (compare(value, searchTerm)) {
match = true
seen.set(value, true)
}
return match
}

/**
* Compares two values
* @param {*} value Mixed type value that will be cast to string
* @param {string} searchTerm Search string
* @returns {boolean} Search match
*/
function compare (value, searchTerm) {
return ('' + value).toLowerCase().indexOf(searchTerm) !== -1
}

export function sortByKey (state) {
Expand Down