Browse Source

More integration with constants and query classes

John R. Supplee 4 years ago
parent
commit
097cae1f8c

+ 2 - 2
client/components/main/globalSearch.jade

@@ -44,9 +44,9 @@ template(name="globalSearch")
       else if hasResults.get
       else if hasResults.get
         .global-search-results-list-wrapper
         .global-search-results-list-wrapper
           if hasQueryErrors.get
           if hasQueryErrors.get
-            div
+            ul
               each msg in errorMessages
               each msg in errorMessages
-                span.global-search-error-messages
+                li.global-search-error-messages
                   = msg
                   = msg
           else
           else
             +resultsPaged(this)
             +resultsPaged(this)

+ 37 - 53
client/components/main/globalSearch.js

@@ -1,16 +1,22 @@
 import { CardSearchPagedComponent } from '../../lib/cardSearch';
 import { CardSearchPagedComponent } from '../../lib/cardSearch';
+import Boards from '../../../models/boards';
 import moment from 'moment';
 import moment from 'moment';
 import {
 import {
   OPERATOR_ASSIGNEE,
   OPERATOR_ASSIGNEE,
   OPERATOR_BOARD,
   OPERATOR_BOARD,
+  OPERATOR_COMMENT,
+  OPERATOR_CREATED_AT,
   OPERATOR_DUE,
   OPERATOR_DUE,
   OPERATOR_HAS,
   OPERATOR_HAS,
   OPERATOR_LABEL,
   OPERATOR_LABEL,
+  OPERATOR_LIMIT,
   OPERATOR_LIST,
   OPERATOR_LIST,
   OPERATOR_MEMBER,
   OPERATOR_MEMBER,
+  OPERATOR_MODIFIED_AT,
   OPERATOR_SORT,
   OPERATOR_SORT,
   OPERATOR_STATUS,
   OPERATOR_STATUS,
   OPERATOR_SWIMLANE,
   OPERATOR_SWIMLANE,
+  OPERATOR_UNKNOWN,
   OPERATOR_USER,
   OPERATOR_USER,
   ORDER_ASCENDING,
   ORDER_ASCENDING,
   ORDER_DESCENDING,
   ORDER_DESCENDING,
@@ -36,7 +42,7 @@ import {
   PREDICATE_WEEK,
   PREDICATE_WEEK,
   PREDICATE_YEAR,
   PREDICATE_YEAR,
 } from '../../../config/search-const';
 } from '../../../config/search-const';
-import { QueryParams } from "../../../config/query-classes";
+import { QueryErrors, QueryParams } from '../../../config/query-classes';
 
 
 // const subManager = new SubsManager();
 // const subManager = new SubsManager();
 
 
@@ -80,7 +86,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
     this.myLists = new ReactiveVar([]);
     this.myLists = new ReactiveVar([]);
     this.myLabelNames = new ReactiveVar([]);
     this.myLabelNames = new ReactiveVar([]);
     this.myBoardNames = new ReactiveVar([]);
     this.myBoardNames = new ReactiveVar([]);
-    this.parsingErrors = [];
+    this.parsingErrors = new QueryErrors();
     this.colorMap = null;
     this.colorMap = null;
     this.queryParams = null;
     this.queryParams = null;
 
 
@@ -119,26 +125,18 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
 
 
   resetSearch() {
   resetSearch() {
     super.resetSearch();
     super.resetSearch();
-    this.parsingErrors = [];
+    this.parsingErrors = new QueryErrors();
   }
   }
 
 
   errorMessages() {
   errorMessages() {
-    if (this.parsingErrors.length) {
-      return this.parsingErrorMessages();
+    if (this.parsingErrors.hasErrors()) {
+      return this.parsingErrors.errorMessages();
     }
     }
     return this.queryErrorMessages();
     return this.queryErrorMessages();
   }
   }
 
 
   parsingErrorMessages() {
   parsingErrorMessages() {
-    const messages = [];
-
-    if (this.parsingErrors.length) {
-      this.parsingErrors.forEach(err => {
-        messages.push(TAPi18n.__(err.tag, err.value));
-      });
-    }
-
-    return messages;
+    this.parsingErrors.errorMessages();
   }
   }
 
 
   searchAllBoards(query) {
   searchAllBoards(query) {
@@ -188,12 +186,12 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
       'operator-assignee-abbrev': OPERATOR_ASSIGNEE,
       'operator-assignee-abbrev': OPERATOR_ASSIGNEE,
       'operator-status': OPERATOR_STATUS,
       'operator-status': OPERATOR_STATUS,
       'operator-due': OPERATOR_DUE,
       'operator-due': OPERATOR_DUE,
-      'operator-created': 'createdAt',
-      'operator-modified': 'modifiedAt',
-      'operator-comment': 'comments',
+      'operator-created': OPERATOR_CREATED_AT,
+      'operator-modified': OPERATOR_MODIFIED_AT,
+      'operator-comment': OPERATOR_COMMENT,
       'operator-has': OPERATOR_HAS,
       'operator-has': OPERATOR_HAS,
       'operator-sort': OPERATOR_SORT,
       'operator-sort': OPERATOR_SORT,
-      'operator-limit': 'limit',
+      'operator-limit': OPERATOR_LIMIT,
     };
     };
 
 
     const predicates = {
     const predicates = {
@@ -247,29 +245,6 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
     // eslint-disable-next-line no-console
     // eslint-disable-next-line no-console
     // console.log('operatorMap:', operatorMap);
     // console.log('operatorMap:', operatorMap);
 
 
-    // const params = {
-    //   limit: this.resultsPerPage,
-    //   // boards: [],
-    //   // swimlanes: [],
-    //   // lists: [],
-    //   // users: [],
-    //   members: [],
-    //   assignees: [],
-    //   // labels: [],
-    //   status: [],
-    //   // dueAt: null,
-    //   createdAt: null,
-    //   modifiedAt: null,
-    //   comments: [],
-    //   has: [],
-    // };
-    // params[OPERATOR_BOARD] = [];
-    // params[OPERATOR_DUE] = null;
-    // params[OPERATOR_LABEL] = [];
-    // params[OPERATOR_LIST] = [];
-    // params[OPERATOR_SWIMLANE] = [];
-    // params[OPERATOR_USER] = [];
-
     const params = new QueryParams();
     const params = new QueryParams();
     let text = '';
     let text = '';
     while (query) {
     while (query) {
@@ -299,7 +274,9 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
               // console.log('found color:', value);
               // console.log('found color:', value);
             }
             }
           } else if (
           } else if (
-            [OPERATOR_DUE, 'createdAt', 'modifiedAt'].includes(operator)
+            [OPERATOR_DUE, OPERATOR_CREATED_AT, OPERATOR_MODIFIED_AT].includes(
+              operator,
+            )
           ) {
           ) {
             const days = parseInt(value, 10);
             const days = parseInt(value, 10);
             let duration = null;
             let duration = null;
@@ -350,19 +327,22 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
                     value: date.format('YYYY-MM-DD'),
                     value: date.format('YYYY-MM-DD'),
                   };
                   };
                 }
                 }
-              } else if (operator === 'dueAt' && value === PREDICATE_OVERDUE) {
+              } else if (
+                operator === OPERATOR_DUE &&
+                value === PREDICATE_OVERDUE
+              ) {
                 value = {
                 value = {
                   operator: '$lt',
                   operator: '$lt',
                   value: moment().format('YYYY-MM-DD'),
                   value: moment().format('YYYY-MM-DD'),
                 };
                 };
               } else {
               } else {
-                this.parsingErrors.push({
+                this.parsingErrors.addError(OPERATOR_DUE, {
                   tag: 'operator-number-expected',
                   tag: 'operator-number-expected',
                   value: { operator: op, value },
                   value: { operator: op, value },
                 });
                 });
-                value = null;
+                continue;
               }
               }
-            } else if (operator === 'dueAt') {
+            } else if (operator === OPERATOR_DUE) {
               value = {
               value = {
                 operator: '$lt',
                 operator: '$lt',
                 value: moment(moment().format('YYYY-MM-DD'))
                 value: moment(moment().format('YYYY-MM-DD'))
@@ -385,10 +365,11 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
               negated = true;
               negated = true;
             }
             }
             if (!predicateTranslations.sorts[value]) {
             if (!predicateTranslations.sorts[value]) {
-              this.parsingErrors.push({
+              this.parsingErrors.addError(OPERATOR_SORT, {
                 tag: 'operator-sort-invalid',
                 tag: 'operator-sort-invalid',
                 value,
                 value,
               });
               });
+              continue;
             } else {
             } else {
               value = {
               value = {
                 name: predicateTranslations.sorts[value],
                 name: predicateTranslations.sorts[value],
@@ -397,10 +378,11 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
             }
             }
           } else if (operator === OPERATOR_STATUS) {
           } else if (operator === OPERATOR_STATUS) {
             if (!predicateTranslations.status[value]) {
             if (!predicateTranslations.status[value]) {
-              this.parsingErrors.push({
+              this.parsingErrors.addError(OPERATOR_STATUS, {
                 tag: 'operator-status-invalid',
                 tag: 'operator-status-invalid',
                 value,
                 value,
               });
               });
+              continue;
             } else {
             } else {
               value = predicateTranslations.status[value];
               value = predicateTranslations.status[value];
             }
             }
@@ -412,23 +394,25 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
               negated = true;
               negated = true;
             }
             }
             if (!predicateTranslations.has[value]) {
             if (!predicateTranslations.has[value]) {
-              this.parsingErrors.push({
+              this.parsingErrors.addError(OPERATOR_HAS, {
                 tag: 'operator-has-invalid',
                 tag: 'operator-has-invalid',
                 value,
                 value,
               });
               });
+              continue;
             } else {
             } else {
               value = {
               value = {
                 field: predicateTranslations.has[value],
                 field: predicateTranslations.has[value],
                 exists: !negated,
                 exists: !negated,
               };
               };
             }
             }
-          } else if (operator === 'limit') {
+          } else if (operator === OPERATOR_LIMIT) {
             const limit = parseInt(value, 10);
             const limit = parseInt(value, 10);
             if (isNaN(limit) || limit < 1) {
             if (isNaN(limit) || limit < 1) {
-              this.parsingErrors.push({
+              this.parsingErrors.addError(OPERATOR_LIMIT, {
                 tag: 'operator-limit-invalid',
                 tag: 'operator-limit-invalid',
                 value,
                 value,
               });
               });
+              continue;
             } else {
             } else {
               value = limit;
               value = limit;
             }
             }
@@ -436,7 +420,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
 
 
           params.addPredicate(operator, value);
           params.addPredicate(operator, value);
         } else {
         } else {
-          this.parsingErrors.push({
+          this.parsingErrors.addError(OPERATOR_UNKNOWN, {
             tag: 'operator-unknown-error',
             tag: 'operator-unknown-error',
             value: op,
             value: op,
           });
           });
@@ -467,7 +451,7 @@ class GlobalSearchComponent extends CardSearchPagedComponent {
 
 
     this.queryParams = params;
     this.queryParams = params;
 
 
-    if (this.parsingErrors.length) {
+    if (this.parsingErrors.hasErrors()) {
       this.searching.set(false);
       this.searching.set(false);
       this.queryErrors = this.parsingErrorMessages();
       this.queryErrors = this.parsingErrorMessages();
       this.hasResults.set(true);
       this.hasResults.set(true);

+ 75 - 40
config/query-classes.js

@@ -8,9 +8,9 @@ import {
   OPERATOR_SWIMLANE,
   OPERATOR_SWIMLANE,
   OPERATOR_USER,
   OPERATOR_USER,
 } from './search-const';
 } from './search-const';
+import Boards from '../models/boards';
 
 
 export class QueryParams {
 export class QueryParams {
-
   text = '';
   text = '';
 
 
   constructor(params = {}) {
   constructor(params = {}) {
@@ -46,58 +46,81 @@ export class QueryParams {
 }
 }
 
 
 export class QueryErrors {
 export class QueryErrors {
+  operatorTagMap = [
+    [OPERATOR_BOARD, 'board-title-not-found'],
+    [OPERATOR_SWIMLANE, 'swimlane-title-not-found'],
+    [
+      OPERATOR_LABEL,
+      label => {
+        if (Boards.labelColors().includes(label)) {
+          return {
+            tag: 'label-color-not-found',
+            value: label,
+            color: true,
+          };
+        } else {
+          return {
+            tag: 'label-not-found',
+            value: label,
+            color: false,
+          };
+        }
+      },
+    ],
+    [OPERATOR_LIST, 'list-title-not-found'],
+    [OPERATOR_COMMENT, 'comment-not-found'],
+    [OPERATOR_USER, 'user-username-not-found'],
+    [OPERATOR_ASSIGNEE, 'user-username-not-found'],
+    [OPERATOR_MEMBER, 'user-username-not-found'],
+  ];
+
   constructor() {
   constructor() {
-    this.errors = {};
+    this._errors = {};
+
+    this.operatorTags = {};
+    this.operatorTagMap.forEach(([operator, tag]) => {
+      this.operatorTags[operator] = tag;
+    });
 
 
     this.colorMap = Boards.colorMap();
     this.colorMap = Boards.colorMap();
   }
   }
 
 
-  addError(operator, value) {
-    if (!this.errors[operator]) {
-      this.errors[operator] = [];
+  addError(operator, error) {
+    if (!this._errors[operator]) {
+      this._errors[operator] = [];
+    }
+    this._errors[operator].push(error);
+  }
+
+  addNotFound(operator, value) {
+    if (typeof this.operatorTags[operator] === 'function') {
+      this.addError(operator, this.operatorTags[operator](value));
+    } else {
+      this.addError(operator, { tag: this.operatorTags[operator], value });
     }
     }
-    this.errors[operator].push(value)
   }
   }
 
 
   hasErrors() {
   hasErrors() {
-    return Object.entries(this.errors).length > 0;
+    return Object.entries(this._errors).length > 0;
+  }
+
+  errors() {
+    const errs = [];
+    Object.entries(this._errors).forEach(([operator, errors]) => {
+      errors.forEach(err => {
+        errs.push(err);
+      });
+    });
+    return errs;
   }
   }
 
 
   errorMessages() {
   errorMessages() {
     const messages = [];
     const messages = [];
-
-    const operatorTags = {};
-    operatorTags[OPERATOR_BOARD] = 'board-title-not-found';
-    operatorTags[OPERATOR_SWIMLANE] = 'swimlane-title-not-found';
-    operatorTags[OPERATOR_LABEL] = label => {
-      if (Boards.labelColors().includes(label)) {
-        return {
-          tag: 'label-color-not-found',
-          value: label,
-          color: true,
-        };
-      } else {
-        return {
-          tag: 'label-not-found',
-          value: label,
-          color: false,
-        };
-      }
-    };
-    operatorTags[OPERATOR_LIST] = 'list-title-not-found';
-    operatorTags[OPERATOR_COMMENT] = 'comment-not-found';
-    operatorTags[OPERATOR_USER] = 'user-username-not-found';
-    operatorTags[OPERATOR_ASSIGNEE] = 'user-username-not-found';
-    operatorTags[OPERATOR_MEMBER] = 'user-username-not-found';
-
-    Object.entries(this.errors, ([operator, value]) => {
-      if (typeof operatorTags[operator] === 'function') {
-        messages.push(operatorTags[operator](value));
-      } else {
-        messages.push({ tag: operatorTags[operator], value: value });
-      }
+    Object.entries(this._errors).forEach(([operator, errors]) => {
+      errors.forEach(err => {
+        messages.push(TAPi18n.__(err.tag, err.value));
+      });
     });
     });
-
     return messages;
     return messages;
   }
   }
 }
 }
@@ -106,9 +129,9 @@ export class Query {
   params = {};
   params = {};
   selector = {};
   selector = {};
   projection = {};
   projection = {};
-  errors = new QueryErrors();
 
 
   constructor(selector, projection) {
   constructor(selector, projection) {
+    this._errors = new QueryErrors();
     if (selector) {
     if (selector) {
       this.selector = selector;
       this.selector = selector;
     }
     }
@@ -117,4 +140,16 @@ export class Query {
       this.projection = projection;
       this.projection = projection;
     }
     }
   }
   }
+
+  hasErrors() {
+    return this._errors.hasErrors();
+  }
+
+  errors() {
+    return this._errors.errors();
+  }
+
+  errorMessages() {
+    return this._errors.errorMessages();
+  }
 }
 }

+ 5 - 0
config/search-const.js

@@ -1,14 +1,19 @@
+export const DEFAULT_LIMIT = 25;
 export const OPERATOR_ASSIGNEE = 'assignee';
 export const OPERATOR_ASSIGNEE = 'assignee';
 export const OPERATOR_COMMENT = 'comment';
 export const OPERATOR_COMMENT = 'comment';
+export const OPERATOR_CREATED_AT = 'createdAt';
 export const OPERATOR_DUE = 'dueAt';
 export const OPERATOR_DUE = 'dueAt';
 export const OPERATOR_BOARD = 'board';
 export const OPERATOR_BOARD = 'board';
 export const OPERATOR_HAS = 'has';
 export const OPERATOR_HAS = 'has';
 export const OPERATOR_LABEL = 'label';
 export const OPERATOR_LABEL = 'label';
+export const OPERATOR_LIMIT = 'limit';
 export const OPERATOR_LIST = 'list';
 export const OPERATOR_LIST = 'list';
 export const OPERATOR_MEMBER = 'member';
 export const OPERATOR_MEMBER = 'member';
+export const OPERATOR_MODIFIED_AT = 'modifiedAt';
 export const OPERATOR_SORT = 'sort';
 export const OPERATOR_SORT = 'sort';
 export const OPERATOR_STATUS = 'status';
 export const OPERATOR_STATUS = 'status';
 export const OPERATOR_SWIMLANE = 'swimlane';
 export const OPERATOR_SWIMLANE = 'swimlane';
+export const OPERATOR_UNKNOWN = 'unknown';
 export const OPERATOR_USER = 'user';
 export const OPERATOR_USER = 'user';
 export const ORDER_ASCENDING = 'asc';
 export const ORDER_ASCENDING = 'asc';
 export const ORDER_DESCENDING = 'des';
 export const ORDER_DESCENDING = 'des';

+ 34 - 21
server/publications/cards.js

@@ -9,12 +9,14 @@ import ChecklistItems from '../../models/checklistItems';
 import SessionData from '../../models/usersessiondata';
 import SessionData from '../../models/usersessiondata';
 import CustomFields from '../../models/customFields';
 import CustomFields from '../../models/customFields';
 import {
 import {
+  DEFAULT_LIMIT,
   OPERATOR_ASSIGNEE,
   OPERATOR_ASSIGNEE,
   OPERATOR_BOARD,
   OPERATOR_BOARD,
   OPERATOR_COMMENT,
   OPERATOR_COMMENT,
   OPERATOR_DUE,
   OPERATOR_DUE,
   OPERATOR_HAS,
   OPERATOR_HAS,
   OPERATOR_LABEL,
   OPERATOR_LABEL,
+  OPERATOR_LIMIT,
   OPERATOR_LIST,
   OPERATOR_LIST,
   OPERATOR_MEMBER,
   OPERATOR_MEMBER,
   OPERATOR_SORT,
   OPERATOR_SORT,
@@ -81,7 +83,7 @@ Meteor.publish('globalSearch', function(sessionId, params) {
   check(params, Object);
   check(params, Object);
 
 
   // eslint-disable-next-line no-console
   // eslint-disable-next-line no-console
-  // console.log('queryParams:', queryParams);
+  console.log('queryParams:', params);
 
 
   return findCards(sessionId, buildQuery(new QueryParams(params)));
   return findCards(sessionId, buildQuery(new QueryParams(params)));
 });
 });
@@ -164,7 +166,7 @@ function buildSelector(queryParams) {
             queryBoards.push(board._id);
             queryBoards.push(board._id);
           });
           });
         } else {
         } else {
-          errors.addError(OPERATOR_BOARD, query);
+          errors.addNotFound(OPERATOR_BOARD, query);
         }
         }
       });
       });
 
 
@@ -182,7 +184,7 @@ function buildSelector(queryParams) {
             querySwimlanes.push(swim._id);
             querySwimlanes.push(swim._id);
           });
           });
         } else {
         } else {
-          errors.addError(OPERATOR_SWIMLANE, query);
+          errors.addNotFound(OPERATOR_SWIMLANE, query);
         }
         }
       });
       });
 
 
@@ -204,7 +206,7 @@ function buildSelector(queryParams) {
             queryLists.push(list._id);
             queryLists.push(list._id);
           });
           });
         } else {
         } else {
-          errors.addError(OPERATOR_LIST, query);
+          errors.addNotFound(OPERATOR_LIST, query);
         }
         }
       });
       });
 
 
@@ -216,7 +218,9 @@ function buildSelector(queryParams) {
     }
     }
 
 
     if (queryParams.hasOperator(OPERATOR_COMMENT)) {
     if (queryParams.hasOperator(OPERATOR_COMMENT)) {
-      const cardIds = CardComments.textSearch(userId, queryParams.getPredicates(OPERATOR_COMMENT)).map(
+      const cardIds = CardComments.textSearch(
+        userId,
+        queryParams.getPredicates(OPERATOR_COMMENT),
         com => {
         com => {
           return com.cardId;
           return com.cardId;
         },
         },
@@ -225,7 +229,7 @@ function buildSelector(queryParams) {
         selector._id = { $in: cardIds };
         selector._id = { $in: cardIds };
       } else {
       } else {
         queryParams.getPredicates(OPERATOR_COMMENT).forEach(comment => {
         queryParams.getPredicates(OPERATOR_COMMENT).forEach(comment => {
-          errors.addError(OPERATOR_COMMENT, comment);
+          errors.addNotFound(OPERATOR_COMMENT, comment);
         });
         });
       }
       }
     }
     }
@@ -238,7 +242,7 @@ function buildSelector(queryParams) {
       }
       }
     });
     });
 
 
-    const queryUsers = {}
+    const queryUsers = {};
     queryUsers[OPERATOR_ASSIGNEE] = [];
     queryUsers[OPERATOR_ASSIGNEE] = [];
     queryUsers[OPERATOR_MEMBER] = [];
     queryUsers[OPERATOR_MEMBER] = [];
 
 
@@ -253,7 +257,7 @@ function buildSelector(queryParams) {
             queryUsers[OPERATOR_ASSIGNEE].push(user._id);
             queryUsers[OPERATOR_ASSIGNEE].push(user._id);
           });
           });
         } else {
         } else {
-          errors.addError(OPERATOR_USER, query);
+          errors.addNotFound(OPERATOR_USER, query);
         }
         }
       });
       });
     }
     }
@@ -269,13 +273,16 @@ function buildSelector(queryParams) {
               queryUsers[key].push(user._id);
               queryUsers[key].push(user._id);
             });
             });
           } else {
           } else {
-            errors.addError(key, query);
+            errors.addNotFound(key, query);
           }
           }
         });
         });
       }
       }
     });
     });
 
 
-    if (queryUsers[OPERATOR_MEMBER].length && queryUsers[OPERATOR_ASSIGNEE].length) {
+    if (
+      queryUsers[OPERATOR_MEMBER].length &&
+      queryUsers[OPERATOR_ASSIGNEE].length
+    ) {
       selector.$and.push({
       selector.$and.push({
         $or: [
         $or: [
           { members: { $in: queryUsers[OPERATOR_MEMBER] } },
           { members: { $in: queryUsers[OPERATOR_MEMBER] } },
@@ -334,7 +341,7 @@ function buildSelector(queryParams) {
                 });
                 });
             });
             });
           } else {
           } else {
-            errors.addError(OPERATOR_LABEL, label);
+            errors.addNotFound(OPERATOR_LABEL, label);
           }
           }
         }
         }
 
 
@@ -441,7 +448,7 @@ function buildSelector(queryParams) {
   const query = new Query();
   const query = new Query();
   query.selector = selector;
   query.selector = selector;
   query.params = queryParams;
   query.params = queryParams;
-  query.errors = errors;
+  query._errors = errors;
 
 
   return query;
   return query;
 }
 }
@@ -451,9 +458,9 @@ function buildProjection(query) {
   if (query.params.skip) {
   if (query.params.skip) {
     skip = query.params.skip;
     skip = query.params.skip;
   }
   }
-  let limit = 25;
-  if (query.params.limit) {
-    limit = query.params.limit;
+  let limit = DEFAULT_LIMIT;
+  if (query.params.hasOperator(OPERATOR_LIMIT)) {
+    limit = query.params.getPredicate(OPERATOR_LIMIT);
   }
   }
 
 
   const projection = {
   const projection = {
@@ -485,9 +492,12 @@ function buildProjection(query) {
     limit,
     limit,
   };
   };
 
 
-  if (query.params[OPERATOR_SORT]) {
-    const order = query.params[OPERATOR_SORT].order === ORDER_ASCENDING ? 1 : -1;
-    switch (query.params[OPERATOR_SORT].name) {
+  if (query.params.hasOperator(OPERATOR_SORT)) {
+    const order =
+      query.params.getPredicate(OPERATOR_SORT).order === ORDER_ASCENDING
+        ? 1
+        : -1;
+    switch (query.params.getPredicate(OPERATOR_SORT).name) {
       case PREDICATE_DUE_AT:
       case PREDICATE_DUE_AT:
         projection.sort = {
         projection.sort = {
           dueAt: order,
           dueAt: order,
@@ -586,12 +596,13 @@ function findCards(sessionId, query) {
   // eslint-disable-next-line no-console
   // eslint-disable-next-line no-console
   // console.log('projection:', projection);
   // console.log('projection:', projection);
   let cards;
   let cards;
-  if (!query.errors || !query.errors.hasErrors()) {
+  if (!query.hasErrors()) {
     cards = Cards.find(query.selector, query.projection);
     cards = Cards.find(query.selector, query.projection);
   }
   }
   // eslint-disable-next-line no-console
   // eslint-disable-next-line no-console
   // console.log('count:', cards.count());
   // console.log('count:', cards.count());
 
 
+  console.log(query);
   const update = {
   const update = {
     $set: {
     $set: {
       totalHits: 0,
       totalHits: 0,
@@ -600,13 +611,15 @@ function findCards(sessionId, query) {
       cards: [],
       cards: [],
       selector: SessionData.pickle(query.selector),
       selector: SessionData.pickle(query.selector),
       projection: SessionData.pickle(query.projection),
       projection: SessionData.pickle(query.projection),
-      errors: query.errors.errorMessages(),
+      errors: query.errors(),
     },
     },
   };
   };
   // if (errors) {
   // if (errors) {
-  //   update.$set.errors = errors.errorMessages();
+  //   update.$set.errors = errors.errors();
   // }
   // }
 
 
+  console.log('errors:', query.errors());
+
   if (cards) {
   if (cards) {
     update.$set.totalHits = cards.count();
     update.$set.totalHits = cards.count();
     update.$set.lastHit =
     update.$set.lastHit =