search backend fixes #1

This commit is contained in:
Lotte Hofstede
2018-03-02 16:32:16 +01:00
parent 461ba33e0d
commit 2435b484ce
23 changed files with 204 additions and 139 deletions

View File

@@ -8,12 +8,19 @@ module.exports = {
nameSpace: '/'
},
// The REST API server settings.
// rest: {
// ssl: true,
// host: 'dspace7.4science.it',
// port: 443,
// // NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
// nameSpace: '/dspace-spring-rest/api'
// },
rest: {
ssl: true,
host: 'dspace7.4science.it',
port: 443,
ssl: false,
host: 'dspace7-internal.atmire.com',
port: 80,
// NOTE: Space is capitalized because 'namespace' is a reserved string in TypeScript
nameSpace: '/dspace-spring-rest/api'
nameSpace: '/rest/api'
},
// Caching settings
cache: {

View File

@@ -36,7 +36,7 @@ export class SearchPageComponent implements OnInit, OnDestroy {
query: string;
scopeObjectRDObs: Observable<RemoteData<DSpaceObject>>;
resultsRDObs: Observable<RemoteData<Array<SearchResult<DSpaceObject>>>>;
resultsRDObs: Observable<RemoteData<Array<SearchResult<DSpaceObject>> | PaginatedList<SearchResult<DSpaceObject>>>>;
currentParams = {};
searchOptions: SearchOptions;
sortConfig: SortOptions;

View File

@@ -0,0 +1,17 @@
import { GenericConstructor } from '../../core/shared/generic-constructor';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
const searchResultMap = new Map();
export function searchResultFor(domainConstructor: GenericConstructor<ListableObject>) {
return function decorator(searchResult: any) {
if (!searchResult) {
return;
}
searchResultMap.set(domainConstructor, searchResult);
};
}
export function getSearchResultFor(domainConstructor: GenericConstructor<ListableObject>) {
return searchResultMap.get(domainConstructor);
}

View File

@@ -1,35 +1,27 @@
import { Inject, Injectable, OnDestroy } from '@angular/core';
import { ActivatedRoute, NavigationExtras, Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { map, flatMap, tap, filter } from 'rxjs/operators';
import { flatMap, map, tap } from 'rxjs/operators';
import { ViewMode } from '../../+search-page/search-options.model';
import { GLOBAL_CONFIG } from '../../../config';
import { GlobalConfig } from '../../../config/global-config.interface';
import { RemoteDataBuildService } from '../../core/cache/builders/remote-data-build.service';
import { NormalizedDSpaceObject } from '../../core/cache/models/normalized-dspace-object.model';
import { SortOptions } from '../../core/cache/models/sort-options.model';
import { RestResponse, SearchSuccessResponse } from '../../core/cache/response-cache.models';
import { SearchSuccessResponse } from '../../core/cache/response-cache.models';
import { ResponseCacheEntry } from '../../core/cache/response-cache.reducer';
import { ResponseCacheService } from '../../core/cache/response-cache.service';
import { DebugResponseParsingService } from '../../core/data/debug-response-parsing.service';
import { DSOResponseParsingService } from '../../core/data/dso-response-parsing.service';
import { ItemDataService } from '../../core/data/item-data.service';
import { PaginatedList } from '../../core/data/paginated-list';
import { ResponseParsingService } from '../../core/data/parsing.service';
import { RemoteData } from '../../core/data/remote-data';
import { GetRequest, EndpointMapRequest, RestRequest } from '../../core/data/request.models';
import { RequestEntry } from '../../core/data/request.reducer';
import { GetRequest, RestRequest } from '../../core/data/request.models';
import { RequestService } from '../../core/data/request.service';
import { DSpaceRESTV2Response } from '../../core/dspace-rest-v2/dspace-rest-v2-response.model';
import { DSpaceObject } from '../../core/shared/dspace-object.model';
import { GenericConstructor } from '../../core/shared/generic-constructor';
import { HALEndpointService } from '../../core/shared/hal-endpoint.service';
import { Item } from '../../core/shared/item.model';
import { Metadatum } from '../../core/shared/metadatum.model';
import { PageInfo } from '../../core/shared/page-info.model';
import { URLCombiner } from '../../core/url-combiner/url-combiner';
import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { ItemSearchResult } from '../../shared/object-collection/shared/item-search-result.model';
import { PaginationComponentOptions } from '../../shared/pagination/pagination-component-options.model';
import { RouteService } from '../../shared/route.service';
import { NormalizedSearchResult } from '../normalized-search-result.model';
@@ -40,6 +32,10 @@ import { FilterType } from './filter-type.model';
import { SearchFilterConfig } from './search-filter-config.model';
import { SearchResponseParsingService } from '../../core/data/search-response-parsing.service';
import { SearchQueryResponse } from './search-query-response.model';
import { PageInfo } from '../../core/shared/page-info.model';
import { getSearchResultFor } from './search-result-element-decorator';
import { ListableObject } from '../../shared/object-collection/shared/listable-object.model';
import { NormalizedItem } from '../../core/cache/models/normalized-item.model';
function shuffle(array: any[]) {
let i = 0;
@@ -95,16 +91,14 @@ export class SearchService extends HALEndpointService implements OnDestroy {
// searchOptions: BehaviorSubject<SearchOptions>;
searchOptions: SearchOptions;
constructor(
protected responseCache: ResponseCacheService,
constructor(protected responseCache: ResponseCacheService,
protected requestService: RequestService,
private itemDataService: ItemDataService,
@Inject(GLOBAL_CONFIG) protected EnvConfig: GlobalConfig,
private routeService: RouteService,
private route: ActivatedRoute,
private rdb: RemoteDataBuildService,
private router: Router
) {
private router: Router) {
super();
const pagination: PaginationComponentOptions = new PaginationComponentOptions();
pagination.id = 'search-results-pagination';
@@ -115,7 +109,7 @@ export class SearchService extends HALEndpointService implements OnDestroy {
// this.searchOptions = new BehaviorSubject<SearchOptions>(searchOptions);
}
search(query: string, scopeId?: string, searchOptions?: SearchOptions): Observable<RemoteData<Array<SearchResult<DSpaceObject>>>> {
search(query: string, scopeId?: string, searchOptions?: SearchOptions): Observable<RemoteData<Array<SearchResult<DSpaceObject>> | PaginatedList<SearchResult<DSpaceObject>>>> {
const requestObs = this.getEndpoint().pipe(
map((url: string) => {
const args: string[] = [];
@@ -150,25 +144,57 @@ export class SearchService extends HALEndpointService implements OnDestroy {
flatMap((request: RestRequest) => this.responseCache.get(request.href))
);
const sqrObs = responseCacheObs.pipe(
// get search results from response cache
const sqrObs: Observable<SearchQueryResponse> = responseCacheObs.pipe(
map((entry: ResponseCacheEntry) => entry.response),
map((response: SearchSuccessResponse) => response.results)
);
const dsoObs = sqrObs.pipe(
// turn dspace href from search results to effective list of DSpaceObjects
// Turn list of observable remote data DSO's into observable remote data object with list of DSO
const dsoObs: Observable<RemoteData<DSpaceObject[]>> = sqrObs.pipe(
map((sqr: SearchQueryResponse) => {
return sqr.objects.map((nsr: NormalizedSearchResult) =>
this.rdb.buildSingle(nsr.dspaceObject, NormalizedDSpaceObject));
this.rdb.buildSingle(nsr.dspaceObject));
}),
flatMap((input: Array<Observable<RemoteData<DSpaceObject>>>) => this.rdb.aggregate(input))
);
const payloadObs = Observable.combineLatest(sqrObs, dsoObs, (sqr: SearchQueryResponse, dsos: RemoteData<DSpaceObject[]>) => {
// Create search results again with the correct dso objects linked to each result
const tDomainListObs: Observable<Array<SearchResult<DSpaceObject>>> = Observable.combineLatest(sqrObs, dsoObs, (sqr: SearchQueryResponse, dsos: RemoteData<DSpaceObject[]>) => {
return sqr.objects.map((object: NormalizedSearchResult, index: number) => {
return Object.assign({}, object, {
let co = DSpaceObject;
if (dsos.payload[index]) {
const constructor: GenericConstructor<ListableObject> = dsos.payload[index].constructor as GenericConstructor<ListableObject>;
co = getSearchResultFor(constructor);
return Object.assign(new co(), object, {
dspaceObject: dsos.payload[index]
});
})
} else {
return undefined;
}
});
});
const pageInfoObs: Observable<PageInfo> = responseCacheObs
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
.map((entry: ResponseCacheEntry) => {
if (hasValue((entry.response as SearchSuccessResponse).pageInfo)) {
const resPageInfo = (entry.response as SearchSuccessResponse).pageInfo;
if (isNotEmpty(resPageInfo) && resPageInfo.currentPage >= 0) {
return Object.assign({}, resPageInfo, { currentPage: resPageInfo.currentPage + 1 });
} else {
return resPageInfo;
}
}
});
const payloadObs = Observable.combineLatest(tDomainListObs, pageInfoObs, (tDomainList, pageInfo) => {
if (hasValue(pageInfo)) {
return new PaginatedList(pageInfo, tDomainList);
} else {
return tDomainList;
}
});
return this.rdb.toRemoteDataObservable(requestEntryObs, responseCacheObs, payloadObs);

View File

@@ -22,24 +22,19 @@ import { DSOSuccessResponse, ErrorResponse, SearchSuccessResponse } from '../res
import { ResponseCacheEntry } from '../response-cache.reducer';
import { ResponseCacheService } from '../response-cache.service';
import { getMapsTo, getRelationMetadata, getRelationships } from './build-decorators';
import { NormalizedObject } from '../models/normalized-object.model';
@Injectable()
export class RemoteDataBuildService {
constructor(
protected objectCache: ObjectCacheService,
constructor(protected objectCache: ObjectCacheService,
protected responseCache: ResponseCacheService,
protected requestService: RequestService
) {
protected requestService: RequestService) {
}
buildSingle<TNormalized extends CacheableObject, TDomain>(
hrefObs: string | Observable<string>,
normalizedType: GenericConstructor<TNormalized>
): Observable<RemoteData<TDomain>> {
buildSingle<TNormalized extends NormalizedObject, TDomain>(hrefObs: string | Observable<string>): Observable<RemoteData<TDomain>> {
if (typeof hrefObs === 'string') {
hrefObs = Observable.of(hrefObs);
}
const requestHrefObs = hrefObs.flatMap((href: string) =>
this.objectCache.getRequestHrefBySelfLink(href));
@@ -59,14 +54,14 @@ export class RemoteDataBuildService {
// always use self link if that is cached, only if it isn't, get it via the response.
const payloadObs =
Observable.combineLatest(
hrefObs.flatMap((href: string) => this.objectCache.getBySelfLink<TNormalized>(href, normalizedType))
hrefObs.flatMap((href: string) => this.objectCache.getBySelfLink<TNormalized>(href))
.startWith(undefined),
responseCacheObs
.filter((entry: ResponseCacheEntry) => entry.response.isSuccessful)
.map((entry: ResponseCacheEntry) => (entry.response as DSOSuccessResponse).resourceSelfLinks)
.flatMap((resourceSelfLinks: string[]) => {
if (isNotEmpty(resourceSelfLinks)) {
return this.objectCache.getBySelfLink(resourceSelfLinks[0], normalizedType);
return this.objectCache.getBySelfLink(resourceSelfLinks[0]);
} else {
return Observable.of(undefined);
}
@@ -97,7 +92,7 @@ export class RemoteDataBuildService {
let isSuccessful: boolean;
let error: RemoteDataError;
if (hasValue(resEntry) && hasValue(resEntry.response)) {
isSuccessful = !responsePending && resEntry.response.isSuccessful;
isSuccessful = resEntry.response.isSuccessful;
const errorMessage = isSuccessful === false ? (resEntry.response as ErrorResponse).errorMessage : undefined;
if (hasValue(errorMessage)) {
error = new RemoteDataError(resEntry.response.statusCode, errorMessage);
@@ -114,10 +109,8 @@ export class RemoteDataBuildService {
});
}
buildList<TNormalized extends CacheableObject, TDomain>(
hrefObs: string | Observable<string>,
normalizedType: GenericConstructor<TNormalized>
): Observable<RemoteData<TDomain[] | PaginatedList<TDomain>>> {
buildList<TNormalized extends NormalizedObject, TDomain>(hrefObs: string | Observable<string>,
normalizedType: GenericConstructor<TNormalized>): Observable<RemoteData<TDomain[] | PaginatedList<TDomain>>> {
if (typeof hrefObs === 'string') {
hrefObs = Observable.of(hrefObs);
}
@@ -181,7 +174,7 @@ export class RemoteDataBuildService {
const rdArr = [];
normalized[relationship].forEach((href: string) => {
rdArr.push(this.buildSingle(href, resourceConstructor));
rdArr.push(this.buildSingle(href));
});
if (isList) {
@@ -198,7 +191,7 @@ export class RemoteDataBuildService {
if (isList) {
links[relationship] = this.buildList(normalized[relationship], resourceConstructor);
} else {
links[relationship] = this.buildSingle(normalized[relationship], resourceConstructor);
links[relationship] = this.buildSingle(normalized[relationship]);
}
}
}

View File

@@ -1,30 +0,0 @@
import { inheritSerialization, autoserialize } from 'cerialize';
import { mapsTo } from '../builders/build-decorators';
import { BitstreamFormat } from '../../shared/bitstream-format.model';
import { NormalizedObject } from './normalized-object.model';
@mapsTo(BitstreamFormat)
@inheritSerialization(NormalizedObject)
export class NormalizedBitstreamFormat extends NormalizedObject {
@autoserialize
shortDescription: string;
@autoserialize
description: string;
@autoserialize
mimetype: string;
@autoserialize
supportLevel: number;
@autoserialize
internal: boolean;
@autoserialize
extensions: string;
}

View File

@@ -1,4 +1,3 @@
import { NormalizedDSpaceObject } from './normalized-dspace-object.model';
import { NormalizedBitstream } from './normalized-bitstream.model';
import { NormalizedBundle } from './normalized-bundle.model';
import { NormalizedItem } from './normalized-item.model';
@@ -7,7 +6,6 @@ import { GenericConstructor } from '../../shared/generic-constructor';
import { NormalizedCommunity } from './normalized-community.model';
import { ResourceType } from '../../shared/resource-type';
import { NormalizedObject } from './normalized-object.model';
import { NormalizedBitstreamFormat } from './normalized-bitstream-format.model';
export class NormalizedObjectFactory {
public static getConstructor(type: ResourceType): GenericConstructor<NormalizedObject> {
@@ -15,9 +13,6 @@ export class NormalizedObjectFactory {
case ResourceType.Bitstream: {
return NormalizedBitstream
}
case ResourceType.BitstreamFormat: {
return NormalizedBitstreamFormat
}
case ResourceType.Bundle: {
return NormalizedBundle
}

View File

@@ -1,5 +1,6 @@
import { CacheableObject } from '../object-cache.reducer';
import { autoserialize } from 'cerialize';
import { ResourceType } from '../../shared/resource-type';
/**
* An abstract model class for a NormalizedObject.
*/
@@ -17,6 +18,9 @@ export abstract class NormalizedObject implements CacheableObject {
@autoserialize
uuid: string;
@autoserialize
type: ResourceType;
@autoserialize
_links: {
[name: string]: string

View File

@@ -4,6 +4,7 @@ import {
} from './object-cache.actions';
import { hasValue } from '../../shared/empty.util';
import { CacheEntry } from './cache-entry';
import { ResourceType } from '../shared/resource-type';
export enum DirtyType {
Created = 'Created',
@@ -19,6 +20,7 @@ export enum DirtyType {
export interface CacheableObject {
uuid?: string;
self: string;
type: ResourceType;
// isNew: boolean;
// dirtyType: DirtyType;
// hasDirtyAttributes: boolean;

View File

@@ -10,6 +10,9 @@ import { hasNoValue } from '../../shared/empty.util';
import { GenericConstructor } from '../shared/generic-constructor';
import { coreSelector, CoreState } from '../core.reducers';
import { pathSelector } from '../shared/selectors';
import { Item } from '../shared/item.model';
import { NormalizedObjectFactory } from './models/normalized-object-factory';
import { NormalizedObject } from './models/normalized-object.model';
function selfLinkFromUuidSelector(uuid: string): MemoizedSelector<CoreState, string> {
return pathSelector<CoreState, string>(coreSelector, 'index', IndexName.OBJECT, uuid);
@@ -24,9 +27,8 @@ function entryFromSelfLinkSelector(selfLink: string): MemoizedSelector<CoreState
*/
@Injectable()
export class ObjectCacheService {
constructor(
private store: Store<CoreState>
) { }
constructor(private store: Store<CoreState>) {
}
/**
* Add an object to the cache
@@ -70,14 +72,17 @@ export class ObjectCacheService {
* @return Observable<T>
* An observable of the requested object
*/
getByUUID<T extends CacheableObject>(uuid: string, type: GenericConstructor<T>): Observable<T> {
getByUUID<T extends NormalizedObject>(uuid: string): Observable<T> {
return this.store.select(selfLinkFromUuidSelector(uuid))
.flatMap((selfLink: string) => this.getBySelfLink(selfLink, type))
.flatMap((selfLink: string) => this.getBySelfLink(selfLink))
}
getBySelfLink<T extends CacheableObject>(selfLink: string, type: GenericConstructor<T>): Observable<T> {
getBySelfLink<T extends NormalizedObject>(selfLink: string): Observable<T> {
return this.getEntry(selfLink)
.map((entry: ObjectCacheEntry) => Object.assign(new type(), entry.data) as T);
.map((entry: ObjectCacheEntry) => {
const type: GenericConstructor<NormalizedObject>= NormalizedObjectFactory.getConstructor(entry.data.type);
return Object.assign(new type(), entry.data) as T
});
}
private getEntry(selfLink: string): Observable<ObjectCacheEntry> {
@@ -116,9 +121,9 @@ export class ObjectCacheService {
* The type of the objects to get
* @return Observable<Array<T>>
*/
getList<T extends CacheableObject>(selfLinks: string[], type: GenericConstructor<T>): Observable<T[]> {
getList<T extends NormalizedObject>(selfLinks: string[], type: GenericConstructor<T>): Observable<T[]> {
return Observable.combineLatest(
selfLinks.map((selfLink: string) => this.getBySelfLink<T>(selfLink, type))
selfLinks.map((selfLink: string) => this.getBySelfLink<T>(selfLink))
);
}

View File

@@ -9,8 +9,9 @@ import { CommunityDataService } from './community-data.service';
import { DataService } from './data.service';
import { FindByIDRequest } from './request.models';
import { NormalizedObject } from '../cache/models/normalized-object.model';
export abstract class ComColDataService<TNormalized extends CacheableObject, TDomain> extends DataService<TNormalized, TDomain> {
export abstract class ComColDataService<TNormalized extends NormalizedObject, TDomain> extends DataService<TNormalized, TDomain> {
protected abstract cds: CommunityDataService;
protected abstract objectCache: ObjectCacheService;
@@ -47,7 +48,7 @@ export abstract class ComColDataService<TNormalized extends CacheableObject, TDo
errorResponse.flatMap((response: ErrorResponse) =>
Observable.throw(new Error(`The Community with scope ${scopeID} couldn't be retrieved`))),
successResponse
.flatMap((response: DSOSuccessResponse) => this.objectCache.getByUUID(scopeID, NormalizedCommunity))
.flatMap((response: DSOSuccessResponse) => this.objectCache.getByUUID(scopeID))
.map((nc: NormalizedCommunity) => nc._links[this.linkPath])
.filter((href) => isNotEmpty(href))
).distinctUntilChanged();

View File

@@ -13,8 +13,9 @@ import { PaginatedList } from './paginated-list';
import { RemoteData } from './remote-data';
import { FindAllOptions, FindAllRequest, FindByIDRequest, GetRequest } from './request.models';
import { RequestService } from './request.service';
import { NormalizedObject } from '../cache/models/normalized-object.model';
export abstract class DataService<TNormalized extends CacheableObject, TDomain> extends HALEndpointService {
export abstract class DataService<TNormalized extends NormalizedObject, TDomain> extends HALEndpointService {
protected abstract responseCache: ResponseCacheService;
protected abstract requestService: RequestService;
protected abstract rdbService: RemoteDataBuildService;
@@ -22,9 +23,7 @@ export abstract class DataService<TNormalized extends CacheableObject, TDomain>
protected abstract linkPath: string;
protected abstract EnvConfig: GlobalConfig;
constructor(
protected normalizedResourceType: GenericConstructor<TNormalized>,
) {
constructor(protected normalizedResourceType: GenericConstructor<TNormalized>,) {
super();
}
@@ -95,12 +94,12 @@ export abstract class DataService<TNormalized extends CacheableObject, TDomain>
this.requestService.configure(request);
});
return this.rdbService.buildSingle<TNormalized, TDomain>(hrefObs, this.normalizedResourceType);
return this.rdbService.buildSingle<TNormalized, TDomain>(hrefObs);
}
findByHref(href: string): Observable<RemoteData<TDomain>> {
this.requestService.configure(new GetRequest(this.requestService.generateRequestId(), href));
return this.rdbService.buildSingle<TNormalized, TDomain>(href, this.normalizedResourceType);
return this.rdbService.buildSingle<TNormalized, TDomain>(href);
}
// TODO implement, after the structure of the REST server's POST response is finalized

View File

@@ -1,23 +1,35 @@
import { Injectable } from '@angular/core';
import {
DSOSuccessResponse, RestResponse,
SearchSuccessResponse
} from '../cache/response-cache.models';
import { RestResponse, SearchSuccessResponse } from '../cache/response-cache.models';
import { DSOResponseParsingService } from './dso-response-parsing.service';
import { ResponseParsingService } from './parsing.service';
import { RestRequest } from './request.models';
import { DSpaceRESTV2Response } from '../dspace-rest-v2/dspace-rest-v2-response.model';
import { DSpaceRESTv2Serializer } from '../dspace-rest-v2/dspace-rest-v2.serializer';
import { PageInfo } from '../shared/page-info.model';
import { hasValue, isNotEmpty } from '../../shared/empty.util';
import { isNotEmpty } from '../../shared/empty.util';
import { SearchQueryResponse } from '../../+search-page/search-service/search-query-response.model';
import { Metadatum } from '../shared/metadatum.model';
@Injectable()
export class SearchResponseParsingService implements ResponseParsingService {
constructor(private dsoParser: DSOResponseParsingService) {}
constructor(private dsoParser: DSOResponseParsingService) {
}
parse(request: RestRequest, data: DSpaceRESTV2Response): RestResponse {
const payload = data.payload;
const hitHighlights = payload._embedded.objects
.map((object) => object.hitHighlights)
.map((hhObject) => {
if (hhObject) {
return Object.keys(hhObject).map((key) => Object.assign(new Metadatum(), {
key: key,
value: hhObject[key].join('...')
}))
} else {
return undefined;
}
});
const dsoSelfLinks = payload._embedded.objects
.map((object) => object._embedded.dspaceObject)
// we don't need embedded collections, bitstreamformats, etc for search results.
@@ -32,11 +44,24 @@ export class SearchResponseParsingService implements ResponseParsingService {
.reduce((combined, thisElement) => [...combined, ...thisElement], []);
const objects = payload._embedded.objects
.map((object, index) => Object.assign({}, object, { dspaceObject: dsoSelfLinks[index] }));
.map((object, index) => Object.assign({}, object, {
dspaceObject: dsoSelfLinks[index],
hitHighlights: hitHighlights[index],
// we don't need embedded collections, bitstreamformats, etc for search results.
// And parsing them all takes up a lot of time. Throw them away to improve performance
// until objs until partial results are supported by the rest api
_embedded: undefined
}));
payload.objects = objects;
const deserialized = new DSpaceRESTv2Serializer(SearchQueryResponse).deserialize(payload);
return new SearchSuccessResponse(deserialized, data.statusCode, undefined);
return new SearchSuccessResponse(deserialized, data.statusCode, this.processPageInfo(data.payload.page));
}
protected processPageInfo(pageObj: any): PageInfo {
if (isNotEmpty(pageObj)) {
return new DSpaceRESTv2Serializer(PageInfo).deserialize(pageObj);
} else {
return undefined;
}
}
}

View File

@@ -1,17 +1,24 @@
import { DSpaceObject } from './dspace-object.model';
export class BitstreamFormat extends DSpaceObject {
import { autoserialize } from 'cerialize';
export class BitstreamFormat {
@autoserialize
shortDescription: string;
@autoserialize
description: string;
@autoserialize
mimetype: string;
@autoserialize
supportLevel: number;
@autoserialize
internal: boolean;
@autoserialize
extensions: string;
}

View File

@@ -1,5 +1,7 @@
import { Collection } from '../../../core/shared/collection.model';
import { SearchResult } from '../../../+search-page/search-result.model';
import { Collection } from '../../../core/shared/collection.model';
import { searchResultFor } from '../../../+search-page/search-service/search-result-element-decorator';
@searchResultFor(Collection)
export class CollectionSearchResult extends SearchResult<Collection> {
}

View File

@@ -1,5 +1,7 @@
import { SearchResult } from '../../../+search-page/search-result.model';
import { Community } from '../../../core/shared/community.model';
import { SearchResult } from '../../../+search-page/search-result.model';
import { searchResultFor } from '../../../+search-page/search-service/search-result-element-decorator';
@searchResultFor(Community)
export class CommunitySearchResult extends SearchResult<Community> {
}

View File

@@ -1,5 +1,7 @@
import { SearchResult } from '../../../+search-page/search-result.model';
import { Item } from '../../../core/shared/item.model';
import { searchResultFor } from '../../../+search-page/search-service/search-result-element-decorator';
@searchResultFor(Item)
export class ItemSearchResult extends SearchResult<Item> {
}

View File

@@ -2,10 +2,11 @@ import { Component } from '@angular/core';
import { renderElementsFor} from '../../../object-collection/shared/dso-element-decorator';
import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model';
import { SearchResultGridElementComponent } from '../search-result-grid-element.component';
import { Collection } from '../../../../core/shared/collection.model';
import { ViewMode } from '../../../../+search-page/search-options.model';
import { CollectionSearchResult } from '../../../object-collection/shared/collection-search-result.model';
@Component({
selector: 'ds-collection-search-result-grid-element',

View File

@@ -1,5 +1,6 @@
import { Component } from '@angular/core';
import { Community } from '../../../../core/shared/community.model';
import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
import { SearchResultGridElementComponent } from '../search-result-grid-element.component';

View File

@@ -1,6 +1,7 @@
import { Component } from '@angular/core';
import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
import { SearchResultListElementComponent } from '../search-result-list-element.component';
import { Collection } from '../../../../core/shared/collection.model';
import { ViewMode } from '../../../../+search-page/search-options.model';

View File

@@ -1,10 +1,11 @@
import { Component } from '@angular/core';
import { renderElementsFor } from '../../../object-collection/shared/dso-element-decorator';
import { CommunitySearchResult } from '../../../object-collection/shared/community-search-result.model';
import { SearchResultListElementComponent } from '../search-result-list-element.component';
import { Community } from '../../../../core/shared/community.model';
import { ViewMode } from '../../../../+search-page/search-options.model';
import { CommunitySearchResult } from '../../../object-collection/shared/community-search-result.model';
@Component({
selector: 'ds-community-search-result-list-element',

View File

@@ -3,7 +3,7 @@ import { Component, Inject } from '@angular/core';
import { SearchResult } from '../../../+search-page/search-result.model';
import { DSpaceObject } from '../../../core/shared/dspace-object.model';
import { Metadatum } from '../../../core/shared/metadatum.model';
import { isEmpty, hasNoValue } from '../../empty.util';
import { isEmpty, hasNoValue, isNotEmpty } from '../../empty.util';
import { ListableObject } from '../../object-collection/shared/listable-object.model';
import { AbstractListableElementComponent } from '../../object-collection/shared/object-collection-element/abstract-listable-element.component';
import { Observable } from 'rxjs/Observable';
@@ -24,6 +24,7 @@ export class SearchResultListElementComponent<T extends SearchResult<K>, K exten
getValues(keys: string[]): string[] {
const results: string[] = new Array<string>();
if (isNotEmpty(this.object.hitHighlights)) {
this.object.hitHighlights.forEach(
(md: Metadatum) => {
if (keys.indexOf(md.key) > -1) {
@@ -31,6 +32,7 @@ export class SearchResultListElementComponent<T extends SearchResult<K>, K exten
}
}
);
}
if (isEmpty(results)) {
this.dso.filterMetadata(keys).forEach(
(md: Metadatum) => {
@@ -43,6 +45,7 @@ export class SearchResultListElementComponent<T extends SearchResult<K>, K exten
getFirstValue(key: string): string {
let result: string;
if (isNotEmpty(this.object.hitHighlights)) {
this.object.hitHighlights.some(
(md: Metadatum) => {
if (key === md.key) {
@@ -51,6 +54,7 @@ export class SearchResultListElementComponent<T extends SearchResult<K>, K exten
}
}
);
}
if (hasNoValue(result)) {
result = this.dso.findMetadata(key);
}