From 158a0807d9b7aaf9f8519175a48c2ce537dcd28b Mon Sep 17 00:00:00 2001 From: "John R. Supplee" Date: Mon, 25 Jan 2021 15:39:36 +0200 Subject: [PATCH] Development * Generate error when a comment text is not found * Save errors to SessionData as objects * Move all search code to globalSearch publication * Add more translation tags --- client/components/cards/resultCard.styl | 3 + client/components/main/globalSearch.jade | 2 +- client/components/main/globalSearch.js | 62 ++-- i18n/en.i18n.json | 8 + models/cardComments.js | 5 +- models/cards.js | 351 -------------------- models/usersessiondata.js | 29 ++ server/publications/cards.js | 401 +++++++++++++++++++++-- 8 files changed, 452 insertions(+), 409 deletions(-) diff --git a/client/components/cards/resultCard.styl b/client/components/cards/resultCard.styl index def39a4d3..7aa94e90f 100644 --- a/client/components/cards/resultCard.styl +++ b/client/components/cards/resultCard.styl @@ -19,3 +19,6 @@ .result-card-context-list margin-bottom: 0.7rem + +.result-card-block-wrapper + display: inline-block diff --git a/client/components/main/globalSearch.jade b/client/components/main/globalSearch.jade index a4215e3d2..896954743 100644 --- a/client/components/main/globalSearch.jade +++ b/client/components/main/globalSearch.jade @@ -28,7 +28,7 @@ template(name="globalSearch") .global-search-results-list-wrapper if hasQueryErrors.get div - each msg in queryErrors + each msg in errorMessages span.global-search-error-messages = msg else diff --git a/client/components/main/globalSearch.js b/client/components/main/globalSearch.js index 3db47a7a8..f842018ee 100644 --- a/client/components/main/globalSearch.js +++ b/client/components/main/globalSearch.js @@ -52,13 +52,6 @@ BlazeComponent.extendComponent({ this.totalHits = 0; this.queryErrors = null; this.colorMap = null; - // this.colorMap = {}; - // for (const color of Boards.simpleSchema()._schema['labels.$.color'] - // .allowedValues) { - // this.colorMap[TAPi18n.__(`color-${color}`)] = color; - // } - // // eslint-disable-next-line no-console - // console.log('colorMap:', this.colorMap); Meteor.call('myLists', (err, data) => { if (!err) { @@ -81,6 +74,15 @@ BlazeComponent.extendComponent({ onRendered() { Meteor.subscribe('setting'); + + this.colorMap = {}; + for (const color of Boards.simpleSchema()._schema['labels.$.color'] + .allowedValues) { + this.colorMap[TAPi18n.__(`color-${color}`)] = color; + } + // // eslint-disable-next-line no-console + // console.log('colorMap:', this.colorMap); + if (Session.get('globalQuery')) { this.searchAllBoards(Session.get('globalQuery')); } @@ -107,17 +109,10 @@ BlazeComponent.extendComponent({ sessionId: SessionData.getSessionId(), }); // eslint-disable-next-line no-console - console.log('session data:', sessionData); + // console.log('session data:', sessionData); const cards = Cards.find({ _id: { $in: sessionData.cards } }); - this.queryErrors = sessionData.errorMessages; - // eslint-disable-next-line no-console - // console.log('errors:', this.queryErrors); - if (this.parsingErrors.length) { - this.queryErrors = this.errorMessages(); - this.hasQueryErrors.set(true); - return null; - } + this.queryErrors = sessionData.errors; if (this.queryErrors.length) { this.hasQueryErrors.set(true); return null; @@ -135,6 +130,13 @@ BlazeComponent.extendComponent({ }, errorMessages() { + if (this.parsingErrors.length) { + return this.parsingErrorMessages(); + } + return this.queryErrorMessages(); + }, + + parsingErrorMessages() { const messages = []; if (this.parsingErrors.length) { @@ -146,6 +148,20 @@ BlazeComponent.extendComponent({ return messages; }, + queryErrorMessages() { + messages = []; + + this.queryErrors.forEach(err => { + let value = err.color ? TAPi18n.__(`color-${err.value}`) : err.value; + if (!value) { + value = err.value; + } + messages.push(TAPi18n.__(err.tag, value)); + }); + + return messages; + }, + searchAllBoards(query) { query = query.trim(); // eslint-disable-next-line no-console @@ -161,14 +177,6 @@ BlazeComponent.extendComponent({ this.searching.set(true); - if (!this.colorMap) { - this.colorMap = {}; - for (const color of Boards.simpleSchema()._schema['labels.$.color'] - .allowedValues) { - this.colorMap[TAPi18n.__(`color-${color}`)] = color; - } - } - const reOperator1 = /^((?\w+):|(?[#@]))(?\w+)(\s+|$)/; const reOperator2 = /^((?\w+):|(?[#@]))(?["']*)(?.*?)\k(\s+|$)/; const reText = /^(?\S+)(\s+|$)/; @@ -200,9 +208,9 @@ BlazeComponent.extendComponent({ Object.entries(operators).forEach(([key, value]) => { operatorMap[TAPi18n.__(key).toLowerCase()] = value; }); - // eslint-disable-next-line no-console // console.log('operatorMap:', operatorMap); + const params = { boards: [], swimlanes: [], @@ -315,13 +323,13 @@ BlazeComponent.extendComponent({ params.text = text; // eslint-disable-next-line no-console - console.log('params:', params); + // console.log('params:', params); this.queryParams = params; if (this.parsingErrors.length) { this.searching.set(false); - this.queryErrors = this.errorMessages(); + this.queryErrors = this.parsingErrorMessages(); this.hasQueryErrors.set(true); return; } diff --git a/i18n/en.i18n.json b/i18n/en.i18n.json index 3ab491ebb..417de5d05 100644 --- a/i18n/en.i18n.json +++ b/i18n/en.i18n.json @@ -876,6 +876,7 @@ "label-not-found": "Label '%s' not found.", "label-color-not-found": "Label color %s not found.", "user-username-not-found": "Username '%s' not found.", + "comment-not-found": "Card with comment containing text '%s' not found.", "globalSearch-title": "Search All Boards", "no-cards-found": "No Cards Found", "one-card-found": "One Card Found", @@ -901,6 +902,9 @@ "operator-modified": "modified", "operator-sort": "sort", "operator-comment": "comment", + "predicate-archived": "archived", + "predicate-active": "active", + "predicate-overdue": "overdue", "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", @@ -911,12 +915,16 @@ "globalSearch-instructions-operator-board": "`__operator_board__:title` - cards in boards matching the specified title", "globalSearch-instructions-operator-list": "`__operator_list__:title` - cards in lists matching the specified title", "globalSearch-instructions-operator-swimlane": "`__operator_swimlane__:title` - cards in swimlanes matching the specified title", + "globalSearch-instructions-operator-comment": "`__operator_comment__:text` - cards with with a comment containing *text*.", "globalSearch-instructions-operator-label": "`__operator_label__:color` `__operator_label__:name` - cards that have a label matching the given color or name", "globalSearch-instructions-operator-hash": "`__operator_label_abbrev__label` - shorthand for `__operator_label__:label`", "globalSearch-instructions-operator-user": "`__operator_user__:username` - cards where the specified user is a *member* or *assignee*", "globalSearch-instructions-operator-at": "`__operator_user_abbrev__username` - shorthand for `user:username`", "globalSearch-instructions-operator-member": "`__operator_member__:username` - cards where the specified user is a *member*", "globalSearch-instructions-operator-assignee": "`__operator_assignee__:username` - cards where the specified user is an *assignee*", + "globalSearch-instructions-operator-due": "`__operator_due__:n` - cards which are due *n* days from now. `__operator_due__:__predicate_overdue__ lists all ", + "globalSearch-instructions-operator-created": "`__operator_created__:n` - cards which which were created *n* days ago", + "globalSearch-instructions-operator-modified": "`__operator_modified__:n` - cards which which were modified *n* days ago", "globalSearch-instructions-notes-1": "Multiple operators may be specified.", "globalSearch-instructions-notes-2": "Similar operators are *OR*ed together. Cards that match any of the conditions will be returned.\n`__operator_list__:Available __operator_list__:Blocked` would return cards contained in any list named *Blocked* or *Available*.", "globalSearch-instructions-notes-3": "Differing operators are *AND*ed together. Only cards that match all of the differing operators are returned.\n`__operator_list__:Available __operator_label__:red` returns only cards in the list *Available* with a *red* label.", diff --git a/models/cardComments.js b/models/cardComments.js index fe49b9161..b366fc57d 100644 --- a/models/cardComments.js +++ b/models/cardComments.js @@ -125,7 +125,10 @@ CardComments.textSearch = (userId, textArray) => { const comments = CardComments.find(selector); // eslint-disable-next-line no-console - console.log('count:', comments.count()); + // console.log('count:', comments.count()); + // eslint-disable-next-line no-console + // console.log('cards with comments:', comments.map(com => { return com.cardId })); + return comments; }; diff --git a/models/cards.js b/models/cards.js index 88f0b0942..74b7b8bfb 100644 --- a/models/cards.js +++ b/models/cards.js @@ -1,5 +1,3 @@ -const escapeForRegex = require('escape-string-regexp'); - Cards = new Mongo.Collection('cards'); // XXX To improve pub/sub performances a card document should include a @@ -1865,355 +1863,6 @@ Cards.mutations({ }, }); -Cards.globalSearch = queryParams => { - const userId = Meteor.userId(); - // eslint-disable-next-line no-console - // console.log('userId:', userId); - - const errors = new (class { - constructor() { - this.notFound = { - boards: [], - swimlanes: [], - lists: [], - labels: [], - users: [], - members: [], - assignees: [], - is: [], - }; - - this.colorMap = {}; - for (const color of Boards.simpleSchema()._schema['labels.$.color'] - .allowedValues) { - this.colorMap[TAPi18n.__(`color-${color}`)] = color; - } - } - - hasErrors() { - for (const prop in this.notFound) { - if (this.notFound[prop].length) { - return true; - } - } - return false; - } - - errorMessages() { - const messages = []; - - this.notFound.boards.forEach(board => { - messages.push(TAPi18n.__('board-title-not-found', board)); - }); - this.notFound.swimlanes.forEach(swim => { - messages.push(TAPi18n.__('swimlane-title-not-found', swim)); - }); - this.notFound.lists.forEach(list => { - messages.push(TAPi18n.__('list-title-not-found', list)); - }); - this.notFound.labels.forEach(label => { - const color = Object.entries(this.colorMap) - .filter(value => value[1] === label) - .map(value => value[0]); - if (color.length) { - messages.push(TAPi18n.__('label-color-not-found', color[0])); - } else { - messages.push(TAPi18n.__('label-not-found', label)); - } - }); - this.notFound.users.forEach(user => { - messages.push(TAPi18n.__('user-username-not-found', user)); - }); - this.notFound.members.forEach(user => { - messages.push(TAPi18n.__('user-username-not-found', user)); - }); - this.notFound.assignees.forEach(user => { - messages.push(TAPi18n.__('user-username-not-found', user)); - }); - - return messages; - } - })(); - - const selector = { - archived: false, - type: 'cardType-card', - boardId: { $in: Boards.userBoardIds(userId) }, - swimlaneId: { $nin: Swimlanes.archivedSwimlaneIds() }, - listId: { $nin: Lists.archivedListIds() }, - }; - - if (queryParams.boards.length) { - const queryBoards = []; - queryParams.boards.forEach(query => { - const boards = Boards.userSearch(userId, { - title: new RegExp(escapeForRegex(query), 'i'), - }); - if (boards.count()) { - boards.forEach(board => { - queryBoards.push(board._id); - }); - } else { - errors.notFound.boards.push(query); - } - }); - - selector.boardId.$in = queryBoards; - } - - if (queryParams.swimlanes.length) { - const querySwimlanes = []; - queryParams.swimlanes.forEach(query => { - const swimlanes = Swimlanes.find({ - title: new RegExp(escapeForRegex(query), 'i'), - }); - if (swimlanes.count()) { - swimlanes.forEach(swim => { - querySwimlanes.push(swim._id); - }); - } else { - errors.notFound.swimlanes.push(query); - } - }); - - selector.swimlaneId.$in = querySwimlanes; - } - - if (queryParams.lists.length) { - const queryLists = []; - queryParams.lists.forEach(query => { - const lists = Lists.find({ - title: new RegExp(escapeForRegex(query), 'i'), - }); - if (lists.count()) { - lists.forEach(list => { - queryLists.push(list._id); - }); - } else { - errors.notFound.lists.push(query); - } - }); - - selector.listId.$in = queryLists; - } - - if (queryParams.comments.length) { - selector._id = { - $in: CardComments.textSearch(userId, queryParams.comments).map(com => { - return com.cardId; - }), - }; - } - - if (queryParams.dueAt !== null) { - selector.dueAt = { $lte: new Date(queryParams.dueAt) }; - } - - if (queryParams.createdAt !== null) { - selector.createdAt = { $gte: new Date(queryParams.createdAt) }; - } - - if (queryParams.modifiedAt !== null) { - selector.modifiedAt = { $gte: new Date(queryParams.modifiedAt) }; - } - - const queryMembers = []; - const queryAssignees = []; - if (queryParams.users.length) { - queryParams.users.forEach(query => { - const users = Users.find({ - username: query, - }); - if (users.count()) { - users.forEach(user => { - queryMembers.push(user._id); - queryAssignees.push(user._id); - }); - } else { - errors.notFound.users.push(query); - } - }); - } - - if (queryParams.members.length) { - queryParams.members.forEach(query => { - const users = Users.find({ - username: query, - }); - if (users.count()) { - users.forEach(user => { - queryMembers.push(user._id); - }); - } else { - errors.notFound.members.push(query); - } - }); - } - - if (queryParams.assignees.length) { - queryParams.assignees.forEach(query => { - const users = Users.find({ - username: query, - }); - if (users.count()) { - users.forEach(user => { - queryAssignees.push(user._id); - }); - } else { - errors.notFound.assignees.push(query); - } - }); - } - - if (queryMembers.length && queryAssignees.length) { - selector.$or = [ - { members: { $in: queryMembers } }, - { assignees: { $in: queryAssignees } }, - ]; - } else if (queryMembers.length) { - selector.members = { $in: queryMembers }; - } else if (queryAssignees.length) { - selector.assignees = { $in: queryAssignees }; - } - - if (queryParams.labels.length) { - queryParams.labels.forEach(label => { - const queryLabels = []; - - let boards = Boards.userSearch(userId, { - labels: { $elemMatch: { color: label.toLowerCase() } }, - }); - - if (boards.count()) { - boards.forEach(board => { - // eslint-disable-next-line no-console - // console.log('board:', board); - // eslint-disable-next-line no-console - // console.log('board.labels:', board.labels); - board.labels - .filter(boardLabel => { - return boardLabel.color === label.toLowerCase(); - }) - .forEach(boardLabel => { - queryLabels.push(boardLabel._id); - }); - }); - } else { - // eslint-disable-next-line no-console - // console.log('label:', label); - const reLabel = new RegExp(escapeForRegex(label), 'i'); - // eslint-disable-next-line no-console - // console.log('reLabel:', reLabel); - boards = Boards.userSearch(userId, { - labels: { $elemMatch: { name: reLabel } }, - }); - - if (boards.count()) { - boards.forEach(board => { - board.labels - .filter(boardLabel => { - return boardLabel.name.match(reLabel); - }) - .forEach(boardLabel => { - queryLabels.push(boardLabel._id); - }); - }); - } else { - errors.notFound.labels.push(label); - } - } - - selector.labelIds = { $in: queryLabels }; - }); - } - - if (errors.hasErrors()) { - return { cards: null, errors }; - } - - if (queryParams.text) { - const regex = new RegExp(escapeForRegex(queryParams.text), 'i'); - - selector.$or = [ - { title: regex }, - { description: regex }, - { customFields: { $elemMatch: { value: regex } } }, - { - _id: { - $in: CardComments.textSearch(userId, [queryParams.text]).map( - com => com.cardId, - ), - }, - }, - ]; - } - - // eslint-disable-next-line no-console - console.log('selector:', selector); - - const projection = { - fields: { - _id: 1, - archived: 1, - boardId: 1, - swimlaneId: 1, - listId: 1, - title: 1, - type: 1, - sort: 1, - members: 1, - assignees: 1, - colors: 1, - dueAt: 1, - createdAt: 1, - modifiedAt: 1, - labelIds: 1, - }, - limit: 50, - }; - - if (queryParams.sort === 'due') { - projection.sort = { - dueAt: 1, - boardId: 1, - swimlaneId: 1, - listId: 1, - sort: 1, - }; - } else if (queryParams.sort === 'modified') { - projection.sort = { - modifiedAt: -1, - boardId: 1, - swimlaneId: 1, - listId: 1, - sort: 1, - }; - } else if (queryParams.sort === 'created') { - projection.sort = { - createdAt: -1, - boardId: 1, - swimlaneId: 1, - listId: 1, - sort: 1, - }; - } else if (queryParams.sort === 'system') { - projection.sort = { - boardId: 1, - swimlaneId: 1, - listId: 1, - modifiedAt: 1, - sort: 1, - }; - } - - const cards = Cards.find(selector, projection); - - // eslint-disable-next-line no-console - console.log('count:', cards.count()); - - return { cards, errors }; -}; - //FUNCTIONS FOR creation of Activities function updateActivities(doc, fieldNames, modifier) { diff --git a/models/usersessiondata.js b/models/usersessiondata.js index 775eacf14..59be52b3a 100644 --- a/models/usersessiondata.js +++ b/models/usersessiondata.js @@ -54,6 +54,35 @@ SessionData.attachSchema( type: [String], optional: true, }, + errors: { + type: [Object], + optional: true, + defaultValue: [], + }, + 'errors.$': { + type: new SimpleSchema({ + tag: { + /** + * i18n tag + */ + type: String, + optional: false, + }, + value: { + /** + * value for the tag + */ + type: String, + optional: true, + defaultValue: null, + }, + color: { + type: Boolean, + optional: true, + defaultValue: false, + }, + }), + }, createdAt: { /** * creation date of the team diff --git a/server/publications/cards.js b/server/publications/cards.js index 838cedbfe..bf152cbd4 100644 --- a/server/publications/cards.js +++ b/server/publications/cards.js @@ -1,3 +1,5 @@ +const escapeForRegex = require('escape-string-regexp'); + Meteor.publish('card', cardId => { check(cardId, String); return Cards.find({ _id: cardId }); @@ -177,18 +179,363 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) { check(sessionId, String); check(queryParams, Object); + const userId = Meteor.userId(); // eslint-disable-next-line no-console - // console.log('queryParams:', queryParams); + // console.log('userId:', userId); - const results = Cards.globalSearch(queryParams); - const cards = results.cards; + const errors = new (class { + constructor() { + this.notFound = { + boards: [], + swimlanes: [], + lists: [], + labels: [], + users: [], + members: [], + assignees: [], + is: [], + comments: [], + }; + + this.colorMap = {}; + for (const color of Boards.simpleSchema()._schema['labels.$.color'] + .allowedValues) { + this.colorMap[TAPi18n.__(`color-${color}`)] = color; + } + } + + hasErrors() { + for (const prop in this.notFound) { + if (this.notFound[prop].length) { + return true; + } + } + return false; + } + + errorMessages() { + const messages = []; + + this.notFound.boards.forEach(board => { + messages.push({ tag: 'board-title-not-found', value: board }); + }); + this.notFound.swimlanes.forEach(swim => { + messages.push({ tag: 'swimlane-title-not-found', value: swim }); + }); + this.notFound.lists.forEach(list => { + messages.push({ tag: 'list-title-not-found', value: list }); + }); + this.notFound.comments.forEach(comments => { + comments.forEach(text => { + messages.push({ tag: 'comment-not-found', value: text }); + }); + }); + this.notFound.labels.forEach(label => { + messages.push({ tag: 'label-not-found', value: label, color: true }); + }); + this.notFound.users.forEach(user => { + messages.push({ tag: 'user-username-not-found', value: user }); + }); + this.notFound.members.forEach(user => { + messages.push({ tag: 'user-username-not-found', value: user }); + }); + this.notFound.assignees.forEach(user => { + messages.push({ tag: 'user-username-not-found', value: user }); + }); + + return messages; + } + })(); + + const selector = { + archived: false, + type: 'cardType-card', + boardId: { $in: Boards.userBoardIds(userId) }, + swimlaneId: { $nin: Swimlanes.archivedSwimlaneIds() }, + listId: { $nin: Lists.archivedListIds() }, + }; + + if (queryParams.boards.length) { + const queryBoards = []; + queryParams.boards.forEach(query => { + const boards = Boards.userSearch(userId, { + title: new RegExp(escapeForRegex(query), 'i'), + }); + if (boards.count()) { + boards.forEach(board => { + queryBoards.push(board._id); + }); + } else { + errors.notFound.boards.push(query); + } + }); + + selector.boardId.$in = queryBoards; + } + + if (queryParams.swimlanes.length) { + const querySwimlanes = []; + queryParams.swimlanes.forEach(query => { + const swimlanes = Swimlanes.find({ + title: new RegExp(escapeForRegex(query), 'i'), + }); + if (swimlanes.count()) { + swimlanes.forEach(swim => { + querySwimlanes.push(swim._id); + }); + } else { + errors.notFound.swimlanes.push(query); + } + }); + + selector.swimlaneId.$in = querySwimlanes; + } + + if (queryParams.lists.length) { + const queryLists = []; + queryParams.lists.forEach(query => { + const lists = Lists.find({ + title: new RegExp(escapeForRegex(query), 'i'), + }); + if (lists.count()) { + lists.forEach(list => { + queryLists.push(list._id); + }); + } else { + errors.notFound.lists.push(query); + } + }); + + selector.listId.$in = queryLists; + } + + if (queryParams.comments.length) { + const cardIds = CardComments.textSearch(userId, queryParams.comments).map( + com => { + return com.cardId; + }, + ); + if (cardIds.length) { + selector._id = { $in: cardIds }; + } else { + errors.notFound.comments.push(queryParams.comments); + } + } + + if (queryParams.dueAt !== null) { + selector.dueAt = { $lte: new Date(queryParams.dueAt) }; + } + + if (queryParams.createdAt !== null) { + selector.createdAt = { $gte: new Date(queryParams.createdAt) }; + } + + if (queryParams.modifiedAt !== null) { + selector.modifiedAt = { $gte: new Date(queryParams.modifiedAt) }; + } + + const queryMembers = []; + const queryAssignees = []; + if (queryParams.users.length) { + queryParams.users.forEach(query => { + const users = Users.find({ + username: query, + }); + if (users.count()) { + users.forEach(user => { + queryMembers.push(user._id); + queryAssignees.push(user._id); + }); + } else { + errors.notFound.users.push(query); + } + }); + } + + if (queryParams.members.length) { + queryParams.members.forEach(query => { + const users = Users.find({ + username: query, + }); + if (users.count()) { + users.forEach(user => { + queryMembers.push(user._id); + }); + } else { + errors.notFound.members.push(query); + } + }); + } + + if (queryParams.assignees.length) { + queryParams.assignees.forEach(query => { + const users = Users.find({ + username: query, + }); + if (users.count()) { + users.forEach(user => { + queryAssignees.push(user._id); + }); + } else { + errors.notFound.assignees.push(query); + } + }); + } + + if (queryMembers.length && queryAssignees.length) { + selector.$or = [ + { members: { $in: queryMembers } }, + { assignees: { $in: queryAssignees } }, + ]; + } else if (queryMembers.length) { + selector.members = { $in: queryMembers }; + } else if (queryAssignees.length) { + selector.assignees = { $in: queryAssignees }; + } + + if (queryParams.labels.length) { + queryParams.labels.forEach(label => { + const queryLabels = []; + + let boards = Boards.userSearch(userId, { + labels: { $elemMatch: { color: label.toLowerCase() } }, + }); + + if (boards.count()) { + boards.forEach(board => { + // eslint-disable-next-line no-console + // console.log('board:', board); + // eslint-disable-next-line no-console + // console.log('board.labels:', board.labels); + board.labels + .filter(boardLabel => { + return boardLabel.color === label.toLowerCase(); + }) + .forEach(boardLabel => { + queryLabels.push(boardLabel._id); + }); + }); + } else { + // eslint-disable-next-line no-console + // console.log('label:', label); + const reLabel = new RegExp(escapeForRegex(label), 'i'); + // eslint-disable-next-line no-console + // console.log('reLabel:', reLabel); + boards = Boards.userSearch(userId, { + labels: { $elemMatch: { name: reLabel } }, + }); + + if (boards.count()) { + boards.forEach(board => { + board.labels + .filter(boardLabel => { + return boardLabel.name.match(reLabel); + }) + .forEach(boardLabel => { + queryLabels.push(boardLabel._id); + }); + }); + } else { + errors.notFound.labels.push(label); + } + } + + selector.labelIds = { $in: queryLabels }; + }); + } + + let cards = null; + + if (!errors.hasErrors()) { + if (queryParams.text) { + const regex = new RegExp(escapeForRegex(queryParams.text), 'i'); + + selector.$or = [ + { title: regex }, + { description: regex }, + { customFields: { $elemMatch: { value: regex } } }, + { + _id: { + $in: CardComments.textSearch(userId, [queryParams.text]).map( + com => com.cardId, + ), + }, + }, + ]; + } + + // eslint-disable-next-line no-console + // console.log('selector:', selector); + // eslint-disable-next-line no-console + // console.log('selector.$or:', selector.$or); + + const projection = { + fields: { + _id: 1, + archived: 1, + boardId: 1, + swimlaneId: 1, + listId: 1, + title: 1, + type: 1, + sort: 1, + members: 1, + assignees: 1, + colors: 1, + dueAt: 1, + createdAt: 1, + modifiedAt: 1, + labelIds: 1, + }, + limit: 50, + }; + + if (queryParams.sort === 'due') { + projection.sort = { + dueAt: 1, + boardId: 1, + swimlaneId: 1, + listId: 1, + sort: 1, + }; + } else if (queryParams.sort === 'modified') { + projection.sort = { + modifiedAt: -1, + boardId: 1, + swimlaneId: 1, + listId: 1, + sort: 1, + }; + } else if (queryParams.sort === 'created') { + projection.sort = { + createdAt: -1, + boardId: 1, + swimlaneId: 1, + listId: 1, + sort: 1, + }; + } else if (queryParams.sort === 'system') { + projection.sort = { + boardId: 1, + swimlaneId: 1, + listId: 1, + modifiedAt: 1, + sort: 1, + }; + } + + cards = Cards.find(selector, projection); + + // eslint-disable-next-line no-console + // console.log('count:', cards.count()); + } const update = { $set: { totalHits: 0, lastHit: 0, cards: [], - errorMessages: results.errors.errorMessages(), + errors: errors.errorMessages(), }, }; @@ -202,12 +549,12 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) { SessionData.upsert({ userId: this.userId, sessionId }, update); - const boards = []; - const swimlanes = []; - const lists = []; - const users = [this.userId]; - if (cards) { + const boards = []; + const swimlanes = []; + const lists = []; + const users = [this.userId]; + cards.forEach(card => { if (card.boardId) boards.push(card.boardId); if (card.swimlaneId) swimlanes.push(card.swimlaneId); @@ -223,28 +570,24 @@ Meteor.publish('globalSearch', function(sessionId, queryParams) { }); } }); + + const fields = { + _id: 1, + title: 1, + archived: 1, + }; + + return [ + cards, + Boards.find({ _id: { $in: boards } }, { fields }), + Swimlanes.find({ _id: { $in: swimlanes } }, { fields }), + Lists.find({ _id: { $in: lists } }, { fields }), + Users.find({ _id: { $in: users } }, { fields: Users.safeFields }), + SessionData.find({ userId: this.userId, sessionId }), + ]; } - const fields = { - _id: 1, - title: 1, - archived: 1, - }; - // eslint-disable-next-line no-console - // console.log('users:', users); - const cursors = [ - Boards.find({ _id: { $in: boards } }, { fields }), - Swimlanes.find({ _id: { $in: swimlanes } }, { fields }), - Lists.find({ _id: { $in: lists } }, { fields }), - Users.find({ _id: { $in: users } }, { fields: Users.safeFields }), - SessionData.find({ userId: this.userId, sessionId }), - ]; - - if (cards) { - cursors.push(cards); - } - - return cursors; + return [SessionData.find({ userId: this.userId, sessionId })]; }); Meteor.publish('brokenCards', function() {