avatar Ilija Studen Staff Dec 21. 2009. 7:43 pm
There a short article about MySQL FULLTEXT configuration: Improving Search Index.

Article about search operators is not yet moved to the new docs, but it's pretty simple:

1. "some phrase" - exact multiword match
2. +word - must contain a word
3. -word - without a word

You can also check MySQL docs for the full list of available operators, but these are basically the ones that are the most useful.
avatar smico Pro Dec 23. 2009. 6:43 am
Project Groups should be included in the filters for sure.

Loose hierarchy that is introduced by Project Group implementation could actually become useful with inclusion in the search.


Happy Holidays and Happy New Year to everyone.

smico
avatar Marko Brščić Dec 24. 2009. 1:29 pm
Also, it would be a great feature that when you search for projects, you can select to search only active projects or only finished project, and also both.
avatar smico Pro Dec 29. 2009. 9:04 am
I gave some thought on improvements of search functionality and I think that we should all learn from our colleagues who write programs for merchandise sale on WEB, as search functionality is their core business.
If you go to www.dell.ca, or www.amazon.ca, search returns all results, and on the left side panel they list categories with number of results, to allow user to refine the search. Each category should show maximum number of results with …See more link to allow easy access to all categories.

Sorting should be included probably in combo box:
Sort by
- Recent activity
- Project group
- Project
- User
- Status

In my example if I search for Security, my ActiveCollab returns 14 results. I will throw in all categories of results that I can think of, and we should pick categories that are most relevant. Categories could be loosely organized, to return total of 14 results, or more, or less.


Refine search:
Recent activity
- 1 day (2) //this could be input box for user to select how many days
- 1 week (17)
- 1 month (17)

Project status
- Active (10)
- Completed (5)
- Paused (1)
- Cancelled (1)

Project ??something, I am bad with names??
- Active (10)
- Archived projects (6)
- New/Updated (9)
- Late/Today (9)

Priority
- High (10)
- Normal (2)
- Lowest (2)

Project Groups
- EGB (3)
- ECB (10)
- Software external (2)
- Software internal (1)
- Miscellaneous (1)

User (it can be Created by, or Active in, or something)
- smico (5)
- stico (5)
- svico (7)

Company
- Data (5)
- Research (12)

Categories
- Bug (2)
- Feature addition (2)
- Project proposal (3)
- Brainstorm (2)

??Name again??:
- Checklists (2)
- Discussions (2)
- Files (1)
- Pages (2)
- Tickets (7)


Milestones
- Promotion EGB daily XML load (2)
- Check constraints (50)
- ..See more


Pre-search filter selection for some categories could be shown.

Regards

smico
avatar Nirav M. Dev Dec 30. 2009. 2:28 am
Great ideas @smico.

Last weekend, I sat down to do some hacking on activeCollab search. Just changing the order of search results to newest first and search method to "natural search" rather than "boolean search" gave me much better results. I "wished" to write some smarts in search that would automatically decide the search method to use - based on the keywords entered. (need to give this some more thought)

Another useful option is to search only in the currently selected project. I also think grouping search results in categories will help finding the item you are looking for.

Intention is to improve the search, without making it too complicated / busy.

I will post more ideas as I spend some more time on this...

:Nirav
Discover a new high in team productivity with our Best Selling & Most Loved activeCollab Extensions
avatar smico Pro Dec 30. 2009. 2:29 pm
Nirav,

I agree with you that search should be easy to use, not too much load on the server, and ultimatelly useful.
Would it be too much trouble to post your mod, so we can do the same while waiting fro Ilija & co to fix the searc?

Thanks,

SMico
avatar Ilija Studen Staff Dec 30. 2009. 11:41 pm
Hi Smico,

Filtering by properties is an interesting approach. I see that it can add additional load to the search results page, but what's a couple of more queries compared to usability enhancements? We'll definitely consider this for the future.
avatar Leon P. Dev Jan 1. 2010. 3:35 am
@smico

Great suggestion for the category/filtering of search results. +1
avatar Nirav M. Dev Jan 1. 2010. 1:54 pm
Here's the quick modification that gave us better search results.

File: activecollab/application/modules/system/models/search_engines/MysqlSearchEngine.class.php

Function: search()

Comment the current search() function. (or rename it) and try the following.

function search($search_for, $type, $user, $page = 1, $per_page = 30) {
      $page = (integer) $page;
      $per_page = (integer) $per_page;

      $search_index_table = TABLE_PREFIX . 'search_index';

      $offset = ($page - 1) * $per_page;

      // Search in projects
      if($type == 'ProjectObject') {
        $type_filter = ProjectUsers::getVisibleTypesFilter($user, array(PROJECT_STATUS_ACTIVE, PROJECT_STATUS_PAUSED, PROJECT_STATUS_COMPLETED, PROJECT_STATUS_CANCELED));
        if(empty($type_filter)) {
          return array(null, new Pager(1, 0, $per_page));
        } // if

        $project_objects_table = TABLE_PREFIX . 'project_objects';

        //$total_items = (integer) array_var(db_execute_one("SELECT COUNT($project_objects_table.id) AS 'row_count' FROM $project_objects_table, $search_index_table WHERE $type_filter AND MATCH ($search_index_table.content) AGAINST (? IN BOOLEAN MODE) AND $project_objects_table.id = $search_index_table.object_id AND $search_index_table.type = ? AND state >= ? AND visibility >= ?", $search_for, $type, STATE_VISIBLE, $user->getVisibility()), 'row_count');
        $total_items = (integer) array_var(db_execute_one("SELECT COUNT($project_objects_table.id) AS 'row_count' FROM $project_objects_table, $search_index_table WHERE $type_filter AND MATCH ($search_index_table.content) AGAINST (?) AND $project_objects_table.id = $search_index_table.object_id AND $search_index_table.type = ? AND state >= ? AND visibility >= ?", $search_for, $type, STATE_VISIBLE, $user->getVisibility()), 'row_count');
        if($total_items) {
          // $items = ProjectObjects::findBySQL("SELECT $project_objects_table.* FROM $project_objects_table, $search_index_table WHERE $type_filter AND MATCH ($search_index_table.content) AGAINST (? IN BOOLEAN MODE) AND $project_objects_table.id = $search_index_table.object_id AND $search_index_table.type = ? AND state >= ? AND visibility >= ? LIMIT $offset, $per_page", array($search_for, $type, STATE_VISIBLE, $user->getVisibility()));
          $items = ProjectObjects::findBySQL("SELECT $project_objects_table.*, MATCH ($search_index_table.content) AGAINST (?) as score  FROM $project_objects_table, $search_index_table WHERE $type_filter AND MATCH ($search_index_table.content) AGAINST (?) AND $project_objects_table.id = $search_index_table.object_id AND $search_index_table.type = ? AND state >= ? AND visibility >= ? ORDER BY score DESC, $project_objects_table.updated_on DESC, $project_objects_table.name LIMIT $offset, $per_page", array($search_for, $search_for, $type, STATE_VISIBLE, $user->getVisibility()));
        } else {
          $items = null;
        } // if

        return array($items, new Pager($page, $total_items, $per_page));

      // Search for projects
      } elseif($type == 'Project') {
        $project_ids = Projects::findProjectIdsByUser($user, null, true);
        if(!is_foreachable($project_ids)) {
          return array(null, new Pager(1, 0, $per_page));
        } // if

        $projects_table = TABLE_PREFIX . 'projects';

        //$total_items = (integer) array_var(db_execute_one("SELECT COUNT($projects_table.id) AS 'row_count' FROM $projects_table, $search_index_table WHERE $projects_table.id IN (?) AND MATCH ($search_index_table.content) AGAINST (? IN BOOLEAN MODE) AND $projects_table.id = $search_index_table.object_id AND $search_index_table.type = ?", $project_ids, $search_for, 'Project'), 'row_count');
        $total_items = (integer) array_var(db_execute_one("SELECT COUNT($projects_table.id) AS 'row_count' FROM $projects_table, $search_index_table WHERE $projects_table.id IN (?) AND MATCH ($search_index_table.content) AGAINST (?) AND $projects_table.id = $search_index_table.object_id AND $search_index_table.type = ?", $project_ids, $search_for, 'Project'), 'row_count');
        if($total_items) {
		  //$items = Projects::findBySQL("SELECT * FROM $projects_table, $search_index_table WHERE $projects_table.id IN (?) AND MATCH ($search_index_table.content) AGAINST (? IN BOOLEAN MODE) AND $projects_table.id = $search_index_table.object_id AND $search_index_table.type = ? LIMIT $offset, $per_page", array($project_ids, $search_for, 'Project'));
          $items = Projects::findBySQL("SELECT *, MATCH ($search_index_table.content) AGAINST (?) as score FROM $projects_table, $search_index_table WHERE $projects_table.id IN (?) AND MATCH ($search_index_table.content) AGAINST (?) AND $projects_table.id = $search_index_table.object_id AND $search_index_table.type = ? ORDER BY score DESC, updated_on DESC, name ASC LIMIT $offset, $per_page", array($search_for, $project_ids, $search_for, 'Project'));
        } else {
          $items = null;
        } // if

        return array($items, new Pager($page, $total_items, $per_page));

      // Search for users
      } elseif($type == 'User') {
        $user_ids = $user->visibleUserIds();
        if(!is_foreachable($user_ids)) {
          return array(null, new Pager(1, 0, $per_page));
        } // if
        $users_table = TABLE_PREFIX . 'users';

        $total_items = (integer) array_var(db_execute_one("SELECT COUNT($users_table.id) AS 'row_count' FROM $users_table, $search_index_table WHERE $users_table.id IN (?) AND MATCH ($search_index_table.content) AGAINST (?) AND $users_table.id = $search_index_table.object_id AND $search_index_table.type = ?", $user_ids, $search_for, 'User'), 'row_count');
        if($total_items) {
          $items = Users::findBySQL("SELECT * FROM $users_table, $search_index_table WHERE $users_table.id IN (?) AND MATCH ($search_index_table.content) AGAINST (?) AND $users_table.id = $search_index_table.object_id AND $search_index_table.type = ? LIMIT $offset, $per_page", array($user_ids, $search_for, 'User'));
        } else {
          $items = null;
        } // if

        return array($items, new Pager($page, $total_items, $per_page));

      // Unknown search type
      } else {
        return array(null, new Pager(1, 0, $per_page));
      } // if
    } // search


This makes minor changes to how Projects and Project Objects are searched. Please ensure to change only the code above. Pay attention to existing code.

Also, make sure you have a backup of the file before making this change. So that you can revert to it if this does not work for you.

Feedback welcome :)
Discover a new high in team productivity with our Best Selling & Most Loved activeCollab Extensions
avatar BrianJCohen Pro Mar 10. 2010. 6:00 pm
+1

Search in activeCollab is, and appears to remain, terrible. I generally cannot find anything I'm looking for with Search.
or Go To Next Page