diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b601d3c..be370f50 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ - ProjectTemplates now are sorted by the attribute 'order'. - Create enpty wiki pages (if not exist) when a new link is created. - Diff messages in history entries now show only the relevant changes (with some context). +- Include created, modified and finished dates for tasks in CSV reports +- User stories and tasks listing API call support extra params to include more data (tasks and attachemnts and attachments, respectively) - Comments: - Now comment owners and project admins can edit existing comments with the history Entry endpoint. - Add a new permissions to allow add comments instead of use the existent modify permission for this purpose. @@ -15,10 +17,9 @@ - New API endpoints over projects to create, rename, edit, delete and mix tags. - Tag color assignation is not automatic. - Select a color (or not) to a tag when add it to stories, issues and tasks. -- Now comment owners and project admins can edit existing comments with the history Entry endpoint. -- Add a new permissions to allow add comments instead of use the existent modify permission for this purpose. -- Include created, modified and finished dates for tasks in CSV reports -- User stories and tasks listing API call support extra params to include more data (tasks and attachemnts and attachments, respectively) +- Improve search system over stories, tasks and issues: + - Search into tags too. (thanks to [Riccardo Cocciol](https://github.com/volans-)) + - Weights are applied: (subject = ref > tags > description). - Import/Export: - Gzip export/import support. - Export performance improvements. @@ -32,7 +33,7 @@ ## 2.1.0 Ursus Americanus (2016-05-03) ### Features -- Add sprint name and slug on search results for user stories ((thanks to [@everblut](https://github.com/everblut))) +- Add sprint name and slug on search results for user stories (thanks to [@everblut](https://github.com/everblut)) - [API] projects resource: Random order if `discover_mode=true` and `is_featured=true`. - Webhooks: Improve webhook data: - add permalinks diff --git a/taiga/searches/services.py b/taiga/searches/services.py index 5e04518a..afc6a7e5 100644 --- a/taiga/searches/services.py +++ b/taiga/searches/services.py @@ -25,58 +25,65 @@ MAX_RESULTS = getattr(settings, "SEARCHES_MAX_RESULTS", 150) def search_user_stories(project, text): - model_cls = apps.get_model("userstories", "UserStory") - where_clause = ("to_tsvector('english_nostop', coalesce(userstories_userstory.subject) || ' ' || " - "coalesce(userstories_userstory.ref) || ' ' || " - "coalesce(array_to_string(userstories_userstory.tags, ' '), '') || ' ' || " - "coalesce(userstories_userstory.description, '')) " - "@@ to_tsquery('english_nostop', %s)") - - queryset = model_cls.objects.filter(project_id=project.pk) - - if text: - queryset = queryset.extra(where=[where_clause], params=[to_tsquery(text)]) - - queryset = attach_total_points(queryset) - return queryset[:MAX_RESULTS] + model = apps.get_model("userstories", "UserStory") + queryset = model.objects.filter(project_id=project.pk) + table = "userstories_userstory" + return _search_items(queryset, table, text) def search_tasks(project, text): - model_cls = apps.get_model("tasks", "Task") - where_clause = ("to_tsvector('english_nostop', coalesce(tasks_task.subject, '') || ' ' || " - "coalesce(tasks_task.ref) || ' ' || " - "coalesce(array_to_string(tasks_task.tags, ' '), '') || ' ' || " - "coalesce(tasks_task.description, '')) @@ to_tsquery('english_nostop', %s)") - - if text: - return (model_cls.objects.extra(where=[where_clause], params=[to_tsquery(text)]) - .filter(project_id=project.pk)[:MAX_RESULTS]) - - return model_cls.objects.filter(project_id=project.pk)[:MAX_RESULTS] + model = apps.get_model("userstories", "UserStory") + queryset = model.objects.filter(project_id=project.pk) + table = "userstories_userstory" + return _search_items(queryset, table, text) def search_issues(project, text): - model_cls = apps.get_model("issues", "Issue") - where_clause = ("to_tsvector('english_nostop', coalesce(issues_issue.subject) || ' ' || " - "coalesce(issues_issue.ref) || ' ' || " - "coalesce(array_to_string(issues_issue.tags, ' '), '') || ' ' || " - "coalesce(issues_issue.description, '')) @@ to_tsquery('english_nostop', %s)") - - if text: - return (model_cls.objects.extra(where=[where_clause], params=[to_tsquery(text)]) - .filter(project_id=project.pk)[:MAX_RESULTS]) - - return model_cls.objects.filter(project_id=project.pk)[:MAX_RESULTS] + model = apps.get_model("userstories", "UserStory") + queryset = model.objects.filter(project_id=project.pk) + table = "userstories_userstory" + return _search_items(queryset, table, text) def search_wiki_pages(project, text): - model_cls = apps.get_model("wiki", "WikiPage") - where_clause = ("to_tsvector('english_nostop', coalesce(wiki_wikipage.slug) || ' ' || " - "coalesce(wiki_wikipage.content, '')) " - "@@ to_tsquery('english_nostop', %s)") + model = apps.get_model("wiki", "WikiPage") + queryset = model.objects.filter(project_id=project.pk) + tsquery = "to_tsquery('english_nostop', %s)" + tsvector = """ + setweight(to_tsvector('english_nostop', coalesce(wiki_wikipage.slug)), 'A') || + setweight(to_tsvector('english_nostop', coalesce(wiki_wikipage.content)), 'B') + """ + + return _search_by_query(queryset, tsquery, tsvector, text) + + +def _search_items(queryset, table, text): + tsquery = "to_tsquery('english_nostop', %s)" + tsvector = """ + setweight(to_tsvector('english_nostop', + coalesce({table}.subject) || ' ' || + coalesce({table}.ref)), 'A') || + setweight(to_tsvector('english_nostop', coalesce(inmutable_array_to_string({table}.tags))), 'B') || + setweight(to_tsvector('english_nostop', coalesce({table}.description)), 'C') + """.format(table=table) + return _search_by_query(queryset, tsquery, tsvector, text) + + +def _search_by_query(queryset, tsquery, tsvector, text): + select = { + "rank": "ts_rank({tsvector},{tsquery})".format(tsquery=tsquery, + tsvector=tsvector), + } + order_by = ["-rank", ] + where = ["{tsvector} @@ {tsquery}".format(tsquery=tsquery, + tsvector=tsvector), ] if text: - return (model_cls.objects.extra(where=[where_clause], params=[to_tsquery(text)]) - .filter(project_id=project.pk)[:MAX_RESULTS]) + queryset = queryset.extra(select=select, + select_params=[to_tsquery(text)], + where=where, + params=[to_tsquery(text)], + order_by=order_by) - return model_cls.objects.filter(project_id=project.pk)[:MAX_RESULTS] + queryset = attach_total_points(queryset) + return queryset[:MAX_RESULTS]