diff --git a/client/components/main/globalSearch.jade b/client/components/main/globalSearch.jade index 31e9584e4..3d58b96c0 100644 --- a/client/components/main/globalSearch.jade +++ b/client/components/main/globalSearch.jade @@ -42,6 +42,16 @@ template(name="globalSearch") autofocus dir="auto" ) a.js-new-search.fa.fa-eraser + if debug.get.show + h1 Debug + if debug.get.showSelector + h2 Selector + pre + = sessionData.selector + if debug.get.showProjection + h2 Projection + pre + = sessionData.projection if searching.get +spinner else if hasResults.get diff --git a/client/lib/cardSearch.js b/client/lib/cardSearch.js index 6daada3c8..7fe74492b 100644 --- a/client/lib/cardSearch.js +++ b/client/lib/cardSearch.js @@ -1,5 +1,7 @@ import Cards from '../../models/cards'; import SessionData from '../../models/usersessiondata'; +import {QueryDebug} from "../../config/query-classes"; +import {OPERATOR_DEBUG} from "../../config/search-const"; export class CardSearchPagedComponent extends BlazeComponent { onCreated() { @@ -19,6 +21,8 @@ export class CardSearchPagedComponent extends BlazeComponent { this.sessionId = SessionData.getSessionId(); this.subscriptionHandle = null; this.serverError = new ReactiveVar(false); + this.sessionData = null; + this.debug = new ReactiveVar(new QueryDebug()); const that = this; this.subscriptionCallbacks = { @@ -52,6 +56,7 @@ export class CardSearchPagedComponent extends BlazeComponent { this.resultsCount = 0; this.totalHits = 0; this.queryErrors = null; + this.debug.set(new QueryDebug()); } getSessionData(sessionId) { @@ -63,30 +68,34 @@ export class CardSearchPagedComponent extends BlazeComponent { getResults() { // eslint-disable-next-line no-console // console.log('getting results'); - const sessionData = this.getSessionData(); + this.sessionData = this.getSessionData(); // eslint-disable-next-line no-console - console.log('session data:', sessionData); + console.log('session data:', this.sessionData); const cards = []; - sessionData.cards.forEach(cardId => { + this.sessionData.cards.forEach(cardId => { cards.push(Cards.findOne({ _id: cardId })); }); - this.queryErrors = sessionData.errors; + this.queryErrors = this.sessionData.errors; if (this.queryErrors.length) { // console.log('queryErrors:', this.queryErrorMessages()); this.hasQueryErrors.set(true); // return null; } + this.debug.set(new QueryDebug(this.sessionData.debug)); + console.log('debug:', this.debug.get().get()); + console.log('debug.show():', this.debug.get().show()); + console.log('debug.showSelector():', this.debug.get().showSelector()); if (cards) { - this.totalHits = sessionData.totalHits; + this.totalHits = this.sessionData.totalHits; this.resultsCount = cards.length; - this.resultsStart = sessionData.lastHit - this.resultsCount + 1; - this.resultsEnd = sessionData.lastHit; + this.resultsStart = this.sessionData.lastHit - this.resultsCount + 1; + this.resultsEnd = this.sessionData.lastHit; this.resultsHeading.set(this.getResultsHeading()); this.results.set(cards); - this.hasNextPage.set(sessionData.lastHit < sessionData.totalHits); + this.hasNextPage.set(this.sessionData.lastHit < this.sessionData.totalHits); this.hasPreviousPage.set( - sessionData.lastHit - sessionData.resultsCount > 0, + this.sessionData.lastHit - this.sessionData.resultsCount > 0, ); return cards; } @@ -113,6 +122,7 @@ export class CardSearchPagedComponent extends BlazeComponent { runGlobalSearch(queryParams) { this.searching.set(true); + this.debug.set(new QueryDebug()); this.stopSubscription(); this.subscriptionHandle = this.getSubscription(queryParams); } diff --git a/config/query-classes.js b/config/query-classes.js index 32b0ff58e..cc7502e99 100644 --- a/config/query-classes.js +++ b/config/query-classes.js @@ -4,6 +4,7 @@ import { OPERATOR_COMMENT, OPERATOR_CREATED_AT, OPERATOR_CREATOR, + OPERATOR_DEBUG, OPERATOR_DUE, OPERATOR_HAS, OPERATOR_LABEL, @@ -34,8 +35,10 @@ import { PREDICATE_OPEN, PREDICATE_OVERDUE, PREDICATE_PRIVATE, + PREDICATE_PROJECTION, PREDICATE_PUBLIC, PREDICATE_QUARTER, + PREDICATE_SELECTOR, PREDICATE_START_AT, PREDICATE_WEEK, PREDICATE_YEAR, @@ -43,6 +46,46 @@ import { import Boards from '../models/boards'; import moment from 'moment'; +export class QueryDebug { + predicate = null; + + constructor(predicate) { + if (predicate) { + this.set(predicate) + } + } + + get() { + return this.predicate; + } + + set(predicate) { + if ([PREDICATE_ALL, PREDICATE_SELECTOR, PREDICATE_PROJECTION].includes( + predicate + )) { + this.predicate = predicate; + } else { + this.predicate = null; + } + } + + show() { + return (this.predicate !== null); + } + + showAll() { + return (this.predicate === PREDICATE_ALL); + } + + showSelector() { + return (this.predicate === PREDICATE_ALL || this.predicate === PREDICATE_SELECTOR); + } + + showProjection() { + return (this.predicate === PREDICATE_ALL || this.predicate === PREDICATE_PROJECTION); + } +} + export class QueryParams { text = ''; @@ -196,6 +239,10 @@ export class Query { return this._errors.errors(); } + addError(operator, error) { + this._errors.addError(operator, error) + } + errorMessages() { return this._errors.errorMessages(); } @@ -213,6 +260,8 @@ export class Query { } buildParams(queryText) { + this.queryParams = new QueryParams(); + queryText = queryText.trim(); // eslint-disable-next-line no-console //console.log('query:', query); @@ -260,42 +309,49 @@ export class Query { 'operator-has': OPERATOR_HAS, 'operator-sort': OPERATOR_SORT, 'operator-limit': OPERATOR_LIMIT, + 'operator-debug': OPERATOR_DEBUG, }; 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, - }, }; + predicates[OPERATOR_DUE] = { + 'predicate-overdue': PREDICATE_OVERDUE, + }; + predicates[OPERATOR_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, + }; + predicates[OPERATOR_SORT] = { + 'predicate-due': PREDICATE_DUE_AT, + 'predicate-created': PREDICATE_CREATED_AT, + 'predicate-modified': PREDICATE_MODIFIED_AT, + }; + predicates[OPERATOR_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, + }; + predicates[OPERATOR_DEBUG] = { + 'predicate-all': PREDICATE_ALL, + 'predicate-selector': PREDICATE_SELECTOR, + 'predicate-projection': PREDICATE_PROJECTION, + }; + const predicateTranslations = {}; Object.entries(predicates).forEach(([category, catPreds]) => { predicateTranslations[category] = {}; @@ -403,7 +459,7 @@ export class Query { value: moment().format('YYYY-MM-DD'), }; } else { - this.errors.addError(OPERATOR_DUE, { + this.addError(OPERATOR_DUE, { tag: 'operator-number-expected', value: { operator: op, value }, }); @@ -431,27 +487,27 @@ export class Query { value = m.groups.operator; negated = true; } - if (!predicateTranslations.sorts[value]) { - this.errors.addError(OPERATOR_SORT, { + if (!predicateTranslations[OPERATOR_SORT][value]) { + this.addError(OPERATOR_SORT, { tag: 'operator-sort-invalid', value, }); continue; } else { value = { - name: predicateTranslations.sorts[value], + name: predicateTranslations[OPERATOR_SORT][value], order: negated ? ORDER_DESCENDING : ORDER_ASCENDING, }; } } else if (operator === OPERATOR_STATUS) { - if (!predicateTranslations.status[value]) { - this.errors.addError(OPERATOR_STATUS, { + if (!predicateTranslations[OPERATOR_STATUS][value]) { + this.addError(OPERATOR_STATUS, { tag: 'operator-status-invalid', value, }); continue; } else { - value = predicateTranslations.status[value]; + value = predicateTranslations[OPERATOR_STATUS][value]; } } else if (operator === OPERATOR_HAS) { let negated = false; @@ -460,22 +516,22 @@ export class Query { value = m.groups.operator; negated = true; } - if (!predicateTranslations.has[value]) { - this.errors.addError(OPERATOR_HAS, { + if (!predicateTranslations[OPERATOR_HAS][value]) { + this.addError(OPERATOR_HAS, { tag: 'operator-has-invalid', value, }); continue; } else { value = { - field: predicateTranslations.has[value], + field: predicateTranslations[OPERATOR_HAS][value], exists: !negated, }; } } else if (operator === OPERATOR_LIMIT) { const limit = parseInt(value, 10); if (isNaN(limit) || limit < 1) { - this.errors.addError(OPERATOR_LIMIT, { + this.addError(OPERATOR_LIMIT, { tag: 'operator-limit-invalid', value, }); @@ -483,11 +539,21 @@ export class Query { } else { value = limit; } + } else if (operator === OPERATOR_DEBUG) { + if (!predicateTranslations[OPERATOR_DEBUG][value]) { + this.addError(OPERATOR_DEBUG, { + tag: 'operator-debug-invalid', + value, + }); + continue; + } else { + value = predicateTranslations[OPERATOR_DEBUG][value]; + } } this.queryParams.addPredicate(operator, value); } else { - this.errors.addError(OPERATOR_UNKNOWN, { + this.addError(OPERATOR_UNKNOWN, { tag: 'operator-unknown-error', value: op, }); @@ -509,11 +575,13 @@ export class Query { } } - // 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); + if (this.queryParams.hasOperator(OPERATOR_DEBUG)) { + // eslint-disable-next-line no-console + console.log('text:', this.queryParams.text); + console.log('queryParams:', this.queryParams); + } } } diff --git a/config/search-const.js b/config/search-const.js index 5a7f54b6b..1b8b5b27c 100644 --- a/config/search-const.js +++ b/config/search-const.js @@ -3,6 +3,7 @@ export const OPERATOR_ASSIGNEE = 'assignees'; export const OPERATOR_COMMENT = 'comment'; export const OPERATOR_CREATED_AT = 'createdAt'; export const OPERATOR_CREATOR = 'userId'; +export const OPERATOR_DEBUG = 'debug'; export const OPERATOR_DUE = 'dueAt'; export const OPERATOR_BOARD = 'board'; export const OPERATOR_HAS = 'has'; @@ -33,9 +34,11 @@ export const PREDICATE_MODIFIED_AT = 'modifiedAt'; export const PREDICATE_MONTH = 'month'; export const PREDICATE_OPEN = 'open'; export const PREDICATE_OVERDUE = 'overdue'; +export const PREDICATE_PROJECTION = 'projection'; export const PREDICATE_PRIVATE = 'private'; export const PREDICATE_PUBLIC = 'public'; export const PREDICATE_QUARTER = 'quarter'; +export const PREDICATE_SELECTOR = 'selector'; export const PREDICATE_START_AT = 'startAt'; export const PREDICATE_SYSTEM = 'system'; export const PREDICATE_WEEK = 'week'; diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index 0a0eb7b27..5bbc902de 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -971,6 +971,7 @@ "operator-comment": "comment", "operator-has": "has", "operator-limit": "limit", + "operator-debug": "debug", "predicate-archived": "archived", "predicate-open": "open", "predicate-ended": "ended", @@ -992,12 +993,15 @@ "predicate-member": "member", "predicate-public": "public", "predicate-private": "private", + "predicate-selector": "selector", + "predicate-projection": "projection", "operator-unknown-error": "%s is not an operator", "operator-number-expected": "operator __operator__ expected a number, got '__value__'", "operator-sort-invalid": "sort of '%s' is invalid", "operator-status-invalid": "'%s' is not a valid status", "operator-has-invalid": "%s is not a valid existence check", "operator-limit-invalid": "%s is not a valid limit. Limit should be a positive integer.", + "operator-debug-invalid": "%s is not a valid debug predicate", "next-page": "Next Page", "previous-page": "Previous Page", "heading-notes": "Notes", diff --git a/models/usersessiondata.js b/models/usersessiondata.js index 7d7189ca1..a100574e6 100644 --- a/models/usersessiondata.js +++ b/models/usersessiondata.js @@ -77,6 +77,10 @@ SessionData.attachSchema( optional: true, defaultValue: [], }, + debug: { + type: String, + optional: true, + }, 'errors.$': { type: new SimpleSchema({ tag: { @@ -177,7 +181,7 @@ function unpickleObject(obj) { SessionData.pickle = value => { return JSON.stringify(value, (key, value) => { return pickleValue(value); - }); + }, 2); }; function pickleValue(value) { diff --git a/server/publications/cards.js b/server/publications/cards.js index 8322de09d..03cb34d8a 100644 --- a/server/publications/cards.js +++ b/server/publications/cards.js @@ -16,7 +16,7 @@ import { OPERATOR_BOARD, OPERATOR_COMMENT, OPERATOR_CREATED_AT, - OPERATOR_CREATOR, + OPERATOR_CREATOR, OPERATOR_DEBUG, OPERATOR_DUE, OPERATOR_HAS, OPERATOR_LABEL, @@ -458,7 +458,7 @@ function buildSelector(queryParams) { } // eslint-disable-next-line no-console - //console.log('cards selector:', JSON.stringify(selector, null, 2)); + // console.log('cards selector:', JSON.stringify(selector, null, 2)); const query = new Query(); query.selector = selector; @@ -634,6 +634,7 @@ function findCards(sessionId, query) { selector: SessionData.pickle(query.selector), projection: SessionData.pickle(query.projection), errors: query.errors(), + debug: query.getQueryParams().getPredicate(OPERATOR_DEBUG) }, };