Commit 67469995 authored by Randall Leeds's avatar Randall Leeds

Refactor page search

Refactor the page search rendering algorithm for fewer scope
variables, simpler template expressions, camel case, support
for holes in threads, less looping, conciseness and readability.
parent 5736d80b
This diff is collapsed.
...@@ -73,7 +73,7 @@ class SearchFilter ...@@ -73,7 +73,7 @@ class SearchFilter
filter = term.slice 0, term.indexOf ":" filter = term.slice 0, term.indexOf ":"
unless filter? then filter = "" unless filter? then filter = ""
switch filter switch filter
when 'quote' then quote.push term[6..] when 'quote' then quote.push term[6..].toLowerCase()
when 'result' then result.push term[7..] when 'result' then result.push term[7..]
when 'since' when 'since'
# We'll turn this into seconds # We'll turn this into seconds
...@@ -109,44 +109,36 @@ class SearchFilter ...@@ -109,44 +109,36 @@ class SearchFilter
# Time given in year # Time given in year
t = /^(\d+)year$/.exec(time)[1] t = /^(\d+)year$/.exec(time)[1]
since.push t * 60 * 60 * 24 * 365 since.push t * 60 * 60 * 24 * 365
when 'tag' then tag.push term[4..] when 'tag' then tag.push term[4..].toLowerCase()
when 'text' then text.push term[5..] when 'text' then text.push term[5..].toLowerCase()
when 'uri' then uri.push term[4..] when 'uri' then uri.push term[4..].toLowerCase()
when 'user' then user.push term[5..] when 'user' then user.push term[5..].toLowerCase()
else any.push term else any.push term.toLowerCase()
any: any:
terms: any terms: any
operator: 'and' operator: 'and'
lowercase: true
quote: quote:
terms: quote terms: quote
operator: 'and' operator: 'and'
lowercase: true
result: result:
terms: result terms: result
operator: 'min' operator: 'min'
lowercase: false
since: since:
terms: since terms: since
operator: 'and' operator: 'and'
lowercase: false
tag: tag:
terms: tag terms: tag
operator: 'and' operator: 'and'
lowercase: true
text: text:
terms: text terms: text
operator: 'and' operator: 'and'
lowercase: true
uri: uri:
terms: uri terms: uri
operator: 'or' operator: 'or'
lowercase: true
user: user:
terms: user terms: user
operator: 'or' operator: 'or'
lowercase: true
# This class will process the results of search and generate the correct filter # This class will process the results of search and generate the correct filter
...@@ -235,19 +227,6 @@ class QueryParser ...@@ -235,19 +227,6 @@ class QueryParser
and_or: 'and' and_or: 'and'
fields: ['quote', 'tag', 'text', 'uri', 'user'] fields: ['quote', 'tag', 'text', 'uri', 'user']
parseModels: (models) ->
# Cluster facets together
categories = {}
for searchItem in models
category = searchItem.attributes.category
value = searchItem.attributes.value
if category of categories
categories[category].push value
else
categories[category] = [value]
categories
populateFilter: (filter, query) => populateFilter: (filter, query) =>
# Populate a filter with a query object # Populate a filter with a query object
for category, value of query for category, value of query
......
imports = [
'h.filters',
'h.searchfilters'
]
# The render function accepts a scope and a data object and schedule the scope # The render function accepts a scope and a data object and schedule the scope
# to be updated with the provided data and digested before the next repaint # to be updated with the provided data and digested before the next repaint
# using window.requestAnimationFrame() (or a fallback). If the resulting digest # using window.requestAnimationFrame() (or a fallback). If the resulting digest
...@@ -501,12 +495,6 @@ class ViewFilter ...@@ -501,12 +495,6 @@ class ViewFilter
any: any:
fields: ['quote', 'text', 'tag', 'user'] fields: ['quote', 'text', 'tag', 'user']
this.$inject = ['searchfilter']
constructor: (searchfilter) ->
@searchfilter = searchfilter
_matches: (filter, value, match) -> _matches: (filter, value, match) ->
matches = true matches = true
...@@ -549,17 +537,17 @@ class ViewFilter ...@@ -549,17 +537,17 @@ class ViewFilter
value = checker.value annotation value = checker.value annotation
if angular.isArray value if angular.isArray value
if filter.lowercase then value = value.map (e) -> e.toLowerCase() if typeof(value[0]) == 'string'
value = value.map (v) -> v.toLowerCase()
return @_arrayMatches filter, value, checker.match return @_arrayMatches filter, value, checker.match
else else
value = value.toLowerCase() if filter.lowercase value = value.toLowerCase() if typeof(value) == 'string'
return @_matches filter, value, checker.match return @_matches filter, value, checker.match
# Filters a set of annotations, according to a given query. # Filters a set of annotations, according to a given query.
# Inputs: # Inputs:
# annotations is the input list of annotations (array) # annotations is the input list of annotations (array)
# query is the query string. It will be converted to faceted filter by the SearchFilter # filters is the query is a faceted filter generated by SearchFilter
# #
# It'll handle the annotation matching by the returned facet configuration (operator, lowercase, etc.) # It'll handle the annotation matching by the returned facet configuration (operator, lowercase, etc.)
# and the here configured @checkers. This @checkers object contains instructions how to verify the match. # and the here configured @checkers. This @checkers object contains instructions how to verify the match.
...@@ -577,84 +565,34 @@ class ViewFilter ...@@ -577,84 +565,34 @@ class ViewFilter
# matched annotation IDs list, # matched annotation IDs list,
# the faceted filters # the faceted filters
# ] # ]
filter: (annotations, query) => filter: (annotations, filters) ->
filters = @searchfilter.generateFacetedFilter query limit = Math.min((filters.result?.terms or [])...)
results = [] count = 0
# Check for given limit results = for annotation in annotations
# Find the minimal break if count >= limit
limit = 0
if filters.result.terms.length
limit = filter.result.terms[0]
for term in filter.result.terms
if limit > term then limit = term
# Convert terms to lowercase if needed
for _, filter of filters
if filter.lowercase then filter.terms.map (e) -> e.toLowerCase()
# Now that this filter is called with the top level annotations, we have to add the children too
annotationsWithChildren = []
for annotation in annotations
annotationsWithChildren.push annotation
children = annotation.thread?.flattenChildren()
if children?.length > 0
for child in children
annotationsWithChildren.push child
for annotation in annotationsWithChildren
matches = true
#ToDo: What about given zero limit?
# Limit reached
if limit and results.length >= limit then break
match = true
for category, filter of filters for category, filter of filters
break unless matches break unless match
terms = filter.terms continue unless filter.terms.length
# No condition for this category
continue unless terms.length
switch category switch category
when 'result'
# Handled above
continue
when 'any' when 'any'
# Special case categoryMatch = false
matchterms = []
matchterms.push false for term in terms
for field in @checkers.any.fields for field in @checkers.any.fields
conf = @checkers[field] if @_checkMatch(filter, annotation, @checkers[field])
categoryMatch = true
continue if conf.autofalse? and conf.autofalse annotation break
value = conf.value annotation match = categoryMatch
if angular.isArray value
if filter.lowercase
value = value.map (e) -> e.toLowerCase()
else
value = value.toLowerCase() if filter.lowercase
matchresult = @_anyMatches filter, value, conf.match
matchterms = matchterms.map (t, i) -> t or matchresult[i]
# Now let's see what we got.
matched = 0
for _, value of matchterms
matched++ if value
if (filter.operator is 'or' and matched > 0) or (filter.operator is 'and' and matched is terms.length)
matches = true
else
matches = false
else else
# For all other categories match = @_checkMatch filter, annotation, @checkers[category]
matches = @_checkMatch filter, annotation, @checkers[category]
if matches
results.push annotation.id
[results, filters]
continue unless match
count++
annotation.id
angular.module('h.services', imports) angular.module('h.services', [])
.factory('render', renderFactory) .factory('render', renderFactory)
.provider('drafts', DraftProvider) .provider('drafts', DraftProvider)
.service('annotator', Hypothesis) .service('annotator', Hypothesis)
......
...@@ -36,7 +36,7 @@ class StreamSearch ...@@ -36,7 +36,7 @@ class StreamSearch
$scope.sort.name = 'Newest' $scope.sort.name = 'Newest'
$scope.shouldShowAnnotation = (id) -> true $scope.shouldShowThread = (container) -> true
$scope.$watch 'updater', (updater) -> $scope.$watch 'updater', (updater) ->
updater?.then (sock) -> updater?.then (sock) ->
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment