From 17c4d4c84e0619ba555aab50ade44a05d321cb64 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 3 May 2017 15:35:24 -0400 Subject: [PATCH 1/5] Don't clear search box on search page when a new query is submitted Users want to be able to see the current query --- static/js/directives/ui/search-box/search-box.component.html | 3 +-- static/js/directives/ui/search-box/search-box.component.ts | 3 ++- static/partials/search.html | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/static/js/directives/ui/search-box/search-box.component.html b/static/js/directives/ui/search-box/search-box.component.html index d62b9c2f5..6eb6043da 100644 --- a/static/js/directives/ui/search-box/search-box.component.html +++ b/static/js/directives/ui/search-box/search-box.component.html @@ -38,13 +38,12 @@ - diff --git a/static/js/directives/ui/search-box/search-box.component.ts b/static/js/directives/ui/search-box/search-box.component.ts index cdb30ffff..597019d76 100644 --- a/static/js/directives/ui/search-box/search-box.component.ts +++ b/static/js/directives/ui/search-box/search-box.component.ts @@ -10,6 +10,7 @@ import { Input, Component, Inject } from 'ng-metadata/core'; }) export class SearchBoxComponent { @Input(' { - $event['callback'](true); // Clear the value. + $event['callback'](this.clearOnSearch == 'true'); // Clear the value. this.$location.url('/search'); this.$location.search('q', $event['value']); }, 10); diff --git a/static/partials/search.html b/static/partials/search.html index baca42247..16882e92a 100644 --- a/static/partials/search.html +++ b/static/partials/search.html @@ -1,6 +1,6 @@ + + + {{ result.namespace.name }}/{{ result.name }} +
+
+
+
diff --git a/static/partials/search.html b/static/partials/search.html index 16882e92a..5f2e11f30 100644 --- a/static/partials/search.html +++ b/static/partials/search.html @@ -21,7 +21,7 @@

- + {{ result.namespace.name }}/{{ result.name }}

From 25d8b6ec026c50606f178e5f3c25c5892cdad6a0 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 3 May 2017 15:40:11 -0400 Subject: [PATCH 3/5] Make sure only the first line of the repository description is shown in search results --- static/partials/search.html | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/static/partials/search.html b/static/partials/search.html index 5f2e11f30..df484a81f 100644 --- a/static/partials/search.html +++ b/static/partials/search.html @@ -25,7 +25,7 @@ {{ result.namespace.name }}/{{ result.name }}

- +

Last Modified: From 481d9b23944ff5bec1ce05c809a88acedc0e4796 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 3 May 2017 16:00:50 -0400 Subject: [PATCH 4/5] Add debounce to search --- .../ui/search-box/search-box.component.html | 1 + .../ui/typeahead/typeahead.directive.ts | 20 ++++++++++++++++--- 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/static/js/directives/ui/search-box/search-box.component.html b/static/js/directives/ui/search-box/search-box.component.html index fa6b0c045..162529c52 100644 --- a/static/js/directives/ui/search-box/search-box.component.html +++ b/static/js/directives/ui/search-box/search-box.component.html @@ -49,6 +49,7 @@ (); @Output('taEntered') entered = new EventEmitter(); private itemSelected: boolean = false; + private existingTimer: Promise = null; constructor(@Inject('$element') private $element: ng.IAugmentedJQuery, @Inject('$compile') private $compile: ng.ICompileService, @Inject('$scope') private $scope: ng.IScope, - @Inject('$templateRequest') private $templateRequest: ng.ITemplateRequestService) { + @Inject('$templateRequest') private $templateRequest: ng.ITemplateRequestService, + @Inject('$timeout') private $timeout: ng.ITimeoutService) { } public ngAfterContentInit(): void { @@ -52,12 +55,23 @@ export class TypeaheadDirective implements AfterContentInit { templates: templates, display: this.displayKey, source: (query, results, asyncResults) => { - this.typeahead.emit({'query': query, 'callback': asyncResults}); - this.itemSelected = false; + this.debounceQuery(query, asyncResults); }, }); } + private debounceQuery(query: string, asyncResults: Function): void { + if (this.existingTimer) { + this.$timeout.cancel(this.existingTimer); + this.existingTimer = null; + } + + this.existingTimer = this.$timeout(() => { + this.typeahead.emit({'query': query, 'callback': asyncResults}); + this.itemSelected = false; + }, this.debounce); + } + @HostListener('keyup', ['$event']) public onKeyup(event: JQueryKeyEventObject): void { if (!this.itemSelected && event.keyCode == 13) { From 0164b48a24a8afb7517958f1184f55ad53142620 Mon Sep 17 00:00:00 2001 From: Joseph Schorr Date: Wed, 3 May 2017 17:02:24 -0400 Subject: [PATCH 5/5] Switch repository search to order matches in repo names higher Helps push better results to the top of the results list --- data/model/repository.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/data/model/repository.py b/data/model/repository.py index c2f65bdfe..1b113fe42 100644 --- a/data/model/repository.py +++ b/data/model/repository.py @@ -4,6 +4,7 @@ import random from enum import Enum from datetime import timedelta, datetime from peewee import JOIN_LEFT_OUTER, fn, SQL, IntegrityError +from playhouse.shortcuts import case from cachetools import ttl_cache from data.model import (config, DataModelException, tag, db_transaction, storage, permission, @@ -485,14 +486,21 @@ def _get_sorted_matching_repositories(lookup_value, repo_kind='image', include_p # Always search at least on name (init clause) clause = Repository.name.match(lookup_value) + computed_score = RepositorySearchScore.score.alias('score') + # If the description field is in the search fields, then we need to compute a synthetic score + # to discount the weight of the description more than the name. if SEARCH_FIELDS.description.name in search_fields: clause = Repository.description.match(lookup_value) | clause - last_week = datetime.now() - timedelta(weeks=1) + cases = [ + (Repository.name.match(lookup_value), 100 * RepositorySearchScore.score), + ] + + computed_score = case(None, cases, RepositorySearchScore.score).alias('score') query = (Repository - .select(Repository, Namespace) + .select(Repository, Namespace, computed_score) .join(Namespace, on=(Namespace.id == Repository.namespace_user)) .where(clause) .group_by(Repository.id, Namespace.id)) @@ -507,7 +515,7 @@ def _get_sorted_matching_repositories(lookup_value, repo_kind='image', include_p .switch(Repository) .join(RepositorySearchScore) .group_by(Repository, Namespace, RepositorySearchScore) - .order_by(RepositorySearchScore.score.desc())) + .order_by(SQL('score').desc())) return query