| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378 |
- import {
- getIdForRef, isPopulated, type IUserGroupRelation,
- } from '@growi/core';
- import type { Model, Document } from 'mongoose';
- import mongoose, { Schema } from 'mongoose';
- import type { ObjectIdLike } from '../interfaces/mongoose-utils';
- import { getOrCreateModel } from '../util/mongoose-utils';
- import type { UserGroupDocument } from './user-group';
- const debug = require('debug')('growi:models:userGroupRelation');
- const mongoosePaginate = require('mongoose-paginate-v2');
- const uniqueValidator = require('mongoose-unique-validator');
- export interface UserGroupRelationDocument extends IUserGroupRelation, Document {}
- export interface UserGroupRelationModel extends Model<UserGroupRelationDocument> {
- [x:string]: any, // for old methods
- PAGE_ITEMS: 50,
- removeAllByUserGroups: (groupsToDelete: UserGroupDocument[]) => Promise<any>,
- findAllUserIdsForUserGroups: (userGroupIds: ObjectIdLike[]) => Promise<string[]>,
- findGroupsWithDescendantsByGroupAndUser: (group: UserGroupDocument, user) => Promise<UserGroupDocument[]>,
- countByGroupIdsAndUser: (userGroupIds: ObjectIdLike[], userData) => Promise<number>
- findAllGroupsForUser: (user) => Promise<UserGroupDocument[]>
- findAllUserGroupIdsRelatedToUser: (user) => Promise<ObjectIdLike[]>
- }
- /*
- * define schema
- */
- const schema = new Schema<UserGroupRelationDocument, UserGroupRelationModel>({
- relatedGroup: { type: Schema.Types.ObjectId, ref: 'UserGroup', required: true },
- relatedUser: { type: Schema.Types.ObjectId, ref: 'User', required: true },
- }, {
- timestamps: { createdAt: true, updatedAt: false },
- });
- schema.plugin(mongoosePaginate);
- schema.plugin(uniqueValidator);
- /**
- * remove all invalid relations that has reference to unlinked document
- */
- schema.statics.removeAllInvalidRelations = function() {
- return this.findAllRelation()
- .then((relations) => {
- // filter invalid documents
- return relations.filter((relation) => {
- return relation.relatedUser == null || relation.relatedGroup == null;
- });
- })
- .then((invalidRelations) => {
- const ids = invalidRelations.map((relation) => { return relation._id });
- return this.deleteMany({ _id: { $in: ids } });
- });
- };
- /**
- * find all user and group relation
- *
- * @static
- * @returns {Promise<UserGroupRelation[]>}
- * @memberof UserGroupRelation
- */
- schema.statics.findAllRelation = function() {
- return this
- .find()
- .populate('relatedUser')
- .populate('relatedGroup')
- .exec();
- };
- /**
- * find all user and group relation of UserGroup
- *
- * @static
- * @param {UserGroup} userGroup
- * @returns {Promise<UserGroupRelation[]>}
- * @memberof UserGroupRelation
- */
- schema.statics.findAllRelationForUserGroup = function(userGroup) {
- debug('findAllRelationForUserGroup is called', userGroup);
- return this
- .find({ relatedGroup: userGroup })
- .populate('relatedUser')
- .exec();
- };
- schema.statics.findAllUserIdsForUserGroups = async function(userGroupIds: ObjectIdLike[]): Promise<string[]> {
- const relations = await this
- .find({ relatedGroup: { $in: userGroupIds } })
- .select('relatedUser')
- .exec();
- // return unique ids
- return [...new Set(relations.map(r => r.relatedUser.toString()))];
- };
- /**
- * find all user and group relation of UserGroups
- *
- * @static
- * @param {UserGroup[]} userGroups
- * @returns {Promise<UserGroupRelation[]>}
- * @memberof UserGroupRelation
- */
- schema.statics.findAllRelationForUserGroups = function(userGroups) {
- return this
- .find({ relatedGroup: { $in: userGroups } })
- .populate('relatedUser')
- .exec();
- };
- /**
- * find all groups of User
- *
- * @static
- * @param {User} user
- * @returns {Promise<UserGroupDocument[]>}
- * @memberof UserGroupRelation
- */
- schema.statics.findAllGroupsForUser = async function(user): Promise<UserGroupDocument[]> {
- const userGroupRelations = await this.find({ relatedUser: user.id }).populate('relatedGroup');
- const userGroups = userGroupRelations.map((relation) => {
- return isPopulated(relation.relatedGroup) ? relation.relatedGroup as UserGroupDocument : null;
- });
- return userGroups.filter((group): group is NonNullable<UserGroupDocument> => group != null);
- };
- /**
- * find all UserGroup IDs that related to specified User
- *
- * @static
- * @param {User} user
- * @returns {Promise<ObjectId[]>}
- */
- schema.statics.findAllUserGroupIdsRelatedToUser = async function(user): Promise<ObjectIdLike[]> {
- const relations = await this.find({ relatedUser: user._id })
- .select('relatedGroup')
- .exec();
- return relations.map((relation) => { return getIdForRef(relation.relatedGroup) });
- };
- /**
- * count by related group id and related user
- *
- * @static
- * @param {string} userGroupId find query param for relatedGroup
- * @param {User} userData find query param for relatedUser
- * @returns {Promise<number>}
- */
- schema.statics.countByGroupIdsAndUser = async function(userGroupIds: ObjectIdLike[], userData): Promise<number> {
- const query = {
- relatedGroup: { $in: userGroupIds },
- relatedUser: userData.id,
- };
- return this.count(query);
- };
- /**
- * find all "not" related user for UserGroup
- *
- * @static
- * @param {UserGroup} userGroup for find users not related
- * @returns {Promise<User>}
- * @memberof UserGroupRelation
- */
- schema.statics.findUserByNotRelatedGroup = function(userGroup, queryOptions) {
- const User = mongoose.model('User') as any;
- let searchWord = new RegExp(`${queryOptions.searchWord}`);
- switch (queryOptions.searchType) {
- case 'forward':
- searchWord = new RegExp(`^${queryOptions.searchWord}`);
- break;
- case 'backword':
- searchWord = new RegExp(`${queryOptions.searchWord}$`);
- break;
- }
- const searthField: Record<string, RegExp>[] = [
- { username: searchWord },
- ];
- if (queryOptions.isAlsoMailSearched === 'true') { searthField.push({ email: searchWord }) }
- if (queryOptions.isAlsoNameSearched === 'true') { searthField.push({ name: searchWord }) }
- return this.findAllRelationForUserGroup(userGroup)
- .then((relations) => {
- const relatedUserIds = relations.map((relation) => {
- return relation.relatedUser.id;
- });
- const query = {
- _id: { $nin: relatedUserIds },
- status: User.STATUS_ACTIVE,
- $or: searthField,
- };
- debug('findUserByNotRelatedGroup ', query);
- return User.find(query).exec();
- });
- };
- /**
- * get if the user has relation for group
- *
- * @static
- * @param {UserGroup} userGroup
- * @param {User} user
- * @returns {Promise<boolean>} is user related for group(or not)
- * @memberof UserGroupRelation
- */
- schema.statics.isRelatedUserForGroup = function(userGroup, user) {
- const query = {
- relatedGroup: userGroup.id,
- relatedUser: user.id,
- };
- return this
- .count(query)
- .exec()
- .then((count) => {
- // return true or false of the relation is exists(not count)
- return (count > 0);
- });
- };
- /**
- * create user and group relation
- *
- * @static
- * @param {UserGroup} userGroup
- * @param {User} user
- * @returns {Promise<UserGroupRelation>} created relation
- * @memberof UserGroupRelation
- */
- schema.statics.createRelation = function(userGroup, user) {
- return this.create({
- relatedGroup: userGroup.id,
- relatedUser: user.id,
- });
- };
- schema.statics.createRelations = async function(userGroupIds, user) {
- const documentsToInsertMany = userGroupIds.map((groupId) => {
- return {
- relatedGroup: groupId,
- relatedUser: user._id,
- createdAt: new Date(),
- };
- });
- return this.insertMany(documentsToInsertMany);
- };
- /**
- * remove all relation for UserGroup
- *
- * @static
- * @param {UserGroup} userGroup related group for remove
- * @returns {Promise<any>}
- * @memberof UserGroupRelation
- */
- schema.statics.removeAllByUserGroups = function(groupsToDelete: UserGroupDocument[]) {
- return this.deleteMany({ relatedGroup: { $in: groupsToDelete } });
- };
- /**
- * remove relation by id
- *
- * @static
- * @param {ObjectId} id
- * @returns {Promise<any>}
- * @memberof UserGroupRelation
- */
- schema.statics.removeById = function(id) {
- return this.findById(id)
- .then((relationData) => {
- if (relationData == null) {
- throw new Error('UserGroupRelation data is not exists. id:', id);
- }
- else {
- relationData.remove();
- }
- });
- };
- schema.statics.findUserIdsByGroupId = async function(groupId) {
- const relations = await this.find({ relatedGroup: groupId }, { _id: 0, relatedUser: 1 }).lean().exec(); // .lean() to get not ObjectId but string
- return relations.map(relation => relation.relatedUser);
- };
- schema.statics.createByGroupIdsAndUserIds = async function(groupIds, userIds) {
- const insertOperations: any[] = [];
- groupIds.forEach((groupId) => {
- userIds.forEach((userId) => {
- insertOperations.push({
- insertOne: {
- document: {
- relatedGroup: groupId,
- relatedUser: userId,
- },
- },
- });
- });
- });
- await this.bulkWrite(insertOperations);
- };
- /**
- * Recursively finds descendant groups by populating relations.
- * @static
- * @param {UserGroupDocument} group
- * @param {UserDocument} user
- * @returns UserGroupDocument[]
- */
- schema.statics.findGroupsWithDescendantsByGroupAndUser = async function(group: UserGroupDocument, user): Promise<UserGroupDocument[]> {
- const descendantGroups = [group];
- const incrementGroupsRecursively = async(groups, user) => {
- const groupIds = groups.map(g => g._id);
- const populatedRelations = await this.aggregate([
- {
- $match: {
- relatedUser: user._id,
- },
- },
- {
- $lookup: {
- from: this.collection.collectionName,
- localField: 'relatedGroup',
- foreignField: '_id',
- as: 'relatedGroup',
- },
- },
- {
- $unwind: {
- path: '$relatedGroup',
- },
- },
- {
- $match: {
- 'relatedGroup.parent': { $in: groupIds },
- },
- },
- ]);
- const nextGroups = populatedRelations.map(d => d.relatedGroup);
- // End
- const shouldEnd = nextGroups.length === 0;
- if (shouldEnd) {
- return;
- }
- // Increment
- descendantGroups.push(...nextGroups);
- return incrementGroupsRecursively(nextGroups, user);
- };
- await incrementGroupsRecursively([group], user);
- return descendantGroups;
- };
- export default getOrCreateModel<UserGroupRelationDocument, UserGroupRelationModel>('UserGroupRelation', schema);
|