mirror of
https://github.com/nasa/openmct.git
synced 2025-04-09 04:14:32 +00:00
Search fix identifier (#4947)
* use identifier not key for object get calls * re-index on composition or name changes only * search should account for namespaces Co-authored-by: Scott Bell <scott@traclabs.com> Co-authored-by: Nikhil <nikhil.k.mandlik@nasa.gov>
This commit is contained in:
parent
a53a3a0297
commit
cf6bc5be2d
@ -36,13 +36,14 @@ class InMemorySearchProvider {
|
||||
*/
|
||||
this.MAX_CONCURRENT_REQUESTS = 100;
|
||||
/**
|
||||
* If max results is not specified in query, use this as default.
|
||||
*/
|
||||
* If max results is not specified in query, use this as default.
|
||||
*/
|
||||
this.DEFAULT_MAX_RESULTS = 100;
|
||||
|
||||
this.openmct = openmct;
|
||||
|
||||
this.indexedIds = {};
|
||||
this.indexedCompositions = {};
|
||||
this.idsToIndex = [];
|
||||
this.pendingIndex = {};
|
||||
this.pendingRequests = 0;
|
||||
@ -58,7 +59,6 @@ class InMemorySearchProvider {
|
||||
this.onWorkerMessageError = this.onWorkerMessageError.bind(this);
|
||||
this.onerror = this.onWorkerError.bind(this);
|
||||
this.startIndexing = this.startIndexing.bind(this);
|
||||
this.onMutationOfIndexedObject = this.onMutationOfIndexedObject.bind(this);
|
||||
|
||||
this.openmct.on('start', this.startIndexing);
|
||||
this.openmct.on('destroy', () => {
|
||||
@ -68,6 +68,9 @@ class InMemorySearchProvider {
|
||||
this.worker.port.onmessageerror = null;
|
||||
this.worker.port.close();
|
||||
}
|
||||
|
||||
this.destroyObservers(this.indexedIds);
|
||||
this.destroyObservers(this.indexedCompositions);
|
||||
});
|
||||
}
|
||||
|
||||
@ -137,7 +140,7 @@ class InMemorySearchProvider {
|
||||
};
|
||||
modelResults.hits = await Promise.all(event.data.results.map(async (hit) => {
|
||||
const identifier = this.openmct.objects.parseKeyString(hit.keyString);
|
||||
const domainObject = await this.openmct.objects.get(identifier.key);
|
||||
const domainObject = await this.openmct.objects.get(identifier);
|
||||
|
||||
return domainObject;
|
||||
}));
|
||||
@ -213,29 +216,52 @@ class InMemorySearchProvider {
|
||||
}
|
||||
}
|
||||
|
||||
onMutationOfIndexedObject(domainObject) {
|
||||
onNameMutation(domainObject, name) {
|
||||
const provider = this;
|
||||
provider.index(domainObject.identifier, domainObject);
|
||||
|
||||
domainObject.name = name;
|
||||
provider.index(domainObject);
|
||||
}
|
||||
|
||||
onCompositionMutation(domainObject, composition) {
|
||||
const provider = this;
|
||||
const indexedComposition = domainObject.composition;
|
||||
const identifiersToIndex = composition
|
||||
.filter(identifier => !indexedComposition
|
||||
.some(indexedIdentifier => this.openmct.objects
|
||||
.areIdsEqual([identifier, indexedIdentifier])));
|
||||
|
||||
identifiersToIndex.forEach(identifier => {
|
||||
this.openmct.objects.get(identifier).then(objectToIndex => provider.index(objectToIndex));
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Pass an id and model to the worker to be indexed. If the model has
|
||||
* composition, schedule those ids for later indexing.
|
||||
* Pass a domainObject to the worker to be indexed.
|
||||
* If the object has composition, schedule those ids for later indexing.
|
||||
* Watch for object changes and re-index object and children if so
|
||||
*
|
||||
* @private
|
||||
* @param id a model id
|
||||
* @param model a model
|
||||
* @param domainObject a domainObject
|
||||
*/
|
||||
async index(id, domainObject) {
|
||||
async index(domainObject) {
|
||||
const provider = this;
|
||||
const keyString = this.openmct.objects.makeKeyString(id);
|
||||
const keyString = this.openmct.objects.makeKeyString(domainObject.identifier);
|
||||
|
||||
if (!this.indexedIds[keyString]) {
|
||||
this.openmct.objects.observe(domainObject, `*`, this.onMutationOfIndexedObject);
|
||||
this.indexedIds[keyString] = this.openmct.objects.observe(
|
||||
domainObject,
|
||||
'name',
|
||||
this.onNameMutation.bind(this, domainObject)
|
||||
);
|
||||
this.indexedCompositions[keyString] = this.openmct.objects.observe(
|
||||
domainObject,
|
||||
'composition',
|
||||
this.onCompositionMutation.bind(this, domainObject)
|
||||
);
|
||||
}
|
||||
|
||||
this.indexedIds[keyString] = true;
|
||||
|
||||
if ((id.key !== 'ROOT')) {
|
||||
if ((keyString !== 'ROOT')) {
|
||||
if (this.worker) {
|
||||
this.worker.port.postMessage({
|
||||
request: 'index',
|
||||
@ -247,15 +273,12 @@ class InMemorySearchProvider {
|
||||
}
|
||||
}
|
||||
|
||||
const composition = this.openmct.composition.registry.find(foundComposition => {
|
||||
return foundComposition.appliesTo(domainObject);
|
||||
});
|
||||
const composition = this.openmct.composition.get(domainObject);
|
||||
|
||||
if (composition) {
|
||||
const childIdentifiers = await composition.load(domainObject);
|
||||
childIdentifiers.forEach(function (childIdentifier) {
|
||||
provider.scheduleForIndexing(childIdentifier);
|
||||
});
|
||||
if (composition !== undefined) {
|
||||
const children = await composition.load();
|
||||
|
||||
children.forEach(child => provider.scheduleForIndexing(child.identifier));
|
||||
}
|
||||
}
|
||||
|
||||
@ -271,12 +294,12 @@ class InMemorySearchProvider {
|
||||
const provider = this;
|
||||
|
||||
this.pendingRequests += 1;
|
||||
const identifier = await this.openmct.objects.parseKeyString(keyString);
|
||||
const domainObject = await this.openmct.objects.get(identifier.key);
|
||||
const domainObject = await this.openmct.objects.get(keyString);
|
||||
delete provider.pendingIndex[keyString];
|
||||
|
||||
try {
|
||||
if (domainObject) {
|
||||
await provider.index(identifier, domainObject);
|
||||
await provider.index(domainObject);
|
||||
}
|
||||
} catch (error) {
|
||||
console.warn('Failed to index domain object ' + keyString, error);
|
||||
@ -305,9 +328,9 @@ class InMemorySearchProvider {
|
||||
}
|
||||
|
||||
/**
|
||||
* A local version of the same SharedWorker function
|
||||
* if we don't have SharedWorkers available (e.g., iOS)
|
||||
*/
|
||||
* A local version of the same SharedWorker function
|
||||
* if we don't have SharedWorkers available (e.g., iOS)
|
||||
*/
|
||||
localIndexItem(keyString, model) {
|
||||
this.localIndexedItems[keyString] = {
|
||||
type: model.type,
|
||||
@ -347,6 +370,16 @@ class InMemorySearchProvider {
|
||||
};
|
||||
this.onWorkerMessage(eventToReturn);
|
||||
}
|
||||
|
||||
destroyObservers(observers) {
|
||||
Object.entries(observers).forEach(([keyString, unobserve]) => {
|
||||
if (typeof unobserve === 'function') {
|
||||
unobserve();
|
||||
}
|
||||
|
||||
delete observers[keyString];
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default InMemorySearchProvider;
|
||||
|
@ -105,13 +105,18 @@ describe("The Object API Search Function", () => {
|
||||
|
||||
beforeEach((done) => {
|
||||
openmct = createOpenMct();
|
||||
const defaultObjectProvider = openmct.objects.getProvider({
|
||||
key: '',
|
||||
namespace: ''
|
||||
});
|
||||
openmct.objects.addProvider('foo', defaultObjectProvider);
|
||||
spyOn(openmct.objects.inMemorySearchProvider, "query").and.callThrough();
|
||||
spyOn(openmct.objects.inMemorySearchProvider, "localSearch").and.callThrough();
|
||||
|
||||
openmct.on('start', async () => {
|
||||
mockIdentifier1 = {
|
||||
key: 'some-object',
|
||||
namespace: 'some-namespace'
|
||||
namespace: 'foo'
|
||||
};
|
||||
mockDomainObject1 = {
|
||||
type: 'clock',
|
||||
@ -120,7 +125,7 @@ describe("The Object API Search Function", () => {
|
||||
};
|
||||
mockIdentifier2 = {
|
||||
key: 'some-other-object',
|
||||
namespace: 'some-namespace'
|
||||
namespace: 'foo'
|
||||
};
|
||||
mockDomainObject2 = {
|
||||
type: 'clock',
|
||||
@ -129,16 +134,16 @@ describe("The Object API Search Function", () => {
|
||||
};
|
||||
mockIdentifier3 = {
|
||||
key: 'yet-another-object',
|
||||
namespace: 'some-namespace'
|
||||
namespace: 'foo'
|
||||
};
|
||||
mockDomainObject3 = {
|
||||
type: 'clock',
|
||||
name: 'redBear',
|
||||
identifier: mockIdentifier3
|
||||
};
|
||||
await openmct.objects.inMemorySearchProvider.index(mockIdentifier1, mockDomainObject1);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockIdentifier2, mockDomainObject2);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockIdentifier3, mockDomainObject3);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockDomainObject1);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockDomainObject2);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockDomainObject3);
|
||||
done();
|
||||
});
|
||||
openmct.startHeadless();
|
||||
@ -175,9 +180,9 @@ describe("The Object API Search Function", () => {
|
||||
beforeEach(async () => {
|
||||
openmct.objects.inMemorySearchProvider.worker = null;
|
||||
// reindex locally
|
||||
await openmct.objects.inMemorySearchProvider.index(mockIdentifier1, mockDomainObject1);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockIdentifier2, mockDomainObject2);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockIdentifier3, mockDomainObject3);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockDomainObject1);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockDomainObject2);
|
||||
await openmct.objects.inMemorySearchProvider.index(mockDomainObject3);
|
||||
});
|
||||
it("calls local search", () => {
|
||||
openmct.objects.search('foo');
|
||||
|
@ -51,41 +51,29 @@ export default class EditPropertiesAction extends PropertiesAction {
|
||||
/**
|
||||
* @private
|
||||
*/
|
||||
async _onSave(changes) {
|
||||
Object.entries(changes).forEach(([key, value]) => {
|
||||
const properties = key.split('.');
|
||||
let object = this.domainObject;
|
||||
const propertiesLength = properties.length;
|
||||
properties.forEach((property, index) => {
|
||||
const isComplexProperty = propertiesLength > 1 && index !== propertiesLength - 1;
|
||||
if (isComplexProperty && object[property] !== null) {
|
||||
object = object[property];
|
||||
} else {
|
||||
object[property] = value;
|
||||
}
|
||||
_onSave(changes) {
|
||||
try {
|
||||
Object.entries(changes).forEach(([key, value]) => {
|
||||
const properties = key.split('.');
|
||||
let object = this.domainObject;
|
||||
const propertiesLength = properties.length;
|
||||
properties.forEach((property, index) => {
|
||||
const isComplexProperty = propertiesLength > 1 && index !== propertiesLength - 1;
|
||||
if (isComplexProperty && object[property] !== null) {
|
||||
object = object[property];
|
||||
} else {
|
||||
object[property] = value;
|
||||
}
|
||||
});
|
||||
|
||||
object = value;
|
||||
this.openmct.objects.mutate(this.domainObject, key, value);
|
||||
this.openmct.notifications.info('Save successful');
|
||||
});
|
||||
|
||||
object = value;
|
||||
});
|
||||
|
||||
this.domainObject.modified = Date.now();
|
||||
|
||||
// Show saving progress dialog
|
||||
let dialog = this.openmct.overlays.progressDialog({
|
||||
progressPerc: 'unknown',
|
||||
message: 'Do not navigate away from this page or close this browser tab while this message is displayed.',
|
||||
iconClass: 'info',
|
||||
title: 'Saving'
|
||||
});
|
||||
|
||||
const success = await this.openmct.objects.save(this.domainObject);
|
||||
if (success) {
|
||||
this.openmct.notifications.info('Save successful');
|
||||
} else {
|
||||
} catch (error) {
|
||||
this.openmct.notifications.error('Error saving objects');
|
||||
console.error(error);
|
||||
}
|
||||
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user