Move query parsing to Query class

This commit is contained in:
John R. Supplee 2021-03-10 12:39:39 +02:00
parent 097cae1f8c
commit 6def7d6f70
3 changed files with 374 additions and 381 deletions

View file

@ -1,48 +1,6 @@
import { CardSearchPagedComponent } from '../../lib/cardSearch';
import Boards from '../../../models/boards';
import moment from 'moment';
import {
OPERATOR_ASSIGNEE,
OPERATOR_BOARD,
OPERATOR_COMMENT,
OPERATOR_CREATED_AT,
OPERATOR_DUE,
OPERATOR_HAS,
OPERATOR_LABEL,
OPERATOR_LIMIT,
OPERATOR_LIST,
OPERATOR_MEMBER,
OPERATOR_MODIFIED_AT,
OPERATOR_SORT,
OPERATOR_STATUS,
OPERATOR_SWIMLANE,
OPERATOR_UNKNOWN,
OPERATOR_USER,
ORDER_ASCENDING,
ORDER_DESCENDING,
PREDICATE_ALL,
PREDICATE_ARCHIVED,
PREDICATE_ASSIGNEES,
PREDICATE_ATTACHMENT,
PREDICATE_CHECKLIST,
PREDICATE_CREATED_AT,
PREDICATE_DESCRIPTION,
PREDICATE_DUE_AT,
PREDICATE_END_AT,
PREDICATE_ENDED,
PREDICATE_MEMBERS,
PREDICATE_MODIFIED_AT,
PREDICATE_MONTH,
PREDICATE_OPEN,
PREDICATE_OVERDUE,
PREDICATE_PRIVATE,
PREDICATE_PUBLIC,
PREDICATE_QUARTER,
PREDICATE_START_AT,
PREDICATE_WEEK,
PREDICATE_YEAR,
} from '../../../config/search-const';
import { QueryErrors, QueryParams } from '../../../config/query-classes';
import { Query, QueryErrors } from '../../../config/query-classes';
// const subManager = new SubsManager();
@ -62,24 +20,6 @@ Template.globalSearch.helpers({
},
});
BlazeComponent.extendComponent({
events() {
return [
{
'click .js-due-cards-view-me'() {
Utils.setDueCardsView('me');
Popup.close();
},
'click .js-due-cards-view-all'() {
Utils.setDueCardsView('all');
Popup.close();
},
},
];
},
}).register('globalSearchViewChangePopup');
class GlobalSearchComponent extends CardSearchPagedComponent {
onCreated() {
super.onCreated();
@ -87,7 +27,6 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
this.myLabelNames = new ReactiveVar([]);
this.myBoardNames = new ReactiveVar([]);
this.parsingErrors = new QueryErrors();
this.colorMap = null;
this.queryParams = null;
Meteor.call('myLists', (err, data) => {
@ -114,9 +53,6 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
// eslint-disable-next-line no-console
//console.log('lang:', TAPi18n.getLanguage());
this.colorMap = Boards.colorMap();
// eslint-disable-next-line no-console
// console.log('colorMap:', this.colorMap);
if (Session.get('globalQuery')) {
this.searchAllBoards(Session.get('globalQuery'));
@ -139,327 +75,38 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
this.parsingErrors.errorMessages();
}
searchAllBoards(query) {
query = query.trim();
searchAllBoards(queryText) {
queryText = queryText.trim();
// eslint-disable-next-line no-console
//console.log('query:', query);
//console.log('queryText:', queryText);
this.query.set(query);
this.query.set(queryText);
this.resetSearch();
if (!query) {
if (!queryText) {
return;
}
this.searching.set(true);
const reOperator1 = new RegExp(
'^((?<operator>[\\p{Letter}\\p{Mark}]+):|(?<abbrev>[#@]))(?<value>[\\p{Letter}\\p{Mark}]+)(\\s+|$)',
'iu',
);
const reOperator2 = new RegExp(
'^((?<operator>[\\p{Letter}\\p{Mark}]+):|(?<abbrev>[#@]))(?<quote>["\']*)(?<value>.*?)\\k<quote>(\\s+|$)',
'iu',
);
const reText = new RegExp('^(?<text>\\S+)(\\s+|$)', 'u');
const reQuotedText = new RegExp(
'^(?<quote>["\'])(?<text>.*?)\\k<quote>(\\s+|$)',
'u',
);
const reNegatedOperator = new RegExp('^-(?<operator>.*)$');
const operators = {
'operator-board': OPERATOR_BOARD,
'operator-board-abbrev': OPERATOR_BOARD,
'operator-swimlane': OPERATOR_SWIMLANE,
'operator-swimlane-abbrev': OPERATOR_SWIMLANE,
'operator-list': OPERATOR_LIST,
'operator-list-abbrev': OPERATOR_LIST,
'operator-label': OPERATOR_LABEL,
'operator-label-abbrev': OPERATOR_LABEL,
'operator-user': OPERATOR_USER,
'operator-user-abbrev': OPERATOR_USER,
'operator-member': OPERATOR_MEMBER,
'operator-member-abbrev': OPERATOR_MEMBER,
'operator-assignee': OPERATOR_ASSIGNEE,
'operator-assignee-abbrev': OPERATOR_ASSIGNEE,
'operator-status': OPERATOR_STATUS,
'operator-due': OPERATOR_DUE,
'operator-created': OPERATOR_CREATED_AT,
'operator-modified': OPERATOR_MODIFIED_AT,
'operator-comment': OPERATOR_COMMENT,
'operator-has': OPERATOR_HAS,
'operator-sort': OPERATOR_SORT,
'operator-limit': OPERATOR_LIMIT,
};
const predicates = {
due: {
'predicate-overdue': PREDICATE_OVERDUE,
},
durations: {
'predicate-week': PREDICATE_WEEK,
'predicate-month': PREDICATE_MONTH,
'predicate-quarter': PREDICATE_QUARTER,
'predicate-year': PREDICATE_YEAR,
},
status: {
'predicate-archived': PREDICATE_ARCHIVED,
'predicate-all': PREDICATE_ALL,
'predicate-open': PREDICATE_OPEN,
'predicate-ended': PREDICATE_ENDED,
'predicate-public': PREDICATE_PUBLIC,
'predicate-private': PREDICATE_PRIVATE,
},
sorts: {
'predicate-due': PREDICATE_DUE_AT,
'predicate-created': PREDICATE_CREATED_AT,
'predicate-modified': PREDICATE_MODIFIED_AT,
},
has: {
'predicate-description': PREDICATE_DESCRIPTION,
'predicate-checklist': PREDICATE_CHECKLIST,
'predicate-attachment': PREDICATE_ATTACHMENT,
'predicate-start': PREDICATE_START_AT,
'predicate-end': PREDICATE_END_AT,
'predicate-due': PREDICATE_DUE_AT,
'predicate-assignee': PREDICATE_ASSIGNEES,
'predicate-member': PREDICATE_MEMBERS,
},
};
const predicateTranslations = {};
Object.entries(predicates).forEach(([category, catPreds]) => {
predicateTranslations[category] = {};
Object.entries(catPreds).forEach(([tag, value]) => {
predicateTranslations[category][TAPi18n.__(tag)] = value;
});
});
// eslint-disable-next-line no-console
// console.log('predicateTranslations:', predicateTranslations);
const operatorMap = {};
Object.entries(operators).forEach(([key, value]) => {
operatorMap[TAPi18n.__(key).toLowerCase()] = value;
});
// eslint-disable-next-line no-console
// console.log('operatorMap:', operatorMap);
const params = new QueryParams();
let text = '';
while (query) {
let m = query.match(reOperator1);
if (!m) {
m = query.match(reOperator2);
if (m) {
query = query.replace(reOperator2, '');
}
} else {
query = query.replace(reOperator1, '');
}
if (m) {
let op;
if (m.groups.operator) {
op = m.groups.operator.toLowerCase();
} else {
op = m.groups.abbrev.toLowerCase();
}
// eslint-disable-next-line no-prototype-builtins
if (operatorMap.hasOwnProperty(op)) {
const operator = operatorMap[op];
let value = m.groups.value;
if (operator === OPERATOR_LABEL) {
if (value in this.colorMap) {
value = this.colorMap[value];
// console.log('found color:', value);
}
} else if (
[OPERATOR_DUE, OPERATOR_CREATED_AT, OPERATOR_MODIFIED_AT].includes(
operator,
)
) {
const days = parseInt(value, 10);
let duration = null;
if (isNaN(days)) {
// duration was specified as text
if (predicateTranslations.durations[value]) {
duration = predicateTranslations.durations[value];
let date = null;
switch (duration) {
case PREDICATE_WEEK:
// eslint-disable-next-line no-case-declarations
const week = moment().week();
if (week === 52) {
date = moment(1, 'W');
date.set('year', date.year() + 1);
} else {
date = moment(week + 1, 'W');
}
break;
case PREDICATE_MONTH:
// eslint-disable-next-line no-case-declarations
const month = moment().month();
// .month() is zero indexed
if (month === 11) {
date = moment(1, 'M');
date.set('year', date.year() + 1);
} else {
date = moment(month + 2, 'M');
}
break;
case PREDICATE_QUARTER:
// eslint-disable-next-line no-case-declarations
const quarter = moment().quarter();
if (quarter === 4) {
date = moment(1, 'Q');
date.set('year', date.year() + 1);
} else {
date = moment(quarter + 1, 'Q');
}
break;
case PREDICATE_YEAR:
date = moment(moment().year() + 1, 'YYYY');
break;
}
if (date) {
value = {
operator: '$lt',
value: date.format('YYYY-MM-DD'),
};
}
} else if (
operator === OPERATOR_DUE &&
value === PREDICATE_OVERDUE
) {
value = {
operator: '$lt',
value: moment().format('YYYY-MM-DD'),
};
} else {
this.parsingErrors.addError(OPERATOR_DUE, {
tag: 'operator-number-expected',
value: { operator: op, value },
});
continue;
}
} else if (operator === OPERATOR_DUE) {
value = {
operator: '$lt',
value: moment(moment().format('YYYY-MM-DD'))
.add(days + 1, duration ? duration : 'days')
.format(),
};
} else {
value = {
operator: '$gte',
value: moment(moment().format('YYYY-MM-DD'))
.subtract(days, duration ? duration : 'days')
.format(),
};
}
} else if (operator === OPERATOR_SORT) {
let negated = false;
const m = value.match(reNegatedOperator);
if (m) {
value = m.groups.operator;
negated = true;
}
if (!predicateTranslations.sorts[value]) {
this.parsingErrors.addError(OPERATOR_SORT, {
tag: 'operator-sort-invalid',
value,
});
continue;
} else {
value = {
name: predicateTranslations.sorts[value],
order: negated ? ORDER_DESCENDING : ORDER_ASCENDING,
};
}
} else if (operator === OPERATOR_STATUS) {
if (!predicateTranslations.status[value]) {
this.parsingErrors.addError(OPERATOR_STATUS, {
tag: 'operator-status-invalid',
value,
});
continue;
} else {
value = predicateTranslations.status[value];
}
} else if (operator === OPERATOR_HAS) {
let negated = false;
const m = value.match(reNegatedOperator);
if (m) {
value = m.groups.operator;
negated = true;
}
if (!predicateTranslations.has[value]) {
this.parsingErrors.addError(OPERATOR_HAS, {
tag: 'operator-has-invalid',
value,
});
continue;
} else {
value = {
field: predicateTranslations.has[value],
exists: !negated,
};
}
} else if (operator === OPERATOR_LIMIT) {
const limit = parseInt(value, 10);
if (isNaN(limit) || limit < 1) {
this.parsingErrors.addError(OPERATOR_LIMIT, {
tag: 'operator-limit-invalid',
value,
});
continue;
} else {
value = limit;
}
}
params.addPredicate(operator, value);
} else {
this.parsingErrors.addError(OPERATOR_UNKNOWN, {
tag: 'operator-unknown-error',
value: op,
});
}
continue;
}
m = query.match(reQuotedText);
if (!m) {
m = query.match(reText);
if (m) {
query = query.replace(reText, '');
}
} else {
query = query.replace(reQuotedText, '');
}
if (m) {
text += (text ? ' ' : '') + m.groups.text;
}
}
const query = new Query();
query.buildParams(queryText);
// eslint-disable-next-line no-console
// console.log('text:', text);
params.text = text;
// console.log('params:', query.getParams());
// eslint-disable-next-line no-console
console.log('params:', params);
this.queryParams = query.getParams();
this.queryParams = params;
if (this.parsingErrors.hasErrors()) {
if (query.hasErrors()) {
this.searching.set(false);
this.queryErrors = this.parsingErrorMessages();
this.queryErrors = query.errors();
this.hasResults.set(true);
this.hasQueryErrors.set(true);
return;
}
this.runGlobalSearch(params.getParams());
this.runGlobalSearch(query.getParams());
}
searchInstructions() {

View file

@ -2,13 +2,45 @@ import {
OPERATOR_ASSIGNEE,
OPERATOR_BOARD,
OPERATOR_COMMENT,
OPERATOR_CREATED_AT,
OPERATOR_DUE,
OPERATOR_HAS,
OPERATOR_LABEL,
OPERATOR_LIMIT,
OPERATOR_LIST,
OPERATOR_MEMBER,
OPERATOR_MODIFIED_AT,
OPERATOR_SORT,
OPERATOR_STATUS,
OPERATOR_SWIMLANE,
OPERATOR_UNKNOWN,
OPERATOR_USER,
ORDER_ASCENDING,
ORDER_DESCENDING,
PREDICATE_ALL,
PREDICATE_ARCHIVED,
PREDICATE_ASSIGNEES,
PREDICATE_ATTACHMENT,
PREDICATE_CHECKLIST,
PREDICATE_CREATED_AT,
PREDICATE_DESCRIPTION,
PREDICATE_DUE_AT,
PREDICATE_END_AT,
PREDICATE_ENDED,
PREDICATE_MEMBERS,
PREDICATE_MODIFIED_AT,
PREDICATE_MONTH,
PREDICATE_OPEN,
PREDICATE_OVERDUE,
PREDICATE_PRIVATE,
PREDICATE_PUBLIC,
PREDICATE_QUARTER,
PREDICATE_START_AT,
PREDICATE_WEEK,
PREDICATE_YEAR,
} from './search-const';
import Boards from '../models/boards';
import moment from 'moment';
export class QueryParams {
text = '';
@ -106,7 +138,8 @@ export class QueryErrors {
errors() {
const errs = [];
Object.entries(this._errors).forEach(([operator, errors]) => {
// eslint-disable-next-line no-unused-vars
Object.entries(this._errors).forEach(([, errors]) => {
errors.forEach(err => {
errs.push(err);
});
@ -116,7 +149,8 @@ export class QueryErrors {
errorMessages() {
const messages = [];
Object.entries(this._errors).forEach(([operator, errors]) => {
// eslint-disable-next-line no-unused-vars
Object.entries(this._errors).forEach(([, errors]) => {
errors.forEach(err => {
messages.push(TAPi18n.__(err.tag, err.value));
});
@ -126,12 +160,14 @@ export class QueryErrors {
}
export class Query {
params = {};
selector = {};
projection = {};
constructor(selector, projection) {
this._errors = new QueryErrors();
this.queryParams = new QueryParams();
this.colorMap = Boards.colorMap();
if (selector) {
this.selector = selector;
}
@ -152,4 +188,312 @@ export class Query {
errorMessages() {
return this._errors.errorMessages();
}
getParams() {
return this.queryParams.getParams();
}
buildParams(queryText) {
queryText = queryText.trim();
// eslint-disable-next-line no-console
//console.log('query:', query);
if (!queryText) {
return;
}
const reOperator1 = new RegExp(
'^((?<operator>[\\p{Letter}\\p{Mark}]+):|(?<abbrev>[#@]))(?<value>[\\p{Letter}\\p{Mark}]+)(\\s+|$)',
'iu',
);
const reOperator2 = new RegExp(
'^((?<operator>[\\p{Letter}\\p{Mark}]+):|(?<abbrev>[#@]))(?<quote>["\']*)(?<value>.*?)\\k<quote>(\\s+|$)',
'iu',
);
const reText = new RegExp('^(?<text>\\S+)(\\s+|$)', 'u');
const reQuotedText = new RegExp(
'^(?<quote>["\'])(?<text>.*?)\\k<quote>(\\s+|$)',
'u',
);
const reNegatedOperator = new RegExp('^-(?<operator>.*)$');
const operators = {
'operator-board': OPERATOR_BOARD,
'operator-board-abbrev': OPERATOR_BOARD,
'operator-swimlane': OPERATOR_SWIMLANE,
'operator-swimlane-abbrev': OPERATOR_SWIMLANE,
'operator-list': OPERATOR_LIST,
'operator-list-abbrev': OPERATOR_LIST,
'operator-label': OPERATOR_LABEL,
'operator-label-abbrev': OPERATOR_LABEL,
'operator-user': OPERATOR_USER,
'operator-user-abbrev': OPERATOR_USER,
'operator-member': OPERATOR_MEMBER,
'operator-member-abbrev': OPERATOR_MEMBER,
'operator-assignee': OPERATOR_ASSIGNEE,
'operator-assignee-abbrev': OPERATOR_ASSIGNEE,
'operator-status': OPERATOR_STATUS,
'operator-due': OPERATOR_DUE,
'operator-created': OPERATOR_CREATED_AT,
'operator-modified': OPERATOR_MODIFIED_AT,
'operator-comment': OPERATOR_COMMENT,
'operator-has': OPERATOR_HAS,
'operator-sort': OPERATOR_SORT,
'operator-limit': OPERATOR_LIMIT,
};
const predicates = {
due: {
'predicate-overdue': PREDICATE_OVERDUE,
},
durations: {
'predicate-week': PREDICATE_WEEK,
'predicate-month': PREDICATE_MONTH,
'predicate-quarter': PREDICATE_QUARTER,
'predicate-year': PREDICATE_YEAR,
},
status: {
'predicate-archived': PREDICATE_ARCHIVED,
'predicate-all': PREDICATE_ALL,
'predicate-open': PREDICATE_OPEN,
'predicate-ended': PREDICATE_ENDED,
'predicate-public': PREDICATE_PUBLIC,
'predicate-private': PREDICATE_PRIVATE,
},
sorts: {
'predicate-due': PREDICATE_DUE_AT,
'predicate-created': PREDICATE_CREATED_AT,
'predicate-modified': PREDICATE_MODIFIED_AT,
},
has: {
'predicate-description': PREDICATE_DESCRIPTION,
'predicate-checklist': PREDICATE_CHECKLIST,
'predicate-attachment': PREDICATE_ATTACHMENT,
'predicate-start': PREDICATE_START_AT,
'predicate-end': PREDICATE_END_AT,
'predicate-due': PREDICATE_DUE_AT,
'predicate-assignee': PREDICATE_ASSIGNEES,
'predicate-member': PREDICATE_MEMBERS,
},
};
const predicateTranslations = {};
Object.entries(predicates).forEach(([category, catPreds]) => {
predicateTranslations[category] = {};
Object.entries(catPreds).forEach(([tag, value]) => {
predicateTranslations[category][TAPi18n.__(tag)] = value;
});
});
// eslint-disable-next-line no-console
// console.log('predicateTranslations:', predicateTranslations);
const operatorMap = {};
Object.entries(operators).forEach(([key, value]) => {
operatorMap[TAPi18n.__(key).toLowerCase()] = value;
});
// eslint-disable-next-line no-console
// console.log('operatorMap:', operatorMap);
let text = '';
while (queryText) {
let m = queryText.match(reOperator1);
if (!m) {
m = queryText.match(reOperator2);
if (m) {
queryText = queryText.replace(reOperator2, '');
}
} else {
queryText = queryText.replace(reOperator1, '');
}
if (m) {
let op;
if (m.groups.operator) {
op = m.groups.operator.toLowerCase();
} else {
op = m.groups.abbrev.toLowerCase();
}
// eslint-disable-next-line no-prototype-builtins
if (operatorMap.hasOwnProperty(op)) {
const operator = operatorMap[op];
let value = m.groups.value;
if (operator === OPERATOR_LABEL) {
if (value in this.colorMap) {
value = this.colorMap[value];
// console.log('found color:', value);
}
} else if (
[OPERATOR_DUE, OPERATOR_CREATED_AT, OPERATOR_MODIFIED_AT].includes(
operator,
)
) {
const days = parseInt(value, 10);
let duration = null;
if (isNaN(days)) {
// duration was specified as text
if (predicateTranslations.durations[value]) {
duration = predicateTranslations.durations[value];
let date = null;
switch (duration) {
case PREDICATE_WEEK:
// eslint-disable-next-line no-case-declarations
const week = moment().week();
if (week === 52) {
date = moment(1, 'W');
date.set('year', date.year() + 1);
} else {
date = moment(week + 1, 'W');
}
break;
case PREDICATE_MONTH:
// eslint-disable-next-line no-case-declarations
const month = moment().month();
// .month() is zero indexed
if (month === 11) {
date = moment(1, 'M');
date.set('year', date.year() + 1);
} else {
date = moment(month + 2, 'M');
}
break;
case PREDICATE_QUARTER:
// eslint-disable-next-line no-case-declarations
const quarter = moment().quarter();
if (quarter === 4) {
date = moment(1, 'Q');
date.set('year', date.year() + 1);
} else {
date = moment(quarter + 1, 'Q');
}
break;
case PREDICATE_YEAR:
date = moment(moment().year() + 1, 'YYYY');
break;
}
if (date) {
value = {
operator: '$lt',
value: date.format('YYYY-MM-DD'),
};
}
} else if (
operator === OPERATOR_DUE &&
value === PREDICATE_OVERDUE
) {
value = {
operator: '$lt',
value: moment().format('YYYY-MM-DD'),
};
} else {
this.errors.addError(OPERATOR_DUE, {
tag: 'operator-number-expected',
value: { operator: op, value },
});
continue;
}
} else if (operator === OPERATOR_DUE) {
value = {
operator: '$lt',
value: moment(moment().format('YYYY-MM-DD'))
.add(days + 1, duration ? duration : 'days')
.format(),
};
} else {
value = {
operator: '$gte',
value: moment(moment().format('YYYY-MM-DD'))
.subtract(days, duration ? duration : 'days')
.format(),
};
}
} else if (operator === OPERATOR_SORT) {
let negated = false;
const m = value.match(reNegatedOperator);
if (m) {
value = m.groups.operator;
negated = true;
}
if (!predicateTranslations.sorts[value]) {
this.errors.addError(OPERATOR_SORT, {
tag: 'operator-sort-invalid',
value,
});
continue;
} else {
value = {
name: predicateTranslations.sorts[value],
order: negated ? ORDER_DESCENDING : ORDER_ASCENDING,
};
}
} else if (operator === OPERATOR_STATUS) {
if (!predicateTranslations.status[value]) {
this.errors.addError(OPERATOR_STATUS, {
tag: 'operator-status-invalid',
value,
});
continue;
} else {
value = predicateTranslations.status[value];
}
} else if (operator === OPERATOR_HAS) {
let negated = false;
const m = value.match(reNegatedOperator);
if (m) {
value = m.groups.operator;
negated = true;
}
if (!predicateTranslations.has[value]) {
this.errors.addError(OPERATOR_HAS, {
tag: 'operator-has-invalid',
value,
});
continue;
} else {
value = {
field: predicateTranslations.has[value],
exists: !negated,
};
}
} else if (operator === OPERATOR_LIMIT) {
const limit = parseInt(value, 10);
if (isNaN(limit) || limit < 1) {
this.errors.addError(OPERATOR_LIMIT, {
tag: 'operator-limit-invalid',
value,
});
continue;
} else {
value = limit;
}
}
this.queryParams.addPredicate(operator, value);
} else {
this.errors.addError(OPERATOR_UNKNOWN, {
tag: 'operator-unknown-error',
value: op,
});
}
continue;
}
m = queryText.match(reQuotedText);
if (!m) {
m = queryText.match(reText);
if (m) {
queryText = queryText.replace(reText, '');
}
} else {
queryText = queryText.replace(reQuotedText, '');
}
if (m) {
text += (text ? ' ' : '') + m.groups.text;
}
}
// eslint-disable-next-line no-console
// console.log('text:', text);
this.queryParams.text = text;
// eslint-disable-next-line no-console
console.log('queryParams:', this.queryParams);
}
}

View file

@ -1,7 +1,9 @@
import moment from 'moment';
import Users from '../../models/users';
import Boards from '../../models/boards';
import Lists from '../../models/lists';
import Swimlanes from '../../models/swimlanes';
import Cards from '../../models/cards';
import CardComments from '../../models/cardComments';
import Attachments from '../../models/attachments';
import Checklists from '../../models/checklists';
@ -411,26 +413,26 @@ function buildSelector(queryParams) {
const attachments = Attachments.find({ 'original.name': regex });
// const comments = CardComments.find(
// { text: regex },
// { fields: { cardId: 1 } },
// );
const comments = CardComments.find(
{ text: regex },
{ fields: { cardId: 1 } },
);
selector.$and.push({
$or: [
{ title: regex },
{ description: regex },
{ customFields: { $elemMatch: { value: regex } } },
{
_id: {
$in: CardComments.textSearch(userId, [queryParams.text]).map(
com => com.cardId,
),
},
},
// {
// _id: {
// $in: CardComments.textSearch(userId, [queryParams.text]).map(
// com => com.cardId,
// ),
// },
// },
{ _id: { $in: checklists.map(list => list.cardId) } },
{ _id: { $in: attachments.map(attach => attach.cardId) } },
// { _id: { $in: comments.map(com => com.cardId) } },
{ _id: { $in: comments.map(com => com.cardId) } },
],
});
}