Skip to content

Commit d6708f3

Browse files
authored
Merge pull request #230 from IQSS/feat/229-get-collection-items-extension-facets-filter-sort
Get Collection Items extension - facets - filter queries - sort/type
2 parents 68639c4 + 3a26dc9 commit d6708f3

File tree

11 files changed

+437
-55
lines changed

11 files changed

+437
-55
lines changed

src/collections/domain/models/CollectionItemSubset.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,17 @@ import { CollectionPreview } from './CollectionPreview'
44

55
export interface CollectionItemSubset {
66
items: (CollectionPreview | DatasetPreview | FilePreview)[]
7+
facets: CollectionItemsFacet[]
78
totalItemCount: number
89
}
10+
11+
export interface CollectionItemsFacet {
12+
name: string
13+
friendlyName: string
14+
labels: CollectionItemsFacetLabel[]
15+
}
16+
17+
interface CollectionItemsFacetLabel {
18+
name: string
19+
count: number
20+
}
Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,73 @@
11
import { CollectionItemType } from './CollectionItemType'
22

3+
export enum SortType {
4+
NAME = 'name',
5+
DATE = 'date'
6+
}
7+
8+
export enum OrderType {
9+
ASC = 'asc',
10+
DESC = 'desc'
11+
}
12+
13+
export type FilterQuery = `${string}:${string}`
14+
315
export class CollectionSearchCriteria {
416
constructor(
517
public readonly searchText?: string,
6-
public readonly itemTypes?: CollectionItemType[]
18+
public readonly itemTypes?: CollectionItemType[],
19+
public readonly sort?: SortType,
20+
public readonly order?: OrderType,
21+
public readonly filterQueries?: FilterQuery[]
722
) {}
823

924
withSearchText(searchText: string | undefined): CollectionSearchCriteria {
10-
return new CollectionSearchCriteria(searchText, this.itemTypes)
25+
return new CollectionSearchCriteria(
26+
searchText,
27+
this.itemTypes,
28+
this.sort,
29+
this.order,
30+
this.filterQueries
31+
)
1132
}
1233

1334
withItemTypes(itemTypes: CollectionItemType[] | undefined): CollectionSearchCriteria {
14-
return new CollectionSearchCriteria(this.searchText, itemTypes)
35+
return new CollectionSearchCriteria(
36+
this.searchText,
37+
itemTypes,
38+
this.sort,
39+
this.order,
40+
this.filterQueries
41+
)
42+
}
43+
44+
withSort(sort: SortType | undefined): CollectionSearchCriteria {
45+
return new CollectionSearchCriteria(
46+
this.searchText,
47+
this.itemTypes,
48+
sort,
49+
this.order,
50+
this.filterQueries
51+
)
52+
}
53+
54+
withOrder(order: OrderType | undefined): CollectionSearchCriteria {
55+
return new CollectionSearchCriteria(
56+
this.searchText,
57+
this.itemTypes,
58+
this.sort,
59+
order,
60+
this.filterQueries
61+
)
62+
}
63+
64+
withFilterQueries(filterQueries: FilterQuery[] | undefined): CollectionSearchCriteria {
65+
return new CollectionSearchCriteria(
66+
this.searchText,
67+
this.itemTypes,
68+
this.sort,
69+
this.order,
70+
filterQueries
71+
)
1572
}
1673
}

src/collections/infra/repositories/CollectionsRepository.ts

Lines changed: 62 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,11 @@ import { CollectionFacet } from '../../domain/models/CollectionFacet'
1111
import { CollectionUserPermissions } from '../../domain/models/CollectionUserPermissions'
1212
import { transformCollectionUserPermissionsResponseToCollectionUserPermissions } from './transformers/collectionUserPermissionsTransformers'
1313
import { CollectionItemSubset } from '../../domain/models/CollectionItemSubset'
14-
import { CollectionSearchCriteria } from '../../domain/models/CollectionSearchCriteria'
14+
import {
15+
CollectionSearchCriteria,
16+
OrderType,
17+
SortType
18+
} from '../../domain/models/CollectionSearchCriteria'
1519
import { CollectionItemType } from '../../domain/models/CollectionItemType'
1620

1721
export interface NewCollectionRequestPayload {
@@ -40,12 +44,16 @@ export interface NewCollectionInputLevelRequestPayload {
4044
required: boolean
4145
}
4246

43-
export interface GetCollectionItemsQueryParams {
44-
q: string
45-
subtree?: string
46-
per_page?: number
47-
start?: number
48-
type?: string
47+
export enum GetCollectionItemsQueryParams {
48+
QUERY = 'q',
49+
SHOW_FACETS = 'show_facets',
50+
SORT = 'sort',
51+
ORDER = 'order',
52+
SUBTREE = 'subtree',
53+
PER_PAGE = 'per_page',
54+
START = 'start',
55+
TYPE = 'type',
56+
FILTERQUERY = 'fq'
4957
}
5058

5159
export class CollectionsRepository extends ApiRepository implements ICollectionsRepository {
@@ -119,37 +127,30 @@ export class CollectionsRepository extends ApiRepository implements ICollections
119127
offset?: number,
120128
collectionSearchCriteria?: CollectionSearchCriteria
121129
): Promise<CollectionItemSubset> {
122-
const queryParams: GetCollectionItemsQueryParams = {
123-
q: '*'
124-
}
130+
const queryParams = new URLSearchParams({
131+
[GetCollectionItemsQueryParams.QUERY]: '*',
132+
[GetCollectionItemsQueryParams.SHOW_FACETS]: 'true',
133+
[GetCollectionItemsQueryParams.SORT]: SortType.DATE,
134+
[GetCollectionItemsQueryParams.ORDER]: OrderType.DESC
135+
})
136+
125137
if (collectionId) {
126-
queryParams.subtree = collectionId
138+
queryParams.set(GetCollectionItemsQueryParams.SUBTREE, collectionId)
127139
}
140+
128141
if (limit) {
129-
queryParams.per_page = limit
142+
queryParams.set(GetCollectionItemsQueryParams.PER_PAGE, limit.toString())
130143
}
144+
131145
if (offset) {
132-
queryParams.start = offset
146+
queryParams.set(GetCollectionItemsQueryParams.START, offset.toString())
133147
}
148+
134149
if (collectionSearchCriteria) {
135150
this.applyCollectionSearchCriteriaToQueryParams(queryParams, collectionSearchCriteria)
136151
}
137152

138-
let url = '/search?sort=date&order=desc'
139-
140-
if (collectionSearchCriteria?.itemTypes) {
141-
const itemTypesQueryString = collectionSearchCriteria.itemTypes
142-
.map((itemType: CollectionItemType) => {
143-
const mappedItemType =
144-
itemType === CollectionItemType.COLLECTION ? 'dataverse' : itemType.toString()
145-
return `type=${mappedItemType}`
146-
})
147-
.join('&')
148-
149-
url += `&${itemTypesQueryString}`
150-
}
151-
152-
return this.doGet(url, true, queryParams)
153+
return this.doGet('/search', true, queryParams)
153154
.then((response) => transformCollectionItemsResponseToCollectionItemSubset(response))
154155
.catch((error) => {
155156
throw error
@@ -201,11 +202,42 @@ export class CollectionsRepository extends ApiRepository implements ICollections
201202
}
202203

203204
private applyCollectionSearchCriteriaToQueryParams(
204-
queryParams: GetCollectionItemsQueryParams,
205+
queryParams: URLSearchParams,
205206
collectionSearchCriteria: CollectionSearchCriteria
206207
) {
207208
if (collectionSearchCriteria.searchText) {
208-
queryParams.q = encodeURIComponent(collectionSearchCriteria.searchText)
209+
queryParams.set(
210+
GetCollectionItemsQueryParams.QUERY,
211+
encodeURIComponent(collectionSearchCriteria.searchText)
212+
)
213+
}
214+
215+
if (collectionSearchCriteria?.itemTypes) {
216+
collectionSearchCriteria.itemTypes.forEach((itemType) => {
217+
const mappedItemType = itemType === CollectionItemType.COLLECTION ? 'dataverse' : itemType
218+
219+
queryParams.append(GetCollectionItemsQueryParams.TYPE, mappedItemType)
220+
})
221+
}
222+
223+
if (collectionSearchCriteria?.sort) {
224+
queryParams.set(GetCollectionItemsQueryParams.SORT, collectionSearchCriteria.sort)
225+
}
226+
227+
if (collectionSearchCriteria?.order) {
228+
queryParams.set(GetCollectionItemsQueryParams.ORDER, collectionSearchCriteria.order)
229+
}
230+
231+
if (collectionSearchCriteria?.filterQueries) {
232+
collectionSearchCriteria.filterQueries.forEach((filterQuery) => {
233+
const [filterQueryKey, filterQueryValue] = filterQuery.split(':')
234+
235+
const filterQueryValueWithQuotes = `"${filterQueryValue}"`
236+
237+
const filterQueryToSet = `${filterQueryKey}:${filterQueryValueWithQuotes}`
238+
239+
queryParams.append(GetCollectionItemsQueryParams.FILTERQUERY, filterQueryToSet)
240+
})
209241
}
210242
}
211243
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export type CollectionItemsFacetPayload = [Record<string, CollectionItemsFacetPayloadValue>]
2+
3+
export interface CollectionItemsFacetPayloadValue {
4+
friendly: string
5+
labels: Record<string, number>[]
6+
}

src/collections/infra/repositories/transformers/collectionTransformers.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,10 @@ import { transformPayloadToOwnerNode } from '../../../../core/infra/repositories
99
import { transformHtmlToMarkdown } from '../../../../datasets/infra/repositories/transformers/datasetTransformers'
1010
import { CollectionFacet } from '../../../domain/models/CollectionFacet'
1111
import { CollectionFacetPayload } from './CollectionFacetPayload'
12-
import { CollectionItemSubset } from '../../../domain/models/CollectionItemSubset'
12+
import {
13+
CollectionItemsFacet,
14+
CollectionItemSubset
15+
} from '../../../domain/models/CollectionItemSubset'
1316
import { DatasetPreview } from '../../../../datasets'
1417
import { FilePreview } from '../../../../files'
1518
import { DatasetPreviewPayload } from '../../../../datasets/infra/repositories/transformers/DatasetPreviewPayload'
@@ -21,6 +24,7 @@ import { CollectionPreviewPayload } from './CollectionPreviewPayload'
2124
import { CollectionPreview } from '../../../domain/models/CollectionPreview'
2225
import { CollectionContact } from '../../../domain/models/CollectionContact'
2326
import { CollectionType } from '../../../domain/models/CollectionType'
27+
import { CollectionItemsFacetPayload } from './CollectionItemsFacetsPayload'
2428

2529
export const transformCollectionResponseToCollection = (response: AxiosResponse): Collection => {
2630
const collectionPayload = response.data.data
@@ -79,7 +83,10 @@ export const transformCollectionItemsResponseToCollectionItemSubset = (
7983
): CollectionItemSubset => {
8084
const responseDataPayload = response.data.data
8185
const itemsPayload = responseDataPayload.items
86+
const facetsPayload = responseDataPayload.facets as CollectionItemsFacetPayload
87+
8288
const items: (DatasetPreview | FilePreview | CollectionPreview)[] = []
89+
8390
itemsPayload.forEach(function (
8491
itemPayload: CollectionPreviewPayload | DatasetPreviewPayload | FilePreviewPayload
8592
) {
@@ -97,8 +104,21 @@ export const transformCollectionItemsResponseToCollectionItemSubset = (
97104
)
98105
}
99106
})
107+
108+
const facets: CollectionItemsFacet[] = Object.entries(facetsPayload[0]).map(
109+
([key, facetData]) => ({
110+
name: key,
111+
friendlyName: facetData.friendly,
112+
labels: facetData.labels.map((label: Record<string, number>) => {
113+
const [name, count] = Object.entries(label)[0]
114+
return { name, count }
115+
})
116+
})
117+
)
118+
100119
return {
101-
items: items,
120+
items,
121+
facets,
102122
totalItemCount: responseDataPayload.total_count
103123
}
104124
}

src/core/infra/repositories/ApiRepository.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export abstract class ApiRepository {
88
public async doGet(
99
apiEndpoint: string,
1010
authRequired = false,
11-
queryParams: object = {}
11+
queryParams: object | URLSearchParams = {}
1212
): Promise<AxiosResponse> {
1313
return await axios
1414
.get(buildRequestUrl(apiEndpoint), buildRequestConfig(authRequired, queryParams))

src/core/infra/repositories/apiConfigBuilders.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ApiConstants } from './ApiConstants'
44

55
export const buildRequestConfig = (
66
authRequired: boolean,
7-
queryParams: object,
7+
queryParams: object | URLSearchParams,
88
contentType: string = ApiConstants.CONTENT_TYPE_APPLICATION_JSON,
99
abortSignal?: AbortSignal
1010
): AxiosRequestConfig => {

0 commit comments

Comments
 (0)