mirror of
https://github.com/nasa/openmct.git
synced 2025-06-24 02:04:02 +00:00
[Search] Moving files
Moved the elasticsearch provider to platform/persistence/elastic. Then moved the generic provider out of its folder.
This commit is contained in:
@ -8,6 +8,12 @@
|
||||
"type": "provider",
|
||||
"implementation": "ElasticPersistenceProvider.js",
|
||||
"depends": [ "$http", "$q", "PERSISTENCE_SPACE", "ELASTIC_ROOT", "ELASTIC_PATH" ]
|
||||
},
|
||||
{
|
||||
"provides": "searchService",
|
||||
"type": "provider",
|
||||
"implementation": "ElasticsearchSearchProvider.js",
|
||||
"depends": [ "$http", "objectService", "ELASTIC_ROOT" ]
|
||||
}
|
||||
],
|
||||
"constants": [
|
||||
|
212
platform/persistence/elastic/src/ElasticsearchSearchProvider.js
Normal file
212
platform/persistence/elastic/src/ElasticsearchSearchProvider.js
Normal file
@ -0,0 +1,212 @@
|
||||
/*****************************************************************************
|
||||
* 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 ElasticsearchSearchProvider. 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 ElasticsearchSearchProvider($http, objectService, ROOT) {
|
||||
|
||||
// Add the fuzziness operator to the search term
|
||||
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
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
// 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 !== '') {
|
||||
// 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 $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 {
|
||||
/**
|
||||
* 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: domainObject ID, 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.
|
||||
*/
|
||||
query: query
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
return ElasticsearchSearchProvider;
|
||||
}
|
||||
);
|
@ -0,0 +1,115 @@
|
||||
/*****************************************************************************
|
||||
* 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,describe,it,expect,beforeEach,jasmine*/
|
||||
|
||||
/**
|
||||
* SearchSpec. Created by shale on 07/31/2015.
|
||||
*/
|
||||
define(
|
||||
["../../src/providers/ElasticsearchSearchProvider"],
|
||||
function (ElasticsearchSearchProvider) {
|
||||
"use strict";
|
||||
|
||||
// JSLint doesn't like underscore-prefixed properties,
|
||||
// so hide them here.
|
||||
var ID = "_id",
|
||||
SCORE = "_score";
|
||||
|
||||
describe("The ElasticSearch search provider ", function () {
|
||||
var mockHttp,
|
||||
mockHttpPromise,
|
||||
mockObjectPromise,
|
||||
mockObjectService,
|
||||
mockDomainObject,
|
||||
provider,
|
||||
mockProviderResults;
|
||||
|
||||
beforeEach(function () {
|
||||
mockHttp = jasmine.createSpy("$http");
|
||||
mockHttpPromise = jasmine.createSpyObj(
|
||||
"promise",
|
||||
[ "then" ]
|
||||
);
|
||||
mockHttp.andReturn(mockHttpPromise);
|
||||
// allow chaining of promise.then().catch();
|
||||
mockHttpPromise.then.andReturn(mockHttpPromise);
|
||||
|
||||
mockObjectService = jasmine.createSpyObj(
|
||||
"objectService",
|
||||
[ "getObjects" ]
|
||||
);
|
||||
mockObjectPromise = jasmine.createSpyObj(
|
||||
"promise",
|
||||
[ "then" ]
|
||||
);
|
||||
mockObjectService.getObjects.andReturn(mockObjectPromise);
|
||||
|
||||
mockDomainObject = jasmine.createSpyObj(
|
||||
"domainObject",
|
||||
[ "getId", "getModel" ]
|
||||
);
|
||||
|
||||
provider = new ElasticsearchSearchProvider(mockHttp, mockObjectService, "");
|
||||
provider.query(' test "query" ', 0, undefined, 1000);
|
||||
});
|
||||
|
||||
it("sends a query to ElasticSearch", function () {
|
||||
expect(mockHttp).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("gets data from ElasticSearch", function () {
|
||||
var data = {
|
||||
hits: {
|
||||
hits: [
|
||||
{},
|
||||
{}
|
||||
],
|
||||
total: 0
|
||||
},
|
||||
timed_out: false
|
||||
};
|
||||
data.hits.hits[0][ID] = 1;
|
||||
data.hits.hits[0][SCORE] = 1;
|
||||
data.hits.hits[1][ID] = 2;
|
||||
data.hits.hits[1][SCORE] = 2;
|
||||
|
||||
mockProviderResults = mockHttpPromise.then.mostRecentCall.args[0]({data: data});
|
||||
|
||||
expect(
|
||||
mockObjectPromise.then.mostRecentCall.args[0]({
|
||||
1: mockDomainObject,
|
||||
2: mockDomainObject
|
||||
}).hits.length
|
||||
).toEqual(2);
|
||||
});
|
||||
|
||||
it("returns nothing for an empty string query", function () {
|
||||
expect(provider.query("").hits).toEqual([]);
|
||||
});
|
||||
|
||||
it("returns something when there is an ElasticSearch error", function () {
|
||||
mockProviderResults = mockHttpPromise.then.mostRecentCall.args[1]();
|
||||
expect(mockProviderResults).toBeDefined();
|
||||
});
|
||||
});
|
||||
}
|
||||
);
|
@ -1,4 +1,5 @@
|
||||
[
|
||||
"ElasticIndicator",
|
||||
"ElasticPersistenceProvider"
|
||||
"ElasticPersistenceProvider",
|
||||
"ElasticsearchSearchProvider"
|
||||
]
|
||||
|
Reference in New Issue
Block a user