Sfoglia il codice sorgente

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
John R. Supplee 4 anni fa
parent
commit
158a0807d9

+ 3 - 0
client/components/cards/resultCard.styl

@@ -19,3 +19,6 @@
 
 .result-card-context-list
   margin-bottom: 0.7rem
+
+.result-card-block-wrapper
+  display: inline-block

+ 1 - 1
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

+ 35 - 27
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 = /^((?<operator>\w+):|(?<abbrev>[#@]))(?<value>\w+)(\s+|$)/;
     const reOperator2 = /^((?<operator>\w+):|(?<abbrev>[#@]))(?<quote>["']*)(?<value>.*?)\k<quote>(\s+|$)/;
     const reText = /^(?<text>\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;
     }

+ 8 - 0
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.",

+ 4 - 1
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;
 };
 

+ 0 - 351
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) {

+ 29 - 0
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

+ 371 - 28
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 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;
+    }
 
-  const results = Cards.globalSearch(queryParams);
-  const cards = results.cards;
+    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,
-  };
-  // 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);
+    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 }),
+    ];
   }
 
-  return cursors;
+  return [SessionData.find({ userId: this.userId, sessionId })];
 });
 
 Meteor.publish('brokenCards', function() {