* update generic search compostion load to new version for testing * logging * removing logging adding check for undefined child * reverting genericsearchprovider * testing using object service instead of modelservice for search * modified the animations for sliding children in and out to be more reliable, pr updates * removing unneccessary code
322 lines
9.6 KiB
JavaScript
322 lines
9.6 KiB
JavaScript
/*****************************************************************************
|
|
* Open MCT, Copyright (c) 2014-2020, United States Government
|
|
* as represented by the Administrator of the National Aeronautics and Space
|
|
* Administration. All rights reserved.
|
|
*
|
|
* Open MCT 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 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.
|
|
*****************************************************************************/
|
|
|
|
/**
|
|
* Module defining GenericSearchProvider. Created by shale on 07/16/2015.
|
|
*/
|
|
define([
|
|
'objectUtils',
|
|
'lodash'
|
|
], function (
|
|
objectUtils,
|
|
_
|
|
) {
|
|
|
|
/**
|
|
* A search service which searches through domain objects in
|
|
* the filetree without using external search implementations.
|
|
*
|
|
* @constructor
|
|
* @param $q Angular's $q, for promise consolidation.
|
|
* @param $log Anglar's $log, for logging.
|
|
* @param {ObjectService} objectService the object service.
|
|
* @param {WorkerService} workerService the workerService.
|
|
* @param {TopicService} topic the topic service.
|
|
* @param {Array} ROOTS An array of object Ids to begin indexing.
|
|
*/
|
|
function GenericSearchProvider($q, $log, objectService, workerService, topic, ROOTS, USE_LEGACY_INDEXER, openmct) {
|
|
var provider = this;
|
|
this.$q = $q;
|
|
this.$log = $log;
|
|
this.objectService = objectService;
|
|
this.openmct = openmct;
|
|
|
|
this.indexedIds = {};
|
|
this.idsToIndex = [];
|
|
this.pendingIndex = {};
|
|
this.pendingRequests = 0;
|
|
|
|
this.pendingQueries = {};
|
|
|
|
this.USE_LEGACY_INDEXER = USE_LEGACY_INDEXER;
|
|
|
|
this.worker = this.startWorker(workerService);
|
|
this.indexOnMutation(topic);
|
|
|
|
ROOTS.forEach(function indexRoot(rootId) {
|
|
provider.scheduleForIndexing(rootId);
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
* Maximum number of concurrent index requests to allow.
|
|
*/
|
|
GenericSearchProvider.prototype.MAX_CONCURRENT_REQUESTS = 100;
|
|
|
|
/**
|
|
* Query the search provider for results.
|
|
*
|
|
* @param {String} input the string to search by.
|
|
* @param {Number} maxResults max number of results to return.
|
|
* @returns {Promise} a promise for a modelResults object.
|
|
*/
|
|
GenericSearchProvider.prototype.query = function (
|
|
input,
|
|
maxResults
|
|
) {
|
|
|
|
var queryId = this.dispatchSearch(input, maxResults),
|
|
pendingQuery = this.$q.defer();
|
|
|
|
this.pendingQueries[queryId] = pendingQuery;
|
|
|
|
return pendingQuery.promise;
|
|
};
|
|
|
|
/**
|
|
* Creates a search worker and attaches handlers.
|
|
*
|
|
* @private
|
|
* @param workerService
|
|
* @returns worker the created search worker.
|
|
*/
|
|
GenericSearchProvider.prototype.startWorker = function (workerService) {
|
|
var provider = this,
|
|
worker;
|
|
|
|
if (this.USE_LEGACY_INDEXER) {
|
|
worker = workerService.run('genericSearchWorker');
|
|
} else {
|
|
worker = workerService.run('bareBonesSearchWorker');
|
|
}
|
|
|
|
worker.addEventListener('message', function (messageEvent) {
|
|
provider.onWorkerMessage(messageEvent);
|
|
});
|
|
|
|
return worker;
|
|
};
|
|
|
|
/**
|
|
* Listen to the mutation topic and re-index objects when they are
|
|
* mutated.
|
|
*
|
|
* @private
|
|
* @param topic the topicService.
|
|
*/
|
|
GenericSearchProvider.prototype.indexOnMutation = function (topic) {
|
|
var mutationTopic = topic('mutation'),
|
|
provider = this;
|
|
|
|
mutationTopic.listen(function (mutatedObject) {
|
|
var editor = mutatedObject.getCapability('editor');
|
|
if (!editor || !editor.inEditContext()) {
|
|
provider.index(
|
|
mutatedObject.getId(),
|
|
mutatedObject.getModel()
|
|
);
|
|
}
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Schedule an id to be indexed at a later date. If there are less
|
|
* pending requests then allowed, will kick off an indexing request.
|
|
*
|
|
* @private
|
|
* @param {String} id to be indexed.
|
|
*/
|
|
GenericSearchProvider.prototype.scheduleForIndexing = function (id) {
|
|
if (!this.indexedIds[id] && !this.pendingIndex[id]) {
|
|
this.indexedIds[id] = true;
|
|
this.pendingIndex[id] = true;
|
|
this.idsToIndex.push(id);
|
|
}
|
|
|
|
this.keepIndexing();
|
|
};
|
|
|
|
/**
|
|
* If there are less pending requests than concurrent requests, keep
|
|
* firing requests.
|
|
*
|
|
* @private
|
|
*/
|
|
GenericSearchProvider.prototype.keepIndexing = function () {
|
|
while (this.pendingRequests < this.MAX_CONCURRENT_REQUESTS
|
|
&& this.idsToIndex.length
|
|
) {
|
|
this.beginIndexRequest();
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Pass an id and model to the worker to be indexed. If the model has
|
|
* composition, schedule those ids for later indexing.
|
|
*
|
|
* @private
|
|
* @param id a model id
|
|
* @param model a model
|
|
*/
|
|
GenericSearchProvider.prototype.index = function (id, model) {
|
|
var provider = this;
|
|
|
|
if (id !== 'ROOT') {
|
|
this.worker.postMessage({
|
|
request: 'index',
|
|
model: model,
|
|
id: id
|
|
});
|
|
}
|
|
|
|
var domainObject = objectUtils.toNewFormat(model, id);
|
|
var composition = this.openmct.composition.registry.find(p => {
|
|
return p.appliesTo(domainObject);
|
|
});
|
|
|
|
if (!composition) {
|
|
return;
|
|
}
|
|
|
|
composition.load(domainObject)
|
|
.then(function (children) {
|
|
children.forEach(function (child) {
|
|
provider.scheduleForIndexing(objectUtils.makeKeyString(child));
|
|
});
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Pulls an id from the indexing queue, loads it from the model service,
|
|
* and indexes it. Upon completion, tells the provider to keep
|
|
* indexing.
|
|
*
|
|
* @private
|
|
*/
|
|
GenericSearchProvider.prototype.beginIndexRequest = function () {
|
|
var idToIndex = this.idsToIndex.shift(),
|
|
provider = this;
|
|
|
|
this.pendingRequests += 1;
|
|
this.objectService
|
|
.getObjects([idToIndex])
|
|
.then(function (objects) {
|
|
delete provider.pendingIndex[idToIndex];
|
|
if (objects[idToIndex]) {
|
|
provider.index(idToIndex, objects[idToIndex].model);
|
|
}
|
|
}, function () {
|
|
provider
|
|
.$log
|
|
.warn('Failed to index domain object ' + idToIndex);
|
|
})
|
|
.then(function () {
|
|
setTimeout(function () {
|
|
provider.pendingRequests -= 1;
|
|
provider.keepIndexing();
|
|
}, 0);
|
|
});
|
|
};
|
|
|
|
/**
|
|
* Handle messages from the worker. Only really knows how to handle search
|
|
* results, which are parsed, transformed into a modelResult object, which
|
|
* is used to resolve the corresponding promise.
|
|
* @private
|
|
*/
|
|
GenericSearchProvider.prototype.onWorkerMessage = function (event) {
|
|
if (event.data.request !== 'search') {
|
|
return;
|
|
}
|
|
|
|
var pendingQuery,
|
|
modelResults;
|
|
|
|
if (this.USE_LEGACY_INDEXER) {
|
|
pendingQuery = this.pendingQueries[event.data.queryId];
|
|
modelResults = {
|
|
total: event.data.total
|
|
};
|
|
|
|
modelResults.hits = event.data.results.map(function (hit) {
|
|
return {
|
|
id: hit.item.id,
|
|
model: hit.item.model,
|
|
score: hit.matchCount
|
|
};
|
|
});
|
|
} else {
|
|
pendingQuery = this.pendingQueries[event.data.queryId];
|
|
modelResults = {
|
|
total: event.data.total
|
|
};
|
|
|
|
modelResults.hits = event.data.results.map(function (hit) {
|
|
return {
|
|
id: hit.id
|
|
};
|
|
});
|
|
}
|
|
|
|
pendingQuery.resolve(modelResults);
|
|
delete this.pendingQueries[event.data.queryId];
|
|
};
|
|
|
|
/**
|
|
* @private
|
|
* @returns {Number} a unique, unused query Id.
|
|
*/
|
|
GenericSearchProvider.prototype.makeQueryId = function () {
|
|
var queryId = Math.ceil(Math.random() * 100000);
|
|
while (this.pendingQueries[queryId]) {
|
|
queryId = Math.ceil(Math.random() * 100000);
|
|
}
|
|
|
|
return queryId;
|
|
};
|
|
|
|
/**
|
|
* Dispatch a search query to the worker and return a queryId.
|
|
*
|
|
* @private
|
|
* @returns {Number} a unique query Id for the query.
|
|
*/
|
|
GenericSearchProvider.prototype.dispatchSearch = function (
|
|
searchInput,
|
|
maxResults
|
|
) {
|
|
var queryId = this.makeQueryId();
|
|
|
|
this.worker.postMessage({
|
|
request: 'search',
|
|
input: searchInput,
|
|
maxResults: maxResults,
|
|
queryId: queryId
|
|
});
|
|
|
|
return queryId;
|
|
};
|
|
|
|
return GenericSearchProvider;
|
|
});
|