[Code Style] Refactor search to use prototypes
WTD-1482.
This commit is contained in:
@@ -44,17 +44,52 @@ define(
|
|||||||
* @param $http Angular's $http service, for working with urls.
|
* @param $http Angular's $http service, for working with urls.
|
||||||
* @param {ObjectService} objectService the service from which
|
* @param {ObjectService} objectService the service from which
|
||||||
* domain objects can be gotten.
|
* domain objects can be gotten.
|
||||||
* @param ROOT the constant ELASTIC_ROOT which allows us to
|
* @param root the constant `ELASTIC_ROOT` which allows us to
|
||||||
* interact with ElasticSearch.
|
* interact with ElasticSearch.
|
||||||
*/
|
*/
|
||||||
function ElasticsearchSearchProvider($http, objectService, ROOT) {
|
function ElasticsearchSearchProvider($http, objectService, root) {
|
||||||
|
this.$http = $http;
|
||||||
// Add the fuzziness operator to the search term
|
this.objectService = objectService;
|
||||||
|
this.root = root;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches through the filetree for domain objects using a search
|
||||||
|
* term. This is done through querying elasticsearch. Returns a
|
||||||
|
* promise for a result object that has the format
|
||||||
|
* {hits: searchResult[], total: number, timedOut: boolean}
|
||||||
|
* where a searchResult has the format
|
||||||
|
* {id: string, object: domainObject, score: number}
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* * The order of the results is from highest to lowest score,
|
||||||
|
* as elsaticsearch determines them to be.
|
||||||
|
* * Uses the fuzziness operator to get more results.
|
||||||
|
* * More about this search's behavior at
|
||||||
|
* https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html
|
||||||
|
*
|
||||||
|
* @param searchTerm The text input that is the query.
|
||||||
|
* @param timestamp The time at which this function was called.
|
||||||
|
* This timestamp is used as a unique identifier for this
|
||||||
|
* query and the corresponding results.
|
||||||
|
* @param maxResults (optional) The maximum number of results
|
||||||
|
* that this function should return.
|
||||||
|
* @param timeout (optional) The time after which the search should
|
||||||
|
* stop calculations and return partial results. Elasticsearch
|
||||||
|
* does not guarentee that this timeout will be strictly followed.
|
||||||
|
*/
|
||||||
|
ElasticsearchSearchProvider.prototype.query = function query(searchTerm, timestamp, maxResults, timeout) {
|
||||||
|
var $http = this.$http,
|
||||||
|
objectService = this.objectService,
|
||||||
|
root = this.root,
|
||||||
|
esQuery;
|
||||||
|
|
||||||
|
// Add the fuzziness operator to the search term
|
||||||
function addFuzziness(searchTerm, editDistance) {
|
function addFuzziness(searchTerm, editDistance) {
|
||||||
if (!editDistance) {
|
if (!editDistance) {
|
||||||
editDistance = '';
|
editDistance = '';
|
||||||
}
|
}
|
||||||
|
|
||||||
return searchTerm.split(' ').map(function (s) {
|
return searchTerm.split(' ').map(function (s) {
|
||||||
// Don't add fuzziness for quoted strings
|
// Don't add fuzziness for quoted strings
|
||||||
if (s.indexOf('"') !== -1) {
|
if (s.indexOf('"') !== -1) {
|
||||||
@@ -64,11 +99,11 @@ define(
|
|||||||
}
|
}
|
||||||
}).join(' ');
|
}).join(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Currently specific to elasticsearch
|
// Currently specific to elasticsearch
|
||||||
function processSearchTerm(searchTerm) {
|
function processSearchTerm(searchTerm) {
|
||||||
var spaceIndex;
|
var spaceIndex;
|
||||||
|
|
||||||
// Cut out any extra spaces
|
// Cut out any extra spaces
|
||||||
while (searchTerm.substr(0, 1) === ' ') {
|
while (searchTerm.substr(0, 1) === ' ') {
|
||||||
searchTerm = searchTerm.substring(1, searchTerm.length);
|
searchTerm = searchTerm.substring(1, searchTerm.length);
|
||||||
@@ -79,18 +114,18 @@ define(
|
|||||||
spaceIndex = searchTerm.indexOf(' ');
|
spaceIndex = searchTerm.indexOf(' ');
|
||||||
while (spaceIndex !== -1) {
|
while (spaceIndex !== -1) {
|
||||||
searchTerm = searchTerm.substring(0, spaceIndex) +
|
searchTerm = searchTerm.substring(0, spaceIndex) +
|
||||||
searchTerm.substring(spaceIndex + 1, searchTerm.length);
|
searchTerm.substring(spaceIndex + 1, searchTerm.length);
|
||||||
spaceIndex = searchTerm.indexOf(' ');
|
spaceIndex = searchTerm.indexOf(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
// Add fuzziness for completeness
|
// Add fuzziness for completeness
|
||||||
searchTerm = addFuzziness(searchTerm);
|
searchTerm = addFuzziness(searchTerm);
|
||||||
|
|
||||||
return searchTerm;
|
return searchTerm;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processes results from the format that elasticsearch returns to
|
// Processes results from the format that elasticsearch returns to
|
||||||
// a list of searchResult objects, then returns a result object
|
// a list of searchResult objects, then returns a result object
|
||||||
// (See documentation for query for object descriptions)
|
// (See documentation for query for object descriptions)
|
||||||
function processResults(rawResults, timestamp) {
|
function processResults(rawResults, timestamp) {
|
||||||
var results = rawResults.data.hits.hits,
|
var results = rawResults.data.hits.hits,
|
||||||
@@ -99,25 +134,25 @@ define(
|
|||||||
scores = {},
|
scores = {},
|
||||||
searchResults = [],
|
searchResults = [],
|
||||||
i;
|
i;
|
||||||
|
|
||||||
// Get the result objects' IDs
|
// Get the result objects' IDs
|
||||||
for (i = 0; i < resultsLength; i += 1) {
|
for (i = 0; i < resultsLength; i += 1) {
|
||||||
ids.push(results[i][ID]);
|
ids.push(results[i][ID]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the result objects' scores
|
// Get the result objects' scores
|
||||||
for (i = 0; i < resultsLength; i += 1) {
|
for (i = 0; i < resultsLength; i += 1) {
|
||||||
scores[ids[i]] = results[i][SCORE];
|
scores[ids[i]] = results[i][SCORE];
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get the domain objects from their IDs
|
// Get the domain objects from their IDs
|
||||||
return objectService.getObjects(ids).then(function (objects) {
|
return objectService.getObjects(ids).then(function (objects) {
|
||||||
var j,
|
var j,
|
||||||
id;
|
id;
|
||||||
|
|
||||||
for (j = 0; j < resultsLength; j += 1) {
|
for (j = 0; j < resultsLength; j += 1) {
|
||||||
id = ids[j];
|
id = ids[j];
|
||||||
|
|
||||||
// Include items we can get models for
|
// Include items we can get models for
|
||||||
if (objects[id].getModel) {
|
if (objects[id].getModel) {
|
||||||
// Format the results as searchResult objects
|
// Format the results as searchResult objects
|
||||||
@@ -128,7 +163,7 @@ define(
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
hits: searchResults,
|
hits: searchResults,
|
||||||
total: rawResults.data.hits.total,
|
total: rawResults.data.hits.total,
|
||||||
@@ -136,76 +171,43 @@ define(
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// For documentation, see query below.
|
|
||||||
function query(searchTerm, timestamp, maxResults, timeout) {
|
|
||||||
var esQuery;
|
|
||||||
|
|
||||||
// Check to see if the user provided a maximum
|
|
||||||
// number of results to display
|
|
||||||
if (!maxResults) {
|
|
||||||
// Else, we provide a default value.
|
|
||||||
maxResults = DEFAULT_MAX_RESULTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the user input is empty, we want to have no search results.
|
|
||||||
if (searchTerm !== '' && searchTerm !== undefined) {
|
|
||||||
// Process the search term
|
|
||||||
searchTerm = processSearchTerm(searchTerm);
|
|
||||||
|
|
||||||
// Create the query to elasticsearch
|
|
||||||
esQuery = ROOT + "/_search/?q=" + searchTerm +
|
|
||||||
"&size=" + maxResults;
|
|
||||||
if (timeout) {
|
|
||||||
esQuery += "&timeout=" + timeout;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get the data...
|
// Check to see if the user provided a maximum
|
||||||
return $http({
|
// number of results to display
|
||||||
method: "GET",
|
if (!maxResults) {
|
||||||
url: esQuery
|
// Else, we provide a default value.
|
||||||
}).then(function (rawResults) {
|
maxResults = DEFAULT_MAX_RESULTS;
|
||||||
// ...then process the data
|
|
||||||
return processResults(rawResults, timestamp);
|
|
||||||
}, function (err) {
|
|
||||||
// In case of error, return nothing. (To prevent
|
|
||||||
// infinite loading time.)
|
|
||||||
return {hits: [], total: 0};
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
return {hits: [], total: 0};
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
// If the user input is empty, we want to have no search results.
|
||||||
/**
|
if (searchTerm !== '' && searchTerm !== undefined) {
|
||||||
* Searches through the filetree for domain objects using a search
|
// Process the search term
|
||||||
* term. This is done through querying elasticsearch. Returns a
|
searchTerm = processSearchTerm(searchTerm);
|
||||||
* promise for a result object that has the format
|
|
||||||
* {hits: searchResult[], total: number, timedOut: boolean}
|
// Create the query to elasticsearch
|
||||||
* where a searchResult has the format
|
esQuery = root + "/_search/?q=" + searchTerm +
|
||||||
* {id: string, object: domainObject, score: number}
|
"&size=" + maxResults;
|
||||||
*
|
if (timeout) {
|
||||||
* Notes:
|
esQuery += "&timeout=" + timeout;
|
||||||
* * The order of the results is from highest to lowest score,
|
}
|
||||||
* as elsaticsearch determines them to be.
|
|
||||||
* * Uses the fuzziness operator to get more results.
|
// Get the data...
|
||||||
* * More about this search's behavior at
|
return this.$http({
|
||||||
* https://www.elastic.co/guide/en/elasticsearch/reference/current/search-uri-request.html
|
method: "GET",
|
||||||
*
|
url: esQuery
|
||||||
* @param searchTerm The text input that is the query.
|
}).then(function (rawResults) {
|
||||||
* @param timestamp The time at which this function was called.
|
// ...then process the data
|
||||||
* This timestamp is used as a unique identifier for this
|
return processResults(rawResults, timestamp);
|
||||||
* query and the corresponding results.
|
}, function (err) {
|
||||||
* @param maxResults (optional) The maximum number of results
|
// In case of error, return nothing. (To prevent
|
||||||
* that this function should return.
|
// infinite loading time.)
|
||||||
* @param timeout (optional) The time after which the search should
|
return {hits: [], total: 0};
|
||||||
* stop calculations and return partial results. Elasticsearch
|
});
|
||||||
* does not guarentee that this timeout will be strictly followed.
|
} else {
|
||||||
*/
|
return {hits: [], total: 0};
|
||||||
query: query
|
}
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return ElasticsearchSearchProvider;
|
return ElasticsearchSearchProvider;
|
||||||
|
|||||||
@@ -48,10 +48,14 @@ define(
|
|||||||
* domain objects' IDs.
|
* domain objects' IDs.
|
||||||
*/
|
*/
|
||||||
function GenericSearchProvider($q, $timeout, objectService, workerService, ROOTS) {
|
function GenericSearchProvider($q, $timeout, objectService, workerService, ROOTS) {
|
||||||
var worker = workerService.run('genericSearchWorker'),
|
var indexed = {},
|
||||||
indexed = {},
|
pendingQueries = {},
|
||||||
pendingQueries = {};
|
worker = workerService.run('genericSearchWorker');
|
||||||
// pendingQueries is a dictionary with the key value pairs st
|
|
||||||
|
this.worker = worker;
|
||||||
|
this.pendingQueries = pendingQueries;
|
||||||
|
this.$q = $q;
|
||||||
|
// pendingQueries is a dictionary with the key value pairs st
|
||||||
// the key is the timestamp and the value is the promise
|
// the key is the timestamp and the value is the promise
|
||||||
|
|
||||||
// Tell the web worker to add a domain object's model to its list of items.
|
// Tell the web worker to add a domain object's model to its list of items.
|
||||||
@@ -71,20 +75,7 @@ define(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Tell the worker to search for items it has that match this searchInput.
|
|
||||||
// Takes the searchInput, as well as a max number of results (will return
|
|
||||||
// less than that if there are fewer matches).
|
|
||||||
function workerSearch(searchInput, maxResults, timestamp, timeout) {
|
|
||||||
var message = {
|
|
||||||
request: 'search',
|
|
||||||
input: searchInput,
|
|
||||||
maxNumber: maxResults,
|
|
||||||
timestamp: timestamp,
|
|
||||||
timeout: timeout
|
|
||||||
};
|
|
||||||
worker.postMessage(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handles responses from the web worker. Namely, the results of
|
// Handles responses from the web worker. Namely, the results of
|
||||||
// a search request.
|
// a search request.
|
||||||
function handleResponse(event) {
|
function handleResponse(event) {
|
||||||
@@ -120,8 +111,6 @@ define(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
worker.onmessage = handleResponse;
|
|
||||||
|
|
||||||
// Helper function for getItems(). Indexes the tree.
|
// Helper function for getItems(). Indexes the tree.
|
||||||
function indexItems(nodes) {
|
function indexItems(nodes) {
|
||||||
nodes.forEach(function (node) {
|
nodes.forEach(function (node) {
|
||||||
@@ -193,75 +182,87 @@ define(
|
|||||||
indexItems(objects);
|
indexItems(objects);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// For documentation, see query below
|
|
||||||
function query(input, timestamp, maxResults, timeout) {
|
|
||||||
var terms = [],
|
|
||||||
searchResults = [],
|
|
||||||
defer = $q.defer();
|
|
||||||
|
|
||||||
// If the input is nonempty, do a search
|
|
||||||
if (input !== '' && input !== undefined) {
|
|
||||||
|
|
||||||
// Allow us to access this promise later to resolve it later
|
|
||||||
pendingQueries[timestamp] = defer;
|
|
||||||
|
|
||||||
// Check to see if the user provided a maximum
|
|
||||||
// number of results to display
|
|
||||||
if (!maxResults) {
|
|
||||||
// Else, we provide a default value
|
|
||||||
maxResults = DEFAULT_MAX_RESULTS;
|
|
||||||
}
|
|
||||||
// Similarly, check if timeout was provided
|
|
||||||
if (!timeout) {
|
|
||||||
timeout = DEFAULT_TIMEOUT;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the query to the worker
|
worker.onmessage = handleResponse;
|
||||||
workerSearch(input, maxResults, timestamp, timeout);
|
|
||||||
|
|
||||||
return defer.promise;
|
|
||||||
} else {
|
|
||||||
// Otherwise return an empty result
|
|
||||||
return {hits: [], total: 0};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Index the tree's contents once at the beginning
|
// Index the tree's contents once at the beginning
|
||||||
getItems();
|
getItems();
|
||||||
|
|
||||||
return {
|
|
||||||
/**
|
|
||||||
* Searches through the filetree for domain objects which match
|
|
||||||
* the search term. This function is to be used as a fallback
|
|
||||||
* in the case where other search services are not avaliable.
|
|
||||||
* Returns a promise for a result object that has the format
|
|
||||||
* {hits: searchResult[], total: number, timedOut: boolean}
|
|
||||||
* where a searchResult has the format
|
|
||||||
* {id: string, object: domainObject, score: number}
|
|
||||||
*
|
|
||||||
* Notes:
|
|
||||||
* * The order of the results is not guarenteed.
|
|
||||||
* * A domain object qualifies as a match for a search input if
|
|
||||||
* the object's name property contains any of the search terms
|
|
||||||
* (which are generated by splitting the input at spaces).
|
|
||||||
* * Scores are higher for matches that have more of the terms
|
|
||||||
* as substrings.
|
|
||||||
*
|
|
||||||
* @param input The text input that is the query.
|
|
||||||
* @param timestamp The time at which this function was called.
|
|
||||||
* This timestamp is used as a unique identifier for this
|
|
||||||
* query and the corresponding results.
|
|
||||||
* @param maxResults (optional) The maximum number of results
|
|
||||||
* that this function should return.
|
|
||||||
* @param timeout (optional) The time after which the search should
|
|
||||||
* stop calculations and return partial results.
|
|
||||||
*/
|
|
||||||
query: query
|
|
||||||
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Searches through the filetree for domain objects which match
|
||||||
|
* the search term. This function is to be used as a fallback
|
||||||
|
* in the case where other search services are not avaliable.
|
||||||
|
* Returns a promise for a result object that has the format
|
||||||
|
* {hits: searchResult[], total: number, timedOut: boolean}
|
||||||
|
* where a searchResult has the format
|
||||||
|
* {id: string, object: domainObject, score: number}
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* * The order of the results is not guarenteed.
|
||||||
|
* * A domain object qualifies as a match for a search input if
|
||||||
|
* the object's name property contains any of the search terms
|
||||||
|
* (which are generated by splitting the input at spaces).
|
||||||
|
* * Scores are higher for matches that have more of the terms
|
||||||
|
* as substrings.
|
||||||
|
*
|
||||||
|
* @param input The text input that is the query.
|
||||||
|
* @param timestamp The time at which this function was called.
|
||||||
|
* This timestamp is used as a unique identifier for this
|
||||||
|
* query and the corresponding results.
|
||||||
|
* @param maxResults (optional) The maximum number of results
|
||||||
|
* that this function should return.
|
||||||
|
* @param timeout (optional) The time after which the search should
|
||||||
|
* stop calculations and return partial results.
|
||||||
|
*/
|
||||||
|
GenericSearchProvider.prototype.query = function query(input, timestamp, maxResults, timeout) {
|
||||||
|
var terms = [],
|
||||||
|
searchResults = [],
|
||||||
|
pendingQueries = this.pendingQueries,
|
||||||
|
worker = this.worker,
|
||||||
|
defer = this.$q.defer();
|
||||||
|
|
||||||
|
// Tell the worker to search for items it has that match this searchInput.
|
||||||
|
// Takes the searchInput, as well as a max number of results (will return
|
||||||
|
// less than that if there are fewer matches).
|
||||||
|
function workerSearch(searchInput, maxResults, timestamp, timeout) {
|
||||||
|
var message = {
|
||||||
|
request: 'search',
|
||||||
|
input: searchInput,
|
||||||
|
maxNumber: maxResults,
|
||||||
|
timestamp: timestamp,
|
||||||
|
timeout: timeout
|
||||||
|
};
|
||||||
|
worker.postMessage(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the input is nonempty, do a search
|
||||||
|
if (input !== '' && input !== undefined) {
|
||||||
|
|
||||||
|
// Allow us to access this promise later to resolve it later
|
||||||
|
pendingQueries[timestamp] = defer;
|
||||||
|
|
||||||
|
// Check to see if the user provided a maximum
|
||||||
|
// number of results to display
|
||||||
|
if (!maxResults) {
|
||||||
|
// Else, we provide a default value
|
||||||
|
maxResults = DEFAULT_MAX_RESULTS;
|
||||||
|
}
|
||||||
|
// Similarly, check if timeout was provided
|
||||||
|
if (!timeout) {
|
||||||
|
timeout = DEFAULT_TIMEOUT;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Send the query to the worker
|
||||||
|
workerSearch(input, maxResults, timestamp, timeout);
|
||||||
|
|
||||||
|
return defer.promise;
|
||||||
|
} else {
|
||||||
|
// Otherwise return an empty result
|
||||||
|
return { hits: [], total: 0 };
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return GenericSearchProvider;
|
return GenericSearchProvider;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,33 +42,55 @@ define(
|
|||||||
* aggregated.
|
* aggregated.
|
||||||
*/
|
*/
|
||||||
function SearchAggregator($q, providers) {
|
function SearchAggregator($q, providers) {
|
||||||
|
this.$q = $q;
|
||||||
// Remove duplicate objects that have the same ID. Modifies the passed
|
this.providers = providers;
|
||||||
// array, and returns the number that were removed.
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a query to each of the providers. Returns a promise for
|
||||||
|
* a result object that has the format
|
||||||
|
* {hits: searchResult[], total: number, timedOut: boolean}
|
||||||
|
* where a searchResult has the format
|
||||||
|
* {id: string, object: domainObject, score: number}
|
||||||
|
*
|
||||||
|
* @param inputText The text input that is the query.
|
||||||
|
* @param maxResults (optional) The maximum number of results
|
||||||
|
* that this function should return. If not provided, a
|
||||||
|
* default of 100 will be used.
|
||||||
|
*/
|
||||||
|
SearchAggregator.prototype.query = function queryAll(inputText, maxResults) {
|
||||||
|
var $q = this.$q,
|
||||||
|
providers = this.providers,
|
||||||
|
i,
|
||||||
|
timestamp = Date.now(),
|
||||||
|
resultPromises = [];
|
||||||
|
|
||||||
|
// Remove duplicate objects that have the same ID. Modifies the passed
|
||||||
|
// array, and returns the number that were removed.
|
||||||
function filterDuplicates(results, total) {
|
function filterDuplicates(results, total) {
|
||||||
var ids = {},
|
var ids = {},
|
||||||
numRemoved = 0,
|
numRemoved = 0,
|
||||||
i;
|
i;
|
||||||
|
|
||||||
for (i = 0; i < results.length; i += 1) {
|
for (i = 0; i < results.length; i += 1) {
|
||||||
if (ids[results[i].id]) {
|
if (ids[results[i].id]) {
|
||||||
// If this result's ID is already there, remove the object
|
// If this result's ID is already there, remove the object
|
||||||
results.splice(i, 1);
|
results.splice(i, 1);
|
||||||
numRemoved += 1;
|
numRemoved += 1;
|
||||||
|
|
||||||
// Reduce loop index because we shortened the array
|
// Reduce loop index because we shortened the array
|
||||||
i -= 1;
|
i -= 1;
|
||||||
} else {
|
} else {
|
||||||
// Otherwise add the ID to the list of the ones we have seen
|
// Otherwise add the ID to the list of the ones we have seen
|
||||||
ids[results[i].id] = true;
|
ids[results[i].id] = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return numRemoved;
|
return numRemoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Order the objects from highest to lowest score in the array.
|
// Order the objects from highest to lowest score in the array.
|
||||||
// Modifies the passed array, as well as returns the modified array.
|
// Modifies the passed array, as well as returns the modified array.
|
||||||
function orderByScore(results) {
|
function orderByScore(results) {
|
||||||
results.sort(function (a, b) {
|
results.sort(function (a, b) {
|
||||||
if (a.score > b.score) {
|
if (a.score > b.score) {
|
||||||
@@ -81,65 +103,42 @@ define(
|
|||||||
});
|
});
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
// For documentation, see query below.
|
if (!maxResults) {
|
||||||
function queryAll(inputText, maxResults) {
|
maxResults = DEFAULT_MAX_RESULTS;
|
||||||
var i,
|
|
||||||
timestamp = Date.now(),
|
|
||||||
resultPromises = [];
|
|
||||||
|
|
||||||
if (!maxResults) {
|
|
||||||
maxResults = DEFAULT_MAX_RESULTS;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Send the query to all the providers
|
|
||||||
for (i = 0; i < providers.length; i += 1) {
|
|
||||||
resultPromises.push(
|
|
||||||
providers[i].query(inputText, timestamp, maxResults, DEFUALT_TIMEOUT)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Get promises for results arrays
|
|
||||||
return $q.all(resultPromises).then(function (resultObjects) {
|
|
||||||
var results = [],
|
|
||||||
totalSum = 0,
|
|
||||||
i;
|
|
||||||
|
|
||||||
// Merge results
|
|
||||||
for (i = 0; i < resultObjects.length; i += 1) {
|
|
||||||
results = results.concat(resultObjects[i].hits);
|
|
||||||
totalSum += resultObjects[i].total;
|
|
||||||
}
|
|
||||||
// Order by score first, so that when removing repeats we keep the higher scored ones
|
|
||||||
orderByScore(results);
|
|
||||||
totalSum -= filterDuplicates(results, totalSum);
|
|
||||||
|
|
||||||
return {
|
|
||||||
hits: results,
|
|
||||||
total: totalSum,
|
|
||||||
timedOut: resultObjects.some(function (obj) {
|
|
||||||
return obj.timedOut;
|
|
||||||
})
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return {
|
// Send the query to all the providers
|
||||||
/**
|
for (i = 0; i < providers.length; i += 1) {
|
||||||
* Sends a query to each of the providers. Returns a promise for
|
resultPromises.push(
|
||||||
* a result object that has the format
|
providers[i].query(inputText, timestamp, maxResults, DEFUALT_TIMEOUT)
|
||||||
* {hits: searchResult[], total: number, timedOut: boolean}
|
);
|
||||||
* where a searchResult has the format
|
}
|
||||||
* {id: string, object: domainObject, score: number}
|
|
||||||
*
|
// Get promises for results arrays
|
||||||
* @param inputText The text input that is the query.
|
return $q.all(resultPromises).then(function (resultObjects) {
|
||||||
* @param maxResults (optional) The maximum number of results
|
var results = [],
|
||||||
* that this function should return. If not provided, a
|
totalSum = 0,
|
||||||
* default of 100 will be used.
|
i;
|
||||||
*/
|
|
||||||
query: queryAll
|
// Merge results
|
||||||
};
|
for (i = 0; i < resultObjects.length; i += 1) {
|
||||||
}
|
results = results.concat(resultObjects[i].hits);
|
||||||
|
totalSum += resultObjects[i].total;
|
||||||
|
}
|
||||||
|
// Order by score first, so that when removing repeats we keep the higher scored ones
|
||||||
|
orderByScore(results);
|
||||||
|
totalSum -= filterDuplicates(results, totalSum);
|
||||||
|
|
||||||
|
return {
|
||||||
|
hits: results,
|
||||||
|
total: totalSum,
|
||||||
|
timedOut: resultObjects.some(function (obj) {
|
||||||
|
return obj.timedOut;
|
||||||
|
})
|
||||||
|
};
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return SearchAggregator;
|
return SearchAggregator;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user