Files
directus/api/src/services/meta.ts
Rijk van Zanten f64a5bef7e Add new advanced filters experience (#8570)
* Remove advanced filter sidebar detail

So long, and thanks for all the fish.

* Remove filter conversion logic

* Start replacing/removing old skool filters

* Add inline mode for usages in search bar

* Make filter work in header bar

* Emit empty string as null in filter

* Move shared filter types to shared

* Upgrade use-items

* Fix manual sort on tabular

* Cleanup styling in search bar usage

* Tweak styling

* Fix filtering issues

* Update cards

* Remove activeFilterCount from tabular

* Update maps to work with new filters

* Update calendar to new filter/sort structure

* Fix activity module nav/search

* Fix no-results message

* Update file library filtering

* Finalize user search

* Allow filtering in drawer-collection

* Handle cancelled responses semi-gracefully

* Add loading start state timeout

* Replace sort type in api

* Last commit before redoing a bunch

* Finish new visual style

* Remove unused rounded prop from v-menu

* Tweak sizing

* Enough size tweaking for now

* Count all filter operators instead of top

* Fix archive casting

* Fix api build

* Add merge filters util

* Split filter in user vs system

* Fix export sidebar detail

* Show field label on permissions configuration

* Add migration for filter/sort

* Use filters in insights
2021-10-07 18:06:03 -04:00

92 lines
2.7 KiB
TypeScript

import { Knex } from 'knex';
import getDatabase from '../database';
import { ForbiddenException } from '../exceptions';
import { AbstractServiceOptions, SchemaOverview } from '../types';
import { Accountability, Query } from '@directus/shared/types';
import { applyFilter, applySearch } from '../utils/apply-query';
import { parseFilter } from '@directus/shared/utils';
export class MetaService {
knex: Knex;
accountability: Accountability | null;
schema: SchemaOverview;
constructor(options: AbstractServiceOptions) {
this.knex = options.knex || getDatabase();
this.accountability = options.accountability || null;
this.schema = options.schema;
}
async getMetaForQuery(collection: string, query: any): Promise<Record<string, any> | undefined> {
if (!query || !query.meta) return;
const results = await Promise.all(
query.meta.map((metaVal: string) => {
if (metaVal === 'total_count') return this.totalCount(collection);
if (metaVal === 'filter_count') return this.filterCount(collection, query);
})
);
return results.reduce((metaObject: Record<string, any>, value, index) => {
return {
...metaObject,
[query.meta![index]]: value,
};
}, {});
}
async totalCount(collection: string): Promise<number> {
const dbQuery = this.knex(collection).count('*', { as: 'count' }).first();
if (this.accountability?.admin !== true) {
const permissionsRecord = this.schema.permissions.find((permission) => {
return permission.action === 'read' && permission.collection === collection;
});
if (!permissionsRecord) throw new ForbiddenException();
const permissions = parseFilter(permissionsRecord.permissions, this.accountability);
applyFilter(this.knex, this.schema, dbQuery, permissions, collection);
}
const result = await dbQuery;
return Number(result?.count ?? 0);
}
async filterCount(collection: string, query: Query): Promise<number> {
const dbQuery = this.knex(collection).count('*', { as: 'count' });
let filter = query.filter || {};
if (this.accountability?.admin !== true) {
const permissionsRecord = this.schema.permissions.find((permission) => {
return permission.action === 'read' && permission.collection === collection;
});
if (!permissionsRecord) throw new ForbiddenException();
const permissions = parseFilter(permissionsRecord.permissions, this.accountability);
if (Object.keys(filter).length > 0) {
filter = { _and: [permissions, filter] };
} else {
filter = permissions;
}
}
if (Object.keys(filter).length > 0) {
applyFilter(this.knex, this.schema, dbQuery, filter, collection);
}
if (query.search) {
applySearch(this.schema, dbQuery, query.search, collection);
}
const records = await dbQuery;
return Number(records[0].count);
}
}