Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sort and filter functionality #125

Merged
merged 11 commits into from
Aug 8, 2024
6 changes: 5 additions & 1 deletion lib/Controller/PublicationsController.php
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,17 @@ public function index(ObjectService $objectService, SearchService $searchService
$searchParams = $searchService->createMySQLSearchParams(filters: $filters);
$searchConditions = $searchService->createMySQLSearchConditions(filters: $filters, fieldsToSearch: $fieldsToSearch);
$filters = $searchService->unsetSpecialQueryParams(filters: $filters);
$sort = $searchService->createSortForMySQL(filters: $filters);

return new JSONResponse(['results' => $this->publicationMapper->findAll(limit: null, offset: null, filters: $filters, searchConditions: $searchConditions, searchParams: $searchParams)]);
return new JSONResponse(['results' => $this->publicationMapper->findAll(limit: null, offset: null, filters: $filters, searchConditions: $searchConditions, searchParams: $searchParams, sort: $sort)]);
}

$filters = $searchService->createMongoDBSearchFilter(filters: $filters, fieldsToSearch: $fieldsToSearch);
$filters = $searchService->unsetSpecialQueryParams(filters: $filters);

// @todo Fix mongodb sort
// $sort = $searchService->createSortForMongoDB(filters: $filters);

$dbConfig['base_uri'] = $this->config->getValueString(app: $this->appName, key: 'mongodbLocation');
$dbConfig['headers']['api-key'] = $this->config->getValueString(app: $this->appName, key: 'mongodbKey');
$dbConfig['mongodbCluster'] = $this->config->getValueString(app: $this->appName, key: 'mongodbCluster');
Expand Down
8 changes: 7 additions & 1 deletion lib/Db/CatalogMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters
->setFirstResult($offset);

foreach($filters as $filter => $value) {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
if ($value === 'IS NOT NULL') {
$qb->andWhere($qb->expr()->isNotNull($filter));
} elseif ($value === 'IS NULL') {
$qb->andWhere($qb->expr()->isNull($filter));
} else {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
}
}

if (!empty($searchConditions)) {
Expand Down
8 changes: 7 additions & 1 deletion lib/Db/ListingMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters
->setFirstResult($offset);

foreach($filters as $filter => $value) {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
if ($value === 'IS NOT NULL') {
$qb->andWhere($qb->expr()->isNotNull($filter));
} elseif ($value === 'IS NULL') {
$qb->andWhere($qb->expr()->isNull($filter));
} else {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
}
}

if (!empty($searchConditions)) {
Expand Down
8 changes: 7 additions & 1 deletion lib/Db/MetaDataMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters
->setFirstResult($offset);

foreach($filters as $filter => $value) {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
if ($value === 'IS NOT NULL') {
$qb->andWhere($qb->expr()->isNotNull($filter));
} elseif ($value === 'IS NULL') {
$qb->andWhere($qb->expr()->isNull($filter));
} else {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
}
}

if (!empty($searchConditions)) {
Expand Down
8 changes: 7 additions & 1 deletion lib/Db/OrganisationMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters
->setFirstResult($offset);

foreach($filters as $filter => $value) {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
if ($value === 'IS NOT NULL') {
$qb->andWhere($qb->expr()->isNotNull($filter));
} elseif ($value === 'IS NULL') {
$qb->andWhere($qb->expr()->isNull($filter));
} else {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
}
}

if (!empty($searchConditions)) {
Expand Down
27 changes: 23 additions & 4 deletions lib/Db/PublicationMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,14 @@ public function find(int $id): Publication
return $this->findEntity(query: $qb);
}

public function findAll(?int $limit = null, ?int $offset = null, ?array $filters = [], ?array $searchConditions = [], ?array $searchParams = []): array
{
public function findAll(
?int $limit = null,
?int $offset = null,
?array $filters = [],
?array $searchConditions = [],
?array $searchParams = [],
?array $sort = []
): array {
$qb = $this->db->getQueryBuilder();

$qb->select('*')
Expand All @@ -38,16 +44,29 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters
->setFirstResult($offset);

foreach($filters as $filter => $value) {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
if ($value === 'IS NOT NULL') {
$qb->andWhere($qb->expr()->isNotNull($filter));
} elseif ($value === 'IS NULL') {
$qb->andWhere($qb->expr()->isNull($filter));
} else {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
}
}

if (!empty($searchConditions)) {
if (empty($searchConditions) === false) {
$qb->andWhere('(' . implode(' OR ', $searchConditions) . ')');
foreach ($searchParams as $param => $value) {
$qb->setParameter($param, $value);
}
}

if (empty($sort) === false) {
foreach ($sort as $field => $direction) {
$direction = strtoupper($direction) === 'DESC' ? 'DESC' : 'ASC';
$qb->addOrderBy($field, $direction);
}
}

return $this->findEntities(query: $qb);
}

Expand Down
8 changes: 7 additions & 1 deletion lib/Db/ThemeMapper.php
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,13 @@ public function findAll(?int $limit = null, ?int $offset = null, ?array $filters
->setFirstResult($offset);

foreach($filters as $filter => $value) {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
if ($value === 'IS NOT NULL') {
$qb->andWhere($qb->expr()->isNotNull($filter));
} elseif ($value === 'IS NULL') {
$qb->andWhere($qb->expr()->isNull($filter));
} else {
$qb->andWhere($qb->expr()->eq($filter, $qb->createNamedParameter($value)));
}
}

if (!empty($searchConditions)) {
Expand Down
5 changes: 5 additions & 0 deletions lib/Service/ObjectService.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,11 @@ public function findObjects(array $filters, array $config): array
$object['dataSource'] = $config['mongodbCluster'];
$object['filter'] = $filters;

// @todo Fix mongodb sort
// if (empty($sort) === false) {
// $object['filter'][] = ['$sort' => $sort];
// }

$returnData = $client->post(
uri: 'action/find',
options: ['json' => $object]
Expand Down
52 changes: 52 additions & 0 deletions lib/Service/SearchService.php
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,15 @@ public function createMongoDBSearchFilter(array $filters, array $fieldsToSearch)
unset($filters['_search']);
}

foreach ($filters as $field => $value) {
if ($value === 'IS NOT NULL') {
$filters[$field] = ['$ne' => null];
}
if ($value === 'IS NULL') {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You could also add || $value === 'null' here

$filters[$field] = ['$eq' => null];
}
}

return $filters;

}//end createMongoDBSearchFilter()
Expand Down Expand Up @@ -270,6 +279,49 @@ public function createMySQLSearchParams(array $filters): array

}//end createMongoDBSearchFilter()

/**
* This function creates an sort array based on given order param from request.
*
* @param array $filters Query parameters from request.
*
* @return array $sort
*/
public function createSortForMySQL(array $filters): array
{
$sort = [];
if (isset($filters['_order']) && is_array($filters['_order'])) {
foreach ($filters['_order'] as $field => $direction) {
$direction = strtoupper($direction) === 'DESC' ? 'DESC' : 'ASC';
$sort[$field] = $direction;
}
}

return $sort;

}//end createSortArrayFromParams()

/**
* This function creates an sort array based on given order param from request.
*
* @todo Not functional yet. Needs to be fixed (see PublicationsController->index).
*
* @param array $filters Query parameters from request.
*
* @return array $sort
*/
public function createSortForMongoDB(array $filters): array
{
$sort = [];
if (isset($filters['_order']) && is_array($filters['_order'])) {
foreach ($filters['_order'] as $field => $direction) {
$sort[$field] = strtoupper($direction) === 'DESC' ? -1 : 1;
}
}

return $sort;

}//end createSortForMongoDB()

/**
* Parses the request query string and returns it as an array of queries.
*
Expand Down
20 changes: 17 additions & 3 deletions src/store/modules/publication.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,26 @@ export const usePublicationStore = defineStore(
this.publicationList = publicationList.map((publicationItem) => new Publication(publicationItem))
console.log('Active publication item set to ' + publicationList.length)
},
async refreshPublicationList(search = null) {
async refreshPublicationList(normalSearch = [], advancedSearch = null, sortField = null, sortDirection = null) {
// @todo this might belong in a service?
console.log(normalSearch)
let endpoint = '/index.php/apps/opencatalogi/api/publications'
if (search !== null && search !== '') {
endpoint = endpoint + '?_search=' + search
const params = new URLSearchParams()
for (const item of normalSearch) {
if (item.key && item.value !== undefined) {
params.append(item.key, item.value)
}
}
if (advancedSearch !== null && advancedSearch !== '') {
params.append('_search', advancedSearch)
}
if (sortField !== null && sortField.value !== null && sortDirection !== null && sortDirection !== '') {
params.append('_order[' + sortField.value + ']', sortDirection)
}
if (params.toString()) {
endpoint += '?' + params.toString()
}

return fetch(
endpoint,
{
Expand Down
87 changes: 62 additions & 25 deletions src/views/publications/PublicationList.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,36 +7,55 @@ import { navigationStore, publicationStore } from '../../store/store.js'
<ul>
<div class="listHeader">
<NcTextField class="searchField"
:value.sync="search"
:value.sync="advancedSearch"
label="Zoeken"
trailing-button-icon="close"
:show-trailing-button="search !== ''"
@trailing-button-click="search = ''">
:show-trailing-button="advancedSearch !== ''"
@trailing-button-click="advancedSearch = ''">
<Magnify :size="20" />
</NcTextField>
<NcActions>
<NcActionCaption name="Zoeken" />
<NcActionCheckbox>
<NcActionCheckbox
:checked="conceptChecked"
:value="'concept'"
@change="handleCheckboxChange('concept', $event)">
Concept
</NcActionCheckbox>
<NcActionCheckbox>
<NcActionCheckbox
:checked="gepubliceerdChecked"
:value="'gepubliceerd'"
@change="handleCheckboxChange('gepubliceerd', $event)">
Gepubliceerd
</NcActionCheckbox>
<NcActionSeparator />
<NcActionCaption name="Sorteren" />
<NcActionInput
v-model="sortField"
type="multiselect"
input-label="Eigenschap"
:options="['Apple', 'Banana', 'Cherry']">
:options="[
{ label: 'Titel', value: 'title' },
{ label: 'Datum gepubliceerd', value: 'published' },
{ label: 'Datum aangepast', value: 'modified' }
]">
<template #icon>
<Pencil :size="20" />
</template>
Kies een eigenschap
</NcActionInput>
<NcActionRadio name="Richting" value="Asc">
<NcActionRadio
:checked="sortDirection === 'asc'"
name="sortDirection"
value="asc"
@update:checked="updateSortOrder('asc')">
Oplopend
</NcActionRadio>
<NcActionRadio name="Richting" value="Desc">
<NcActionRadio
:checked="sortDirection === 'desc'"
name="sortDirection"
value="desc"
@update:checked="updateSortOrder('desc')">
Aflopend
</NcActionRadio>
<NcActionSeparator />
Expand Down Expand Up @@ -194,18 +213,18 @@ export default {
HelpCircleOutline,
},
beforeRouteLeave(to, from, next) {
search = ''
this.advancedSearch = ''
next()
},
props: {
search: {
type: String,
required: true,
},
},
data() {
return {
loading: false,
sortField: null,
sortDirection: 'desc',
normalSearch: [],
advancedSearch: '',
conceptChecked: false,
gepubliceerdChecked: false,
}
},
computed: {
Expand All @@ -217,11 +236,10 @@ export default {
},
},
watch: {
search: {
handler(search) {
this.debouncedFetchData(search)
},
},
advancedSearch: 'debouncedFetchData',
sortField: 'fetchData',
sortDirection: 'fetchData',
normalSearch: 'fetchData',
},
mounted() {
this.fetchData()
Expand All @@ -233,16 +251,35 @@ export default {
},
fetchData(search = null) {
this.loading = true
publicationStore.refreshPublicationList(search)
publicationStore.refreshPublicationList(this.normalSearch, this.advancedSearch, this.sortField, this.sortDirection)
.then(() => {
this.loading = false
})
},
debouncedFetchData: debounce(function(search) {
this.fetchData(search)
debouncedFetchData: debounce(function() {
this.fetchData()
}, 500),
openLink(url, type = '') {
window.open(url, type)
updateSortOrder(value) {
this.sortDirection = value
},
updateNormalSearch() {
this.normalSearch = []
if (this.conceptChecked) {
this.normalSearch.push({ key: 'status', value: 'concept' })
}
if (this.gepubliceerdChecked) {
this.normalSearch.push({ key: 'status', value: 'published' })
}
},
handleCheckboxChange(key, event) {
const checked = event.target.checked

if (key === 'concept') {
this.conceptChecked = checked
} else if (key === 'gepubliceerd') {
this.gepubliceerdChecked = checked
}
this.updateNormalSearch()
},
},
}
Expand Down
Loading