Skip to content

Commit

Permalink
[DSC-1988] Limit the number of items requested by search manager
Browse files Browse the repository at this point in the history
  • Loading branch information
alisaismailati committed Oct 29, 2024
1 parent 66bc5ac commit 6b21eea
Show file tree
Hide file tree
Showing 10 changed files with 53 additions and 10 deletions.
15 changes: 10 additions & 5 deletions src/app/core/browse/search-manager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,8 @@ export class SearchManager {
})
.filter((item) => hasValue(item));

const uuidList = this.extractUUID(items, environment.followAuthorityMetadata);
const uuidList = this.extractUUID(items, environment.followAuthorityMetadata, 100);

return uuidList.length > 0 ? this.itemService.findAllById(uuidList).pipe(
getFirstCompletedRemoteData(),
map(data => {
Expand All @@ -124,27 +125,31 @@ export class SearchManager {
) : of(null);
}

protected extractUUID(items: Item[], metadataToFollow: FollowAuthorityMetadata[]): string[] {
protected extractUUID(items: Item[], metadataToFollow: FollowAuthorityMetadata[], numberOfElementsToReturn?: number): string[] {
const uuidMap = {};

items.forEach((item) => {
metadataToFollow.forEach((followMetadata: FollowAuthorityMetadata) => {
if (item.entityType === followMetadata.type) {
if (isArray(followMetadata.metadata)) {
followMetadata.metadata.forEach((metadata) => {
Metadata.all(item.metadata, metadata)
followMetadata.metadata.forEach((metadata) => {
Metadata.all(item.metadata, metadata, null, environment.followAuthorityMetadataValuesLimit)
.filter((metadataValue: MetadataValue) => Metadata.hasValidItemAuthority(metadataValue.authority))
.forEach((metadataValue: MetadataValue) => uuidMap[metadataValue.authority] = metadataValue);
});
} else {
Metadata.all(item.metadata, followMetadata.metadata)
Metadata.all(item.metadata, followMetadata.metadata, null, environment.followAuthorityMetadataValuesLimit)
.filter((metadataValue: MetadataValue) => Metadata.hasValidItemAuthority(metadataValue.authority))
.forEach((metadataValue: MetadataValue) => uuidMap[metadataValue.authority] = metadataValue);
}
}
});
});

if (hasValue(numberOfElementsToReturn) && numberOfElementsToReturn > 0) {
return Object.keys(uuidMap).slice(0, numberOfElementsToReturn);
}

return Object.keys(uuidMap);
}
}
5 changes: 5 additions & 0 deletions src/app/core/browse/search.manager.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,11 @@ describe('SearchManager', () => {
const uuidList = (service as any).extractUUID([firstPublication, firstPublication], [{type: 'Publication', metadata: ['dc.contributor.author']}]);
expect(uuidList).toEqual([validAuthority]);
});

it('should limit the number of extracted uuids', () => {
const uuidList = (service as any).extractUUID([firstPublication, secondPublication, invalidAuthorityPublication], [{type: 'Publication', metadata: ['dc.contributor.author']}], 2);
expect(uuidList.length).toBe(2);
});
});

});
12 changes: 12 additions & 0 deletions src/app/core/shared/dspace-object.model.ts
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,18 @@ export class DSpaceObject extends ListableObject implements CacheableObject {
return Metadata.all(this.metadata, keyOrKeys, valueFilter);
}

/**
* Gets all matching metadata in this DSpaceObject, up to a limit.
*
* @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see [[Metadata]].
* @param {number} limit The maximum number of results to return.
* @param {MetadataValueFilter} valueFilter The value filter to use. If unspecified, no filtering will be done.
* @returns {MetadataValue[]} the matching values or an empty array.
*/
limitedMetadata(keyOrKeys: string | string[], limit: number, valueFilter?: MetadataValueFilter): MetadataValue[] {
return Metadata.all(this.metadata, keyOrKeys, valueFilter, limit);
}

/**
* Like [[allMetadata]], but only returns string values.
*
Expand Down
15 changes: 12 additions & 3 deletions src/app/core/shared/metadata.utils.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,20 @@ const multiViewModelList = [
{ key: 'foo', ...bar, order: 0 }
];

const testMethod = (fn, resultKind, mapOrMaps, keyOrKeys, expected, filter?) => {
const testMethod = (fn, resultKind, mapOrMaps, keyOrKeys, expected, filter?, limit?: number) => {
const keys = keyOrKeys instanceof Array ? keyOrKeys : [keyOrKeys];
describe('and key' + (keys.length === 1 ? (' ' + keys[0]) : ('s ' + JSON.stringify(keys)))
+ ' with ' + (isUndefined(filter) ? 'no filter' : 'filter ' + JSON.stringify(filter)), () => {
const result = fn(mapOrMaps, keys, filter);
const result = fn(mapOrMaps, keys, filter, limit);
let shouldReturn;
if (resultKind === 'boolean') {
shouldReturn = expected;
} else if (isUndefined(expected)) {
shouldReturn = 'undefined';
} else if (expected instanceof Array) {
shouldReturn = 'an array with ' + expected.length + ' ' + (expected.length > 1 ? 'ordered ' : '')
+ resultKind + (expected.length !== 1 ? 's' : '');
+ resultKind + (expected.length !== 1 ? 's' : '')
+ (isUndefined(limit) ? '' : ' (limited to ' + limit + ')');
} else {
shouldReturn = 'a ' + resultKind;
}
Expand Down Expand Up @@ -297,4 +298,12 @@ describe('Metadata', () => {

});

describe('all method with limit', () => {
const testAllWithLimit = (mapOrMaps, keyOrKeys, expected, limit) =>
testMethod(Metadata.all, 'value', mapOrMaps, keyOrKeys, expected, undefined, limit);

describe('with multiMap and limit', () => {
testAllWithLimit(multiMap, 'dc.title', [dcTitle1], 1);
});
});
});
6 changes: 5 additions & 1 deletion src/app/core/shared/metadata.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,10 +37,11 @@ export class Metadata {
* checked in order, and only values from the first with at least one match will be returned.
* @param {string|string[]} keyOrKeys The metadata key(s) in scope. Wildcards are supported; see above.
* @param {MetadataValueFilter} filter The value filter to use. If unspecified, no filtering will be done.
* @param {number} limit The maximum number of values to return. If unspecified, all matching values will be returned.
* @returns {MetadataValue[]} the matching values or an empty array.
*/
public static all(mapOrMaps: MetadataMapInterface | MetadataMapInterface[], keyOrKeys: string | string[],
filter?: MetadataValueFilter): MetadataValue[] {
filter?: MetadataValueFilter, limit?: number): MetadataValue[] {
const mdMaps: MetadataMapInterface[] = mapOrMaps instanceof Array ? mapOrMaps : [mapOrMaps];
const matches: MetadataValue[] = [];
for (const mdMap of mdMaps) {
Expand All @@ -50,6 +51,9 @@ export class Metadata {
for (const candidate of candidates) {
if (Metadata.valueMatches(candidate as MetadataValue, filter)) {
matches.push(candidate as MetadataValue);
if (hasValue(limit) && matches.length >= limit) {
return matches;
}
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ <h3 [innerHTML]="dsoTitle" [ngClass]="{'lead': true,'text-muted': !item.firstMet
<span class="item-list-date" [innerHTML]="item.firstMetadataValue('dc.date.issued') || ('mydspace.results.no-date' | translate)"></span>)
<span *ngIf="item.hasMetadata(authorMetadata);" class="item-list-authors">
<span *ngIf="item.allMetadataValues(authorMetadata).length === 0">{{'mydspace.results.no-authors' | translate}}</span>
<span *ngFor="let author of item.allMetadata(authorMetadata); let i=index; let last=last;">
<span *ngFor="let author of item.limitedMetadata(authorMetadata, authorMetadataLimit); let i=index; let last=last;">
<ds-metadata-link-view *ngIf="!!item && !!author" [item]="item" [metadataName]="authorMetadata"
[metadata]="author"></ds-metadata-link-view><span
*ngIf="!last">; </span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,8 @@ export class ItemListPreviewComponent implements OnInit {

authorMetadata = environment.searchResult.authorMetadata;

authorMetadataLimit = environment.followAuthorityMetadataValuesLimit;

constructor(
@Inject(APP_CONFIG) protected appConfig: AppConfig,
public dsoNameService: DSONameService,
Expand Down
1 change: 1 addition & 0 deletions src/config/app-config.interface.ts
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ interface AppConfig extends Config {
suggestion: SuggestionConfig[];
addToAnyPlugin: AddToAnyPluginConfig;
followAuthorityMetadata: FollowAuthorityMetadata[];
followAuthorityMetadataValuesLimit: number;
metricVisualizationConfig: MetricVisualizationConfig[];
attachmentRendering: AttachmentRenderingConfig;
advancedAttachmentRendering: AdvancedAttachmentRenderingConfig;
Expand Down
4 changes: 4 additions & 0 deletions src/config/default-app-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,10 @@ export class DefaultAppConfig implements AppConfig {
}
];

// The maximum number of metadata values to process for each metadata key
// when following authority metadata values.
followAuthorityMetadataValuesLimit = 5;

// Collection Page Config
collection: CollectionPageConfig = {
edit: {
Expand Down
1 change: 1 addition & 0 deletions src/environments/environment.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -276,6 +276,7 @@ export const environment: BuildConfig = {
metadata: ['dc.contributor.author']
}
],
followAuthorityMetadataValuesLimit: 5,
item: {
edit: {
undoTimeout: 10000 // 10 seconds
Expand Down

0 comments on commit 6b21eea

Please sign in to comment.