Improved perforamnce of global search (reduced throttle of search queries, ony start queries if query has changed).
This commit is contained in:
parent
ab6634bcfe
commit
dd54f8a787
3 changed files with 134 additions and 64 deletions
|
@ -17,6 +17,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
|
||||||
'dblclick form.search-holder .icon-magnifier': 'openExtendedSearch'
|
'dblclick form.search-holder .icon-magnifier': 'openExtendedSearch'
|
||||||
'focus #global-search': 'searchFocus'
|
'focus #global-search': 'searchFocus'
|
||||||
'blur #global-search': 'searchBlur'
|
'blur #global-search': 'searchBlur'
|
||||||
|
'paste #global-search': 'searchPaste'
|
||||||
'keyup #global-search': 'listNavigate'
|
'keyup #global-search': 'listNavigate'
|
||||||
'click .js-global-search-result': 'emptyAndCloseDelayed'
|
'click .js-global-search-result': 'emptyAndCloseDelayed'
|
||||||
'click .js-details-link': 'openExtendedSearch'
|
'click .js-details-link': 'openExtendedSearch'
|
||||||
|
@ -27,8 +28,6 @@ class App.Navigation extends App.ControllerWidgetPermanent
|
||||||
super
|
super
|
||||||
@render()
|
@render()
|
||||||
|
|
||||||
@throttledSearch = _.throttle @search, 200
|
|
||||||
|
|
||||||
@globalSearch = new App.GlobalSearch(
|
@globalSearch = new App.GlobalSearch(
|
||||||
render: @renderResult
|
render: @renderResult
|
||||||
)
|
)
|
||||||
|
@ -171,20 +170,14 @@ class App.Navigation extends App.ControllerWidgetPermanent
|
||||||
type: 'personal'
|
type: 'personal'
|
||||||
)
|
)
|
||||||
|
|
||||||
renderResult: (result = [], noChange) =>
|
renderResult: (result = []) =>
|
||||||
if noChange
|
|
||||||
return
|
|
||||||
|
|
||||||
@removePopovers()
|
@removePopovers()
|
||||||
|
|
||||||
# remove result if not result exists
|
# remove result if not result exists
|
||||||
if _.isEmpty(result)
|
if _.isEmpty(result)
|
||||||
@searchContainer.removeClass('loading').addClass('no-match')
|
|
||||||
@searchResult.html(App.view('navigation/no_result')())
|
@searchResult.html(App.view('navigation/no_result')())
|
||||||
return
|
return
|
||||||
|
|
||||||
@searchContainer.removeClass('no-match loading')
|
|
||||||
|
|
||||||
# build markup
|
# build markup
|
||||||
html = App.view('navigation/result')(
|
html = App.view('navigation/result')(
|
||||||
result: result
|
result: result
|
||||||
|
@ -215,11 +208,21 @@ class App.Navigation extends App.ControllerWidgetPermanent
|
||||||
|
|
||||||
searchFocus: (e) =>
|
searchFocus: (e) =>
|
||||||
@clearDelay('emptyAndCloseDelayed')
|
@clearDelay('emptyAndCloseDelayed')
|
||||||
@throttledSearch()
|
@query = undefined
|
||||||
|
@search(10)
|
||||||
App.PopoverProvidable.anyPopoversDestroy()
|
App.PopoverProvidable.anyPopoversDestroy()
|
||||||
@searchContainer.addClass('focused')
|
@searchContainer.addClass('focused')
|
||||||
@selectAll(e)
|
@selectAll(e)
|
||||||
|
|
||||||
|
searchPaste: (e) =>
|
||||||
|
update = =>
|
||||||
|
@clearDelay('emptyAndCloseDelayed')
|
||||||
|
@query = undefined
|
||||||
|
@search(10)
|
||||||
|
App.PopoverProvidable.anyPopoversDestroy()
|
||||||
|
@searchContainer.addClass('focused')
|
||||||
|
@delay(update, 10, 'searchFocus')
|
||||||
|
|
||||||
searchBlur: (e) =>
|
searchBlur: (e) =>
|
||||||
|
|
||||||
# delay to be able to "click/execute" x if query is ''
|
# delay to be able to "click/execute" x if query is ''
|
||||||
|
@ -249,7 +252,7 @@ class App.Navigation extends App.ControllerWidgetPermanent
|
||||||
return
|
return
|
||||||
|
|
||||||
# on other keys, show result
|
# on other keys, show result
|
||||||
@throttledSearch()
|
@search(0)
|
||||||
|
|
||||||
nudge: (e, position) =>
|
nudge: (e, position) =>
|
||||||
|
|
||||||
|
@ -307,22 +310,41 @@ class App.Navigation extends App.ControllerWidgetPermanent
|
||||||
@globalSearch.close()
|
@globalSearch.close()
|
||||||
@delayedRemoveAnyPopover()
|
@delayedRemoveAnyPopover()
|
||||||
|
|
||||||
search: =>
|
search: (delay) =>
|
||||||
query = @searchInput.val().trim()
|
query = @searchInput.val().trim()
|
||||||
@searchContainer.toggleClass('filled', !!query)
|
@searchContainer.toggleClass('filled', !!query)
|
||||||
|
|
||||||
# if we started a new search and already typed something in
|
return if @query is query
|
||||||
if query != '' and @query == ''
|
|
||||||
@searchContainer.addClass('open no-match loading')
|
|
||||||
|
|
||||||
@query = query
|
@query = query
|
||||||
|
|
||||||
if @query == ''
|
if delay is 0
|
||||||
@searchContainer.removeClass('open loading')
|
delay = 500
|
||||||
|
if query.length > 2
|
||||||
|
delay = 350
|
||||||
|
else if query.length > 4
|
||||||
|
delay = 200
|
||||||
|
|
||||||
|
# if we started a new search and already typed something in
|
||||||
|
if query is ''
|
||||||
|
@searchContainer.removeClass('open')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
@globalSearch.search(
|
||||||
|
delay: delay
|
||||||
|
query: @query
|
||||||
|
callbackLongerAsExpected: =>
|
||||||
|
@searchContainer.removeClass('open')
|
||||||
|
callbackNoMatch: =>
|
||||||
|
@searchContainer.addClass('no-match')
|
||||||
@searchContainer.addClass('open')
|
@searchContainer.addClass('open')
|
||||||
@globalSearch.search(query: @query)
|
callbackMatch: =>
|
||||||
|
@searchContainer.removeClass('no-match')
|
||||||
|
@searchContainer.addClass('open')
|
||||||
|
callbackStop: =>
|
||||||
|
@searchContainer.removeClass('loading')
|
||||||
|
callbackStart: =>
|
||||||
|
@searchContainer.addClass('loading')
|
||||||
|
)
|
||||||
|
|
||||||
filterNavbar: (values, parent = null) ->
|
filterNavbar: (values, parent = null) ->
|
||||||
return _.filter values, (item) =>
|
return _.filter values, (item) =>
|
||||||
|
|
|
@ -24,8 +24,6 @@ class App.Search extends App.Controller
|
||||||
# update taskbar with new meta data
|
# update taskbar with new meta data
|
||||||
App.TaskManager.touch(@taskKey)
|
App.TaskManager.touch(@taskKey)
|
||||||
|
|
||||||
@throttledSearch = _.throttle @search, 200
|
|
||||||
|
|
||||||
@globalSearch = new App.GlobalSearch(
|
@globalSearch = new App.GlobalSearch(
|
||||||
render: @renderResult
|
render: @renderResult
|
||||||
limit: 50
|
limit: 50
|
||||||
|
@ -59,9 +57,11 @@ class App.Search extends App.Controller
|
||||||
@table.show()
|
@table.show()
|
||||||
@navupdate(url: '#search', type: 'menu')
|
@navupdate(url: '#search', type: 'menu')
|
||||||
return if _.isEmpty(params.query)
|
return if _.isEmpty(params.query)
|
||||||
|
|
||||||
@$('.js-search').val(params.query).trigger('change')
|
@$('.js-search').val(params.query).trigger('change')
|
||||||
return if @shown
|
return if @shown
|
||||||
@throttledSearch(true)
|
|
||||||
|
@search(1000, true)
|
||||||
|
|
||||||
hide: ->
|
hide: ->
|
||||||
@shown = false
|
@shown = false
|
||||||
|
@ -102,7 +102,7 @@ class App.Search extends App.Controller
|
||||||
)
|
)
|
||||||
|
|
||||||
if @query
|
if @query
|
||||||
@throttledSearch(true)
|
@search(500, true)
|
||||||
|
|
||||||
listNavigate: (e) =>
|
listNavigate: (e) =>
|
||||||
if e.keyCode is 27 # close on esc
|
if e.keyCode is 27 # close on esc
|
||||||
|
@ -110,7 +110,7 @@ class App.Search extends App.Controller
|
||||||
return
|
return
|
||||||
|
|
||||||
# on other keys, show result
|
# on other keys, show result
|
||||||
@throttledSearch(200)
|
@search(0)
|
||||||
|
|
||||||
empty: =>
|
empty: =>
|
||||||
@searchInput.val('')
|
@searchInput.val('')
|
||||||
|
@ -120,7 +120,7 @@ class App.Search extends App.Controller
|
||||||
|
|
||||||
@delayedRemoveAnyPopover()
|
@delayedRemoveAnyPopover()
|
||||||
|
|
||||||
search: (force = false) =>
|
search: (delay, force = false) =>
|
||||||
query = @searchInput.val().trim()
|
query = @searchInput.val().trim()
|
||||||
if !force
|
if !force
|
||||||
return if !query
|
return if !query
|
||||||
|
@ -128,7 +128,17 @@ class App.Search extends App.Controller
|
||||||
@query = query
|
@query = query
|
||||||
@updateTask()
|
@updateTask()
|
||||||
|
|
||||||
@globalSearch.search(query: @query)
|
if delay is 0
|
||||||
|
delay = 500
|
||||||
|
if query.length > 2
|
||||||
|
delay = 350
|
||||||
|
else if query.length > 4
|
||||||
|
delay = 200
|
||||||
|
|
||||||
|
@globalSearch.search(
|
||||||
|
delay: delay
|
||||||
|
query: @query
|
||||||
|
)
|
||||||
|
|
||||||
renderResult: (result = []) =>
|
renderResult: (result = []) =>
|
||||||
@result = result
|
@result = result
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
class App.GlobalSearch extends App.Controller
|
class App.GlobalSearch extends App.Controller
|
||||||
|
ajaxCount: 0
|
||||||
constructor: ->
|
constructor: ->
|
||||||
super
|
super
|
||||||
@searchResultCache = {}
|
@searchResultCache = {}
|
||||||
|
@ -12,10 +13,25 @@ class App.GlobalSearch extends App.Controller
|
||||||
# use cache for search result
|
# use cache for search result
|
||||||
currentTime = new Date
|
currentTime = new Date
|
||||||
if @searchResultCache[query] && @searchResultCache[query].time > currentTime.setSeconds(currentTime.getSeconds() - 20)
|
if @searchResultCache[query] && @searchResultCache[query].time > currentTime.setSeconds(currentTime.getSeconds() - 20)
|
||||||
@renderTry(@searchResultCache[query].result, query)
|
if @ajaxRequestId
|
||||||
|
App.Ajax.abort(@ajaxRequestId)
|
||||||
|
@ajaxStart(params)
|
||||||
|
@renderTry(@searchResultCache[query].result, query, params)
|
||||||
|
delayCallback = =>
|
||||||
|
@ajaxStop(params)
|
||||||
|
@delay(delayCallback, 700)
|
||||||
return
|
return
|
||||||
|
|
||||||
App.Ajax.request(
|
delayCallback = =>
|
||||||
|
|
||||||
|
@ajaxStart(params)
|
||||||
|
|
||||||
|
delayCallback = ->
|
||||||
|
if params.callbackLongerAsExpected
|
||||||
|
params.callbackLongerAsExpected()
|
||||||
|
@delay(delayCallback, 10000, 'global-search-ajax-longer-as-expected')
|
||||||
|
|
||||||
|
@ajaxRequestId = App.Ajax.request(
|
||||||
id: @ajaxId
|
id: @ajaxId
|
||||||
type: 'GET'
|
type: 'GET'
|
||||||
url: "#{@apiPath}/search"
|
url: "#{@apiPath}/search"
|
||||||
|
@ -24,6 +40,7 @@ class App.GlobalSearch extends App.Controller
|
||||||
limit: @limit || 10
|
limit: @limit || 10
|
||||||
processData: true
|
processData: true
|
||||||
success: (data, status, xhr) =>
|
success: (data, status, xhr) =>
|
||||||
|
@clearDelay('global-search-ajax-longer-as-expected')
|
||||||
App.Collection.loadAssets(data.assets)
|
App.Collection.loadAssets(data.assets)
|
||||||
result = {}
|
result = {}
|
||||||
for item in data.result
|
for item in data.result
|
||||||
|
@ -38,17 +55,38 @@ class App.GlobalSearch extends App.Controller
|
||||||
App.Log.error('_globalSearchSingleton', "No such model #{item.type.toLocaleLowerCase()}.searchResultAttributes()")
|
App.Log.error('_globalSearchSingleton', "No such model #{item.type.toLocaleLowerCase()}.searchResultAttributes()")
|
||||||
else
|
else
|
||||||
App.Log.error('_globalSearchSingleton', "No such model App.#{item.type}")
|
App.Log.error('_globalSearchSingleton', "No such model App.#{item.type}")
|
||||||
|
@ajaxStop(params)
|
||||||
@renderTry(result, query)
|
@renderTry(result, query, params)
|
||||||
|
error: =>
|
||||||
|
@clearDelay('global-search-ajax-longer-as-expected')
|
||||||
|
@ajaxStop(params)
|
||||||
)
|
)
|
||||||
|
@delay(delayCallback, params.delay || 1, 'global-search-ajax')
|
||||||
|
|
||||||
renderTry: (result, query) =>
|
ajaxStart: (params) =>
|
||||||
|
@ajaxCount++
|
||||||
|
if params.callbackStart
|
||||||
|
params.callbackStart()
|
||||||
|
|
||||||
|
ajaxStop: (params) =>
|
||||||
|
@ajaxCount--
|
||||||
|
if @ajaxCount == 0 && params.callbackStop
|
||||||
|
params.callbackStop()
|
||||||
|
|
||||||
|
renderTry: (result, query, params) =>
|
||||||
|
|
||||||
|
if query
|
||||||
|
if _.isEmpty(result)
|
||||||
|
if params.callbackNoMatch
|
||||||
|
params.callbackNoMatch()
|
||||||
|
else
|
||||||
|
if params.callbackMatch
|
||||||
|
params.callbackMatch()
|
||||||
|
|
||||||
# if result hasn't changed, do not rerender
|
# if result hasn't changed, do not rerender
|
||||||
if @lastQuery is query && @searchResultCache[query]
|
if @lastQuery is query && @searchResultCache[query]
|
||||||
diff = difference(@searchResultCache[query].result, result)
|
diff = difference(@searchResultCache[query].result, result)
|
||||||
if _.isEmpty(diff)
|
if _.isEmpty(diff)
|
||||||
@render(result, true)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
@lastQuery = query
|
@lastQuery = query
|
||||||
|
|
Loading…
Reference in a new issue