mirror of
https://github.com/wekan/wekan.git
synced 2026-02-05 08:01:49 +01:00
Merge pull request #3492 from jrsupplee/new-search
Global Search Update
This commit is contained in:
commit
c10f32cc30
12 changed files with 1384 additions and 811 deletions
|
|
@ -1278,37 +1278,33 @@ Boards.userSearch = (
|
|||
userId,
|
||||
selector = {},
|
||||
projection = {},
|
||||
includeArchived = false,
|
||||
// includeArchived = false,
|
||||
) => {
|
||||
if (!includeArchived) {
|
||||
selector.archived = false;
|
||||
}
|
||||
selector.$or = [
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId, isActive: true } } },
|
||||
];
|
||||
// if (!includeArchived) {
|
||||
// selector.archived = false;
|
||||
// }
|
||||
selector.$or = [{ permission: 'public' }];
|
||||
|
||||
if (userId) {
|
||||
selector.$or.push({ members: { $elemMatch: { userId, isActive: true } } });
|
||||
}
|
||||
return Boards.find(selector, projection);
|
||||
};
|
||||
|
||||
Boards.userBoards = (userId, includeArchived = false, selector = {}) => {
|
||||
check(userId, String);
|
||||
|
||||
if (!includeArchived) {
|
||||
selector = {
|
||||
archived: false,
|
||||
};
|
||||
Boards.userBoards = (userId, archived = false, selector = {}) => {
|
||||
if (typeof archived === 'boolean') {
|
||||
selector.archived = archived;
|
||||
}
|
||||
selector.$or = [
|
||||
{ permission: 'public' },
|
||||
{ members: { $elemMatch: { userId, isActive: true } } },
|
||||
];
|
||||
selector.$or = [{ permission: 'public' }];
|
||||
|
||||
if (userId) {
|
||||
selector.$or.push({ members: { $elemMatch: { userId, isActive: true } } });
|
||||
}
|
||||
return Boards.find(selector);
|
||||
};
|
||||
|
||||
Boards.userBoardIds = (userId, includeArchived = false, selector = {}) => {
|
||||
return Boards.userBoards(userId, includeArchived, selector).map(board => {
|
||||
Boards.userBoardIds = (userId, archived = false, selector = {}) => {
|
||||
return Boards.userBoards(userId, archived, selector).map(board => {
|
||||
return board._id;
|
||||
});
|
||||
};
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
const escapeForRegex = require('escape-string-regexp');
|
||||
CardComments = new Mongo.Collection('card_comments');
|
||||
|
||||
/**
|
||||
|
|
@ -109,6 +110,28 @@ function commentCreation(userId, doc) {
|
|||
});
|
||||
}
|
||||
|
||||
CardComments.textSearch = (userId, textArray) => {
|
||||
const selector = {
|
||||
boardId: { $in: Boards.userBoardIds(userId) },
|
||||
$and: [],
|
||||
};
|
||||
|
||||
for (const text of textArray) {
|
||||
selector.$and.push({ text: new RegExp(escapeForRegex(text)) });
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('cardComments selector:', selector);
|
||||
|
||||
const comments = CardComments.find(selector);
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('count:', comments.count());
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('cards with comments:', comments.map(com => { return com.cardId }));
|
||||
|
||||
return comments;
|
||||
};
|
||||
|
||||
if (Meteor.isServer) {
|
||||
// Comments are often fetched within a card, so we create an index to make these
|
||||
// queries more efficient.
|
||||
|
|
|
|||
256
models/cards.js
256
models/cards.js
|
|
@ -1863,262 +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: [],
|
||||
};
|
||||
}
|
||||
|
||||
hasErrors() {
|
||||
for (const prop in this.notFound) {
|
||||
if (this.notFound[prop].length) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
})();
|
||||
|
||||
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(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(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(query, 'i'),
|
||||
});
|
||||
if (lists.count()) {
|
||||
lists.forEach(list => {
|
||||
queryLists.push(list._id);
|
||||
});
|
||||
} else {
|
||||
errors.notFound.lists.push(query);
|
||||
}
|
||||
});
|
||||
|
||||
selector.listId.$in = queryLists;
|
||||
}
|
||||
|
||||
if (queryParams.dueAt !== null) {
|
||||
selector.dueAt = { $gte: 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(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(queryParams.text, 'i');
|
||||
|
||||
selector.$or = [
|
||||
{ title: regex },
|
||||
{ description: regex },
|
||||
{ customFields: { $elemMatch: { value: regex } } },
|
||||
];
|
||||
}
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
// console.log('selector:', selector);
|
||||
const cards = Cards.find(selector, {
|
||||
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,
|
||||
});
|
||||
|
||||
// eslint-disable-next-line no-console
|
||||
//console.log('count:', cards.count());
|
||||
|
||||
return { cards, errors };
|
||||
};
|
||||
|
||||
//FUNCTIONS FOR creation of Activities
|
||||
|
||||
function updateActivities(doc, fieldNames, modifier) {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,13 @@ SessionData.attachSchema(
|
|||
type: String,
|
||||
optional: false,
|
||||
},
|
||||
sessionId: {
|
||||
/**
|
||||
* unique session ID
|
||||
*/
|
||||
type: String,
|
||||
optional: false,
|
||||
},
|
||||
totalHits: {
|
||||
/**
|
||||
* total number of hits in the last report query
|
||||
|
|
@ -32,6 +39,13 @@ SessionData.attachSchema(
|
|||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
resultsCount: {
|
||||
/**
|
||||
* number of results returned
|
||||
*/
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
lastHit: {
|
||||
/**
|
||||
* the last hit returned from a report query
|
||||
|
|
@ -39,6 +53,48 @@ SessionData.attachSchema(
|
|||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
cards: {
|
||||
type: [String],
|
||||
optional: true,
|
||||
},
|
||||
selector: {
|
||||
type: String,
|
||||
optional: true,
|
||||
blackbox: true,
|
||||
},
|
||||
errorMessages: {
|
||||
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
|
||||
|
|
@ -70,4 +126,50 @@ SessionData.attachSchema(
|
|||
}),
|
||||
);
|
||||
|
||||
SessionData.helpers({
|
||||
getSelector() {
|
||||
return SessionData.unpickle(this.selector);
|
||||
},
|
||||
});
|
||||
|
||||
SessionData.unpickle = pickle => {
|
||||
return JSON.parse(pickle, (key, value) => {
|
||||
if (typeof value === 'object') {
|
||||
if (value.hasOwnProperty('$$class')) {
|
||||
if (value.$$class === 'RegExp') {
|
||||
return new RegExp(value.source, value.flags);
|
||||
}
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
};
|
||||
|
||||
SessionData.pickle = value => {
|
||||
return JSON.stringify(value, (key, value) => {
|
||||
if (typeof value === 'object') {
|
||||
if (value.constructor.name === 'RegExp') {
|
||||
return {
|
||||
$$class: 'RegExp',
|
||||
source: value.source,
|
||||
flags: value.flags,
|
||||
};
|
||||
}
|
||||
}
|
||||
return value;
|
||||
});
|
||||
};
|
||||
|
||||
if (!Meteor.isServer) {
|
||||
SessionData.getSessionId = () => {
|
||||
let sessionId = Session.get('sessionId');
|
||||
if (!sessionId) {
|
||||
sessionId = `${String(Meteor.userId())}-${String(Math.random())}`;
|
||||
Session.set('sessionId', sessionId);
|
||||
}
|
||||
|
||||
return sessionId;
|
||||
};
|
||||
}
|
||||
|
||||
export default SessionData;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue