Add documentation search to the main search bar
This commit is contained in:
parent
db841de26f
commit
8a8955d234
5 changed files with 142 additions and 6 deletions
|
@ -13,7 +13,8 @@ angular.module('quay').directive('headerBar', function () {
|
|||
scope: {
|
||||
},
|
||||
controller: function($rootScope, $scope, $element, $location, $timeout, hotkeys, UserService,
|
||||
PlanService, ApiService, NotificationService, Config, CreateService, Features) {
|
||||
PlanService, ApiService, NotificationService, Config, CreateService, Features,
|
||||
DocumentationService) {
|
||||
var hotkeysAdded = false;
|
||||
var userUpdated = function(cUser) {
|
||||
$scope.searchingAllowed = Features.ANONYMOUS_ACCESS || !cUser.anonymous;
|
||||
|
@ -71,6 +72,39 @@ angular.module('quay').directive('headerBar', function () {
|
|||
$scope.currentPageContext['repository'] = r;
|
||||
});
|
||||
|
||||
var documentSearchMaxResults = 10;
|
||||
var documentSearchScoreThreshold = 0.9;
|
||||
|
||||
var conductDocumentationSearch = function(query) {
|
||||
if (!query) { return; }
|
||||
|
||||
var mapper = function(result, score) {
|
||||
return {
|
||||
'kind': 'doc',
|
||||
'name': result.title.replace(/'\;/g, "'"),
|
||||
'score': score,
|
||||
'href': Config.DOCUMENTATION_LOCATION + result.url
|
||||
}
|
||||
};
|
||||
|
||||
DocumentationService.findDocumentation($scope, query.split(' '), function(results) {
|
||||
if (!$scope.searchVisible) { return; }
|
||||
|
||||
var currentResults = $scope.searchResultState['results'];
|
||||
results.forEach(function(result) {
|
||||
if (currentResults.length < documentSearchMaxResults) {
|
||||
currentResults.push(result);
|
||||
}
|
||||
});
|
||||
|
||||
$scope.searchResultState = {
|
||||
'state': currentResults.length ? 'results' : 'no-results',
|
||||
'results': currentResults,
|
||||
'current': currentResults.length ? 0 : -1
|
||||
};
|
||||
}, mapper, documentSearchScoreThreshold);
|
||||
}
|
||||
|
||||
var conductSearch = function(query) {
|
||||
if (!query) { $scope.searchResultState = null; return; }
|
||||
|
||||
|
@ -90,6 +124,10 @@ angular.module('quay').directive('headerBar', function () {
|
|||
'results': resp.results,
|
||||
'current': resp.results.length ? 0 : -1
|
||||
};
|
||||
|
||||
if (resp.results.length < documentSearchMaxResults) {
|
||||
conductDocumentationSearch(query);
|
||||
}
|
||||
}, function(resp) {
|
||||
$scope.searchResultState = null;
|
||||
}, /* background */ true);
|
||||
|
@ -178,6 +216,11 @@ angular.module('quay').directive('headerBar', function () {
|
|||
$scope.showResult = function(result) {
|
||||
$scope.toggleSearch();
|
||||
$timeout(function() {
|
||||
if (result['kind'] == 'doc') {
|
||||
window.location = result['href'];
|
||||
return;
|
||||
}
|
||||
|
||||
$scope.currentSearchQuery = '';
|
||||
$location.url(result['href'])
|
||||
}, 500);
|
||||
|
|
86
static/js/services/documentation-service.js
Normal file
86
static/js/services/documentation-service.js
Normal file
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* Service which exposes access to the documentation metadata.
|
||||
*/
|
||||
angular.module('quay').factory('DocumentationService', ['Config', '$timeout', function(Config, $timeout) {
|
||||
var documentationService = {};
|
||||
var documentationData = null;
|
||||
var documentationFailure = false;
|
||||
|
||||
var MINIMUM_KEYWORD_LENGTH = 3;
|
||||
var TITLE_MATCH_SCORE = 1;
|
||||
var CONTENT_MATCH_SCORE = 0.5;
|
||||
|
||||
documentationService.findDocumentation = function($scope, keywords, callback, opt_mapper, opt_threshold) {
|
||||
opt_threshold = opt_threshold || 0;
|
||||
|
||||
documentationService.loadDocumentation(function(metadata) {
|
||||
if (!metadata) {
|
||||
$scope.$apply(function() {
|
||||
callback([]);
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
var results = [];
|
||||
|
||||
metadata.forEach(function(page) {
|
||||
var score = 0;
|
||||
|
||||
keywords.forEach(function(keyword) {
|
||||
if (keyword.length < MINIMUM_KEYWORD_LENGTH) { return; }
|
||||
|
||||
var title = page.title || '';
|
||||
var content = page.content || '';
|
||||
|
||||
if (title.toLowerCase().indexOf(keyword.toLowerCase()) >= 0) {
|
||||
score += TITLE_MATCH_SCORE;
|
||||
}
|
||||
|
||||
if (content.toLowerCase().indexOf(keyword.toLowerCase()) >= 0) {
|
||||
score += CONTENT_MATCH_SCORE;
|
||||
}
|
||||
});
|
||||
|
||||
if (score > opt_threshold) {
|
||||
results.push(opt_mapper ? opt_mapper(page, score) : {'page': page, 'score': score});
|
||||
}
|
||||
});
|
||||
|
||||
$scope.$apply(function() {
|
||||
results.sort(function(a, b) {
|
||||
return b.score - a.score;
|
||||
});
|
||||
|
||||
callback(results);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
documentationService.loadDocumentation = function(callback) {
|
||||
if (documentationFailure) {
|
||||
$timeout(function() {
|
||||
callback(null);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
if (documentationData != null) {
|
||||
$timeout(function() {
|
||||
callback(documentationData);
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
$.ajax(Config.DOCUMENTATION_LOCATION + '/search.json')
|
||||
.done(function(r) {
|
||||
documentationData = r;
|
||||
callback(documentationData);
|
||||
})
|
||||
.fail(function() {
|
||||
documentationFailure = true;
|
||||
});
|
||||
};
|
||||
|
||||
return documentationService;
|
||||
}]);
|
Reference in a new issue