mirror of
https://github.com/nasa/openmct.git
synced 2025-01-12 16:02:51 +00:00
c81e2dbb4a
Conflicts: platform/commonUI/general/src/controllers/TreeNodeController.js platform/persistence/elastic/src/ElasticSearchProvider.js
213 lines
8.5 KiB
JavaScript
213 lines
8.5 KiB
JavaScript
/*****************************************************************************
|
|
* Open MCT Web, Copyright (c) 2014-2015, United States Government
|
|
* as represented by the Administrator of the National Aeronautics and Space
|
|
* Administration. All rights reserved.
|
|
*
|
|
* Open MCT Web is licensed under the Apache License, Version 2.0 (the
|
|
* "License"); you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
* http://www.apache.org/licenses/LICENSE-2.0.
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
* License for the specific language governing permissions and limitations
|
|
* under the License.
|
|
*
|
|
* Open MCT Web includes source code licensed under additional open source
|
|
* licenses. See the Open Source Licenses file (LICENSES.md) included with
|
|
* this source code distribution or the Licensing information page available
|
|
* at runtime from the About dialog for additional information.
|
|
*****************************************************************************/
|
|
/*global define*/
|
|
|
|
/**
|
|
* Module defining ElasticSearchProvider. Created by shale on 07/16/2015.
|
|
*/
|
|
define(
|
|
[],
|
|
function () {
|
|
"use strict";
|
|
|
|
// JSLint doesn't like underscore-prefixed properties,
|
|
// so hide them here.
|
|
var ID = "_id",
|
|
SCORE = "_score",
|
|
DEFAULT_MAX_RESULTS = 100;
|
|
|
|
/**
|
|
* A search service which searches through domain objects in
|
|
* the filetree using ElasticSearch.
|
|
*
|
|
* @constructor
|
|
* @param $http Angular's $http service, for working with urls.
|
|
* @param {ObjectService} objectService the service from which
|
|
* domain objects can be gotten.
|
|
* @param ROOT the constant `ELASTIC_ROOT` which allows us to
|
|
* interact with ElasticSearch.
|
|
*/
|
|
function ElasticSearchProvider($http, objectService, ROOT) {
|
|
this.$http = $http;
|
|
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.
|
|
*/
|
|
ElasticSearchProvider.prototype.query = function query(searchTerm, timestamp, maxResults, timeout) {
|
|
var $http = this.$http,
|
|
objectService = this.objectService,
|
|
root = this.root,
|
|
esQuery;
|
|
|
|
function addFuzziness(searchTerm, editDistance) {
|
|
if (!editDistance) {
|
|
editDistance = '';
|
|
}
|
|
|
|
return searchTerm.split(' ').map(function (s) {
|
|
// Don't add fuzziness for quoted strings
|
|
if (s.indexOf('"') !== -1) {
|
|
return s;
|
|
} else {
|
|
return s + '~' + editDistance;
|
|
}
|
|
}).join(' ');
|
|
}
|
|
|
|
// Currently specific to elasticsearch
|
|
function processSearchTerm(searchTerm) {
|
|
var spaceIndex;
|
|
|
|
// Cut out any extra spaces
|
|
while (searchTerm.substr(0, 1) === ' ') {
|
|
searchTerm = searchTerm.substring(1, searchTerm.length);
|
|
}
|
|
while (searchTerm.substr(searchTerm.length - 1, 1) === ' ') {
|
|
searchTerm = searchTerm.substring(0, searchTerm.length - 1);
|
|
}
|
|
spaceIndex = searchTerm.indexOf(' ');
|
|
while (spaceIndex !== -1) {
|
|
searchTerm = searchTerm.substring(0, spaceIndex) +
|
|
searchTerm.substring(spaceIndex + 1, searchTerm.length);
|
|
spaceIndex = searchTerm.indexOf(' ');
|
|
}
|
|
|
|
// Add fuzziness for completeness
|
|
searchTerm = addFuzziness(searchTerm);
|
|
|
|
return searchTerm;
|
|
}
|
|
|
|
// Processes results from the format that elasticsearch returns to
|
|
// a list of searchResult objects, then returns a result object
|
|
// (See documentation for query for object descriptions)
|
|
function processResults(rawResults, timestamp) {
|
|
var results = rawResults.data.hits.hits,
|
|
resultsLength = results.length,
|
|
ids = [],
|
|
scores = {},
|
|
searchResults = [],
|
|
i;
|
|
|
|
// Get the result objects' IDs
|
|
for (i = 0; i < resultsLength; i += 1) {
|
|
ids.push(results[i][ID]);
|
|
}
|
|
|
|
// Get the result objects' scores
|
|
for (i = 0; i < resultsLength; i += 1) {
|
|
scores[ids[i]] = results[i][SCORE];
|
|
}
|
|
|
|
// Get the domain objects from their IDs
|
|
return objectService.getObjects(ids).then(function (objects) {
|
|
var j,
|
|
id;
|
|
|
|
for (j = 0; j < resultsLength; j += 1) {
|
|
id = ids[j];
|
|
|
|
// Include items we can get models for
|
|
if (objects[id].getModel) {
|
|
// Format the results as searchResult objects
|
|
searchResults.push({
|
|
id: id,
|
|
object: objects[id],
|
|
score: scores[id]
|
|
});
|
|
}
|
|
}
|
|
|
|
return {
|
|
hits: searchResults,
|
|
total: rawResults.data.hits.total,
|
|
timedOut: rawResults.data.timed_out
|
|
};
|
|
});
|
|
}
|
|
|
|
|
|
// 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...
|
|
return this.$http({
|
|
method: "GET",
|
|
url: esQuery
|
|
}).then(function (rawResults) {
|
|
// ...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 ElasticSearchProvider;
|
|
}
|
|
); |