Moved directory member search into database instead of Lucence index

This commit is contained in:
Lucas Bartholemy 2019-06-27 17:18:02 +02:00
parent 18df3db3cf
commit 578f0c4427
5 changed files with 103 additions and 42 deletions

View File

@ -31,9 +31,9 @@ class Module extends \humhub\components\Module
public $isCoreModule = true;
/**
* @var string sort field (e.g. lastname) of member list (leave empty to sort by auto sort search)
* @var string sort field (e.g. lastname) of member list
*/
public $memberListSortField = '';
public $memberListSortField = 'profile.lastname';
/**
* @var int default page size for directory pages

View File

@ -10,6 +10,7 @@ namespace humhub\modules\directory\controllers;
use humhub\modules\directory\components\UserPostsStreamAction;
use humhub\modules\directory\components\Controller;
use humhub\modules\directory\Module;
use humhub\modules\user\models\Group;
use humhub\modules\user\models\User;
use humhub\modules\space\models\Space;
@ -22,13 +23,14 @@ use humhub\modules\directory\widgets\GroupStatistics;
use yii\data\Pagination;
use yii\base\Event;
use Yii;
use yii\web\HttpException;
/**
* Community/Directory Controller
*
* Shows all available users, group, spaces
*
* @package humhub.modules_core.directory.controllers
* @property Module $module
* @since 0.5
*/
class DirectoryController extends Controller
@ -74,52 +76,49 @@ class DirectoryController extends Controller
/**
* Action for the members section of the directory
*
* @todo Dont pass lucene hits to view, build user array inside of action
*/
public function actionMembers()
{
$keyword = Yii::$app->request->get('keyword', '');
$page = (int) Yii::$app->request->get('page', 1);
$groupId = (int) Yii::$app->request->get('groupId', '');
$keyword = (string)Yii::$app->request->get('keyword');
$query = User::find()->visible()->search($keyword);
// Restrict to group
$group = null;
$groupId = (int)Yii::$app->request->get('groupId');
if ($groupId) {
$group = Group::findOne(['id' => $groupId, 'show_at_directory' => 1]);
if ($group === null) {
throw new HttpException(404);
}
$query->isGroupMember($group);
}
$searchOptions = [
'model' => User::class,
'page' => $page,
'pageSize' => $this->module->pageSize,
];
if ($this->module->memberListSortField != '') {
$searchOptions['sortField'] = $this->module->memberListSortField;
$countQuery = clone $query;
$pagination = new Pagination(['totalCount' => $countQuery->count()]);
// Order
$query->joinWith('profile');
if (empty($this->module->memberListSortField) || $this->module->memberListSortField === 'lastname' || $this->module->memberListSortField === 'firstname') {
// Fallback to default value
$query->addOrderBy('profile.lastname');
} else {
$query->addOrderBy($this->module->memberListSortField);
}
if ($group !== null) {
$searchOptions['filters'] = ['groups' => $group->id];
}
$searchResultSet = Yii::$app->search->find($keyword, $searchOptions);
$pagination = new Pagination([
'totalCount' => $searchResultSet->total,
'pageSize' => $searchResultSet->pageSize
]);
Event::on(Sidebar::class, Sidebar::EVENT_INIT, function ($event) {
$event->sender->addWidget(NewMembers::class, [], ['sortOrder' => 10]);
$event->sender->addWidget(MemberStatistics::class, [], ['sortOrder' => 20]);
});
return $this->render('members', [
'keyword' => $keyword,
'group' => $group,
'users' => $searchResultSet->getResultInstances(),
'pagination' => $pagination
'keyword' => $keyword,
'group' => $group,
'users' => $query->offset($pagination->offset)->limit($pagination->limit)->all(),
'pagination' => $pagination
]);
}
/**
@ -132,7 +131,7 @@ class DirectoryController extends Controller
public function actionSpaces()
{
$keyword = Yii::$app->request->get('keyword', '');
$page = (int) Yii::$app->request->get('page', 1);
$page = (int)Yii::$app->request->get('page', 1);
$searchResultSet = Yii::$app->search->find($keyword, [
'model' => Space::class,
@ -142,8 +141,8 @@ class DirectoryController extends Controller
]);
$pagination = new Pagination([
'totalCount' => $searchResultSet->total,
'pageSize' => $searchResultSet->pageSize
'totalCount' => $searchResultSet->total,
'pageSize' => $searchResultSet->pageSize
]);
Event::on(Sidebar::class, Sidebar::EVENT_INIT, function ($event) {
@ -152,9 +151,9 @@ class DirectoryController extends Controller
});
return $this->render('spaces', [
'keyword' => $keyword,
'spaces' => $searchResultSet->getResultInstances(),
'pagination' => $pagination,
'keyword' => $keyword,
'spaces' => $searchResultSet->getResultInstances(),
'pagination' => $pagination,
]);
}
@ -176,7 +175,7 @@ class DirectoryController extends Controller
});
return $this->render('groups', [
'groups' => $groups,
'groups' => $groups,
]);
}

View File

@ -8,6 +8,8 @@
namespace humhub\modules\user\components;
use humhub\modules\user\models\Group;
use humhub\modules\user\models\ProfileField;
use yii\db\ActiveQuery;
use humhub\modules\user\models\User as UserModel;
use humhub\events\ActiveQueryEvent;
@ -19,6 +21,7 @@ use humhub\events\ActiveQueryEvent;
*/
class ActiveQueryUser extends ActiveQuery
{
const MAX_SEARCH_NEEDLES = 5;
/**
* @event Event an event that is triggered when only visible users are requested via [[visible()]].
@ -32,7 +35,7 @@ class ActiveQueryUser extends ActiveQuery
/**
* Limit to active users
*
*
* @return ActiveQueryUser the query
*/
public function active()
@ -46,9 +49,9 @@ class ActiveQueryUser extends ActiveQuery
/**
* Returns only users that should appear in user lists or in the search results.
* Also only active (enabled) users are returned.
*
* @since 1.2.3
*
* @return ActiveQueryUser the query
* @since 1.2.3
*/
public function visible()
{
@ -56,9 +59,10 @@ class ActiveQueryUser extends ActiveQuery
return $this->active();
}
/**
* Adds default user order (e.g. by lastname)
*
*
* @return ActiveQueryUser the query
*/
public function defaultOrder()
@ -69,4 +73,62 @@ class ActiveQueryUser extends ActiveQuery
return $this;
}
/**
* Performs a user full text search
*
* @param string|array $keywords
* @param array|null $fields if empty all searchable profile fields will be used
*/
public function search($keywords, $fields = null)
{
if (empty($keywords)) {
return;
}
if (empty($fields)) {
$fields = $this->getSearchableUserFields();
}
$this->joinWith('profile');
if (!is_array($keywords)) {
$keywords = explode(' ', $keywords);
}
foreach (array_slice($keywords, 0, static::MAX_SEARCH_NEEDLES) as $keyword) {
$conditions = [];
foreach ($fields as $field) {
$conditions[] = ['LIKE', $field, $keyword];
}
$this->andWhere(array_merge(['OR'], $conditions));
}
}
/**
* Returns a list of fields to be included in a user search.
*
* @return array
*/
private function getSearchableUserFields()
{
$fields = ['user.username', 'user.email', 'user.tags'];
foreach (ProfileField::findAll(['searchable' => 1]) as $profileField) {
$fields[] = $profileField->internal_name;
}
return $fields;
}
/**
* Limits the query to a specified user group
*
* @param Group $group
*/
public function isGroupMember(Group $group)
{
$this->leftJoin('group_user', 'user.id=group_user.user_id');
$this->andWhere(['group_user.group_id' => $group->id]);
}
}

View File

@ -37,6 +37,7 @@ use yii\db\ActiveQuery;
* @property string $ldap_attribute
* @property string $translation_category
* @property integer $is_system
* @property integer $searchable
*/
class ProfileField extends ActiveRecord
{

View File

@ -447,7 +447,6 @@ class User extends ContentContainerActiveRecord implements IdentityInterface, Se
public function beforeSave($insert)
{
if ($insert) {
if ($this->auth_mode == '') {
$passwordAuth = new PasswordAuth();
$this->auth_mode = $passwordAuth->getId();