Merge branch 'master' of https://github.com/nasa/openmctweb into search
Conflicts: platform/commonUI/general/src/controllers/TreeNodeController.js platform/persistence/elastic/src/ElasticSearchProvider.js
This commit is contained in:
@@ -48,10 +48,14 @@ define(
|
||||
* domain objects' IDs.
|
||||
*/
|
||||
function GenericSearchProvider($q, $timeout, objectService, workerService, ROOTS) {
|
||||
var worker = workerService.run('genericSearchWorker'),
|
||||
indexed = {},
|
||||
pendingQueries = {};
|
||||
// pendingQueries is a dictionary with the key value pairs st
|
||||
var indexed = {},
|
||||
pendingQueries = {},
|
||||
worker = workerService.run('genericSearchWorker');
|
||||
|
||||
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
|
||||
|
||||
// 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
|
||||
// a search request.
|
||||
function handleResponse(event) {
|
||||
@@ -120,8 +111,6 @@ define(
|
||||
}
|
||||
}
|
||||
|
||||
worker.onmessage = handleResponse;
|
||||
|
||||
// Helper function for getItems(). Indexes the tree.
|
||||
function indexItems(nodes) {
|
||||
nodes.forEach(function (node) {
|
||||
@@ -193,75 +182,87 @@ define(
|
||||
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
|
||||
workerSearch(input, maxResults, timestamp, timeout);
|
||||
worker.onmessage = handleResponse;
|
||||
|
||||
return defer.promise;
|
||||
} else {
|
||||
// Otherwise return an empty result
|
||||
return {hits: [], total: 0};
|
||||
}
|
||||
}
|
||||
|
||||
// Index the tree's contents once at the beginning
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -42,33 +42,55 @@ define(
|
||||
* aggregated.
|
||||
*/
|
||||
function SearchAggregator($q, providers) {
|
||||
|
||||
// Remove duplicate objects that have the same ID. Modifies the passed
|
||||
// array, and returns the number that were removed.
|
||||
this.$q = $q;
|
||||
this.providers = providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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) {
|
||||
var ids = {},
|
||||
numRemoved = 0,
|
||||
i;
|
||||
|
||||
|
||||
for (i = 0; i < results.length; i += 1) {
|
||||
if (ids[results[i].id]) {
|
||||
// If this result's ID is already there, remove the object
|
||||
results.splice(i, 1);
|
||||
numRemoved += 1;
|
||||
|
||||
// Reduce loop index because we shortened the array
|
||||
|
||||
// Reduce loop index because we shortened the array
|
||||
i -= 1;
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return numRemoved;
|
||||
}
|
||||
|
||||
|
||||
// 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) {
|
||||
results.sort(function (a, b) {
|
||||
if (a.score > b.score) {
|
||||
@@ -81,65 +103,42 @@ define(
|
||||
});
|
||||
return results;
|
||||
}
|
||||
|
||||
// For documentation, see query below.
|
||||
function queryAll(inputText, maxResults) {
|
||||
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;
|
||||
})
|
||||
};
|
||||
});
|
||||
|
||||
if (!maxResults) {
|
||||
maxResults = DEFAULT_MAX_RESULTS;
|
||||
}
|
||||
|
||||
return {
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
query: queryAll
|
||||
};
|
||||
}
|
||||
|
||||
// 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 SearchAggregator;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user