diff --git a/scripts/core/query-formatting.ts b/scripts/core/query-formatting.ts index 15659154d1..8b538f9760 100644 --- a/scripts/core/query-formatting.ts +++ b/scripts/core/query-formatting.ts @@ -38,6 +38,12 @@ function toElasticFilter(q: ILogicalOperator | IComparison) { return {range: {[field]: {'lte': value}}}; case '$in': return {terms: {[field]: value}}; + case '$exists': + return {query: {exists: {field: field}}}; + case '$notExists': + return {query: {bool: {must_not: [{exists: {field: field}}]}}}; + case '$stringContains': + return {query: {simple_query_string: {query: value.val, fields: [field]}}}; } throw new Error(`Conversion for operator ${operator} is not defined.`); @@ -79,6 +85,15 @@ function toPyEveFilter(q: ILogicalOperator | IComparison) { return {[field]: {$lte: value}}; case '$in': return {[field]: {$in: value}}; + case '$exists': + return {[field]: {$exists: true}}; + case '$notExists': + return {[field]: {$exists: false}}; + case '$stringContains': + return {[field]: { + $regex: value.val, + $options: '-i', + }}; } throw new Error(`Conversion for operator ${operator} is not defined.`); @@ -138,9 +153,7 @@ export function toElasticQuery(q: ISuperdeskQuery): {q?: string; source: string} } if (Object.keys(filtered).length > 0) { - query['query'] = { - filtered: filtered, - }; + query['query'] = {filtered: filtered}; } if (q.fullTextSearch) { @@ -172,7 +185,10 @@ function objectToTuple(obj: {[key: string]: T}): [string, T] { return [key, obj[key]]; } -export function toPyEveQuery(filter: ISuperdeskQuery['filter'], sort: ISuperdeskQuery['sort']): IPyEveQuery { +export function toPyEveQuery( + filter: ISuperdeskQuery['filter'], + sort: ISuperdeskQuery['sort'], +): IPyEveQuery { const result: IPyEveQuery = { sort: `[${ sort.map((sortOption) => { diff --git a/scripts/core/superdesk-api.d.ts b/scripts/core/superdesk-api.d.ts index 5d14664bf0..fb58c2db68 100644 --- a/scripts/core/superdesk-api.d.ts +++ b/scripts/core/superdesk-api.d.ts @@ -1621,13 +1621,17 @@ declare module 'superdesk-api' { | {$gte: any} | {$lt: any} | {$lte: any} - | {$in: any}; - // consider adding $inString for matching substrings + | {$in: any} + | {$exists: any} // not null + | {$notExists: any} // is null + | {$stringContains: any}; // options for the future export type IComparison = {[field: string]: IComparisonOptions}; export type IAndOperator = {$and: Array}; export type IOrOperator = {$or: Array}; export type ILogicalOperator = IAndOperator | IOrOperator; + export type ISortDirection = 'asc' | 'desc'; + export type ISortOptions = Array<{[field: string]: ISortDirection}>; /** * Universal query format that works with both - Elasticsearch and pyeve endpoints @@ -1635,7 +1639,7 @@ declare module 'superdesk-api' { export interface ISuperdeskQuery { filter: ILogicalOperator; fullTextSearch?: string; - sort: Array<{[field: string]: 'asc' | 'desc'}>; + sort: ISortOptions; page: number; max_results: number; } @@ -1644,7 +1648,7 @@ declare module 'superdesk-api' { endpoint: string, filter: ILogicalOperator; fullTextSearch?: string; - sort: Array<{[field: string]: 'asc' | 'desc'}>; + sort: ISortOptions; } diff --git a/scripts/core/ui/components/content-create-dropdown/more-templates.tsx b/scripts/core/ui/components/content-create-dropdown/more-templates.tsx index 1a62d63ca3..0aacf8d4d2 100644 --- a/scripts/core/ui/components/content-create-dropdown/more-templates.tsx +++ b/scripts/core/ui/components/content-create-dropdown/more-templates.tsx @@ -2,11 +2,19 @@ import React from 'react'; import {IconButton, Input, WithPagination} from 'superdesk-ui-framework/react'; import {gettext} from 'core/utils'; import {Spacer, SpacerBlock} from '../Spacer'; -import {IRestApiResponse, ITemplate} from 'superdesk-api'; +import { + IComparison, + ILogicalOperator, + IRestApiResponse, + ISortOptions, + ISuperdeskQuery, + ITemplate, +} from 'superdesk-api'; import {httpRequestJsonLocal} from 'core/helpers/network'; import {DropdownOption} from './dropdown-option'; import {nameof} from 'core/helpers/typescript-helpers'; import {sdApi} from 'api'; +import {prepareSuperdeskQuery} from 'core/helpers/universal-query'; interface IProps { onSelect(template: ITemplate): void; @@ -29,39 +37,55 @@ export class MoreTemplates extends React.PureComponent { } fetchData(pageToFetch: number, pageSize: number, abortSignal?: AbortSignal): Promise> { - const templateDesks = nameof('template_desks'); + const template_desks = nameof('template_desks'); const currentDeskId = sdApi.desks.getCurrentDeskId(); - const deskCriteria: any = [ - {[templateDesks]: {$exists: false}}, - {[templateDesks]: []}, + const templateDesks: Array = [ + {[template_desks]: {$exists: false}}, + {[template_desks]: {$eq: []}}, ]; + const criteria: ILogicalOperator = { + $or: [ + { + $and: [ + {is_public: {$eq: false}}, + {user: {$eq: sdApi.user.getCurrentUserId()}}, + ], + }, + { + $and: [ + {is_public: {$eq: true}}, + {$or: templateDesks}, + ], + }, + ], + }; + if (currentDeskId != null) { - deskCriteria.push({[templateDesks]: {$in: [currentDeskId]}}); + templateDesks.push({$and: [{[template_desks]: {$in: [currentDeskId]}}, {is_public: {$eq: true}}]}); } - const criteria = {$or: [{$or: deskCriteria}, {user: sdApi.user.getCurrentUserId()}]}; const templateName = nameof('template_name'); - const where = {$and: [criteria]}; + const sort: ISortOptions = [{[templateName]: 'desc'}]; + const filtered: ILogicalOperator = { + $and: [ + criteria, + {[templateName]: {$stringContains: {val: this.state.searchString, options: null}}}, + ], + }; + const maybeFiltered: ILogicalOperator = this.state.searchString.length < 1 ? criteria : filtered; - if (this.state.searchString.length < 1) { - where[templateName] = { - $regex: this.state.searchString, - $options: '-i', - }; - } + const query: ISuperdeskQuery = { + filter: maybeFiltered, + page: pageToFetch, + max_results: pageSize, + sort: sort, + }; return httpRequestJsonLocal>({ - method: 'GET', - path: '/content_templates', - urlParams: { - max_results: pageSize, - page: pageToFetch, - sort: templateName, - where: where, - }, - abortSignal, + ...prepareSuperdeskQuery('/content_templates', query), + abortSignal: abortSignal, }); }