| 1 | import sys |
|---|
| 2 | |
|---|
| 3 | from trac.admin import AdminCommandError, IAdminCommandProvider, PrefixList |
|---|
| 4 | from trac.core import Component, implements |
|---|
| 5 | from trac.util.translation import _ |
|---|
| 6 | from trac.util.text import printout, print_table |
|---|
| 7 | |
|---|
| 8 | from fulltextsearchplugin.fulltextsearch import FullTextSearch |
|---|
| 9 | |
|---|
| 10 | class FullTextSearchAdmin(Component): |
|---|
| 11 | """trac-admin command provider for full text search administration. |
|---|
| 12 | """ |
|---|
| 13 | implements(IAdminCommandProvider) |
|---|
| 14 | |
|---|
| 15 | # IAdminCommandProvider methods |
|---|
| 16 | |
|---|
| 17 | def get_admin_commands(self): |
|---|
| 18 | yield ('fulltext index', '[realm]', |
|---|
| 19 | """Index Trac resources that are out of date |
|---|
| 20 | |
|---|
| 21 | When [realm] is specified, only that realm is updated. |
|---|
| 22 | Synchronises the search index with Trac by indexing resources |
|---|
| 23 | that have been added or updated. |
|---|
| 24 | """, |
|---|
| 25 | self._complete_admin_command, self._do_index) |
|---|
| 26 | yield ('fulltext list', '[realm]', |
|---|
| 27 | """List Trac resources that are indexed. |
|---|
| 28 | |
|---|
| 29 | When [realm] is specified, only that realm is listed. |
|---|
| 30 | """, |
|---|
| 31 | self._complete_search_command, self._do_list) |
|---|
| 32 | yield ('fulltext optimize', '', |
|---|
| 33 | """Optimize the search index by merging segments and removing |
|---|
| 34 | stale documents |
|---|
| 35 | |
|---|
| 36 | Optimizing should be performed infrequently (e.g. nightly), if |
|---|
| 37 | at all, since it is very expensive and involves reading and |
|---|
| 38 | re-writing the entire index. NB: if multiple projects share |
|---|
| 39 | an index this will operation will affect all of them. |
|---|
| 40 | """, |
|---|
| 41 | None, self._do_optimize) |
|---|
| 42 | yield ('fulltext reindex', '[realm]', |
|---|
| 43 | """Re-index all Trac resources. |
|---|
| 44 | |
|---|
| 45 | When [realm] is specified, only that realm is re-indexed. |
|---|
| 46 | Discards the search index and recreates it. Note that this |
|---|
| 47 | operation can take a long time to complete. If indexing gets |
|---|
| 48 | interrupted, it can be resumed later using the `index` command. |
|---|
| 49 | """, |
|---|
| 50 | self._complete_admin_command, self._do_reindex) |
|---|
| 51 | yield ('fulltext remove', '[realm]', |
|---|
| 52 | """Remove the search index, or part of it |
|---|
| 53 | |
|---|
| 54 | When [realm] is specified, only that realm is removed from the |
|---|
| 55 | index. |
|---|
| 56 | """, |
|---|
| 57 | self._complete_admin_command, self._do_remove) |
|---|
| 58 | |
|---|
| 59 | def _complete_admin_command(self, args): |
|---|
| 60 | fts = FullTextSearch(self.env) |
|---|
| 61 | if len(args) == 1: |
|---|
| 62 | return PrefixList(fts.index_realms) |
|---|
| 63 | |
|---|
| 64 | def _complete_search_command(self, args): |
|---|
| 65 | fts = FullTextSearch(self.env) |
|---|
| 66 | if len(args) == 1: |
|---|
| 67 | return PrefixList(fts.search_realms) |
|---|
| 68 | |
|---|
| 69 | def _index(self, realm, clean): |
|---|
| 70 | fts = FullTextSearch(self.env) |
|---|
| 71 | realms = realm and [realm] or fts.index_realms |
|---|
| 72 | if clean: |
|---|
| 73 | printout(_("Wiping search index and re-indexing all items in " |
|---|
| 74 | "realms: %(realms)s", realms=fts._fmt_realms(realms))) |
|---|
| 75 | else: |
|---|
| 76 | printout(_("Indexing new and changed items in realms: %(realms)s", |
|---|
| 77 | realms=fts._fmt_realms(realms))) |
|---|
| 78 | fts.index(realms, clean, self._index_feedback, self._clean_feedback) |
|---|
| 79 | printout(_("Indexing finished")) |
|---|
| 80 | |
|---|
| 81 | def _index_feedback(self, realm, resource): |
|---|
| 82 | #sys.stdout.write('\r\x1b[K %s' % (resource,)) |
|---|
| 83 | sys.stdout.flush() |
|---|
| 84 | |
|---|
| 85 | def _clean_feedback(self, realm, resource): |
|---|
| 86 | #sys.stdout.write('\r\x1b[K') |
|---|
| 87 | sys.stdout.flush() |
|---|
| 88 | |
|---|
| 89 | def _do_index(self, realm=None): |
|---|
| 90 | self._index(realm, clean=False) |
|---|
| 91 | |
|---|
| 92 | def _do_list(self, realm=None): |
|---|
| 93 | fts = FullTextSearch(self.env) |
|---|
| 94 | realms = realm and [realm] or fts.index_realms |
|---|
| 95 | fields = ['realm', 'id'] |
|---|
| 96 | query, response = fts._do_search('*', realms, sort_by=fields, |
|---|
| 97 | field_limit=fields) |
|---|
| 98 | rows = ((doc['realm'], doc['id']) for doc in fts._docs(query)) |
|---|
| 99 | print_table(rows, (_("Realm"), _("Id"))) |
|---|
| 100 | |
|---|
| 101 | def _do_optimize(self): |
|---|
| 102 | fts = FullTextSearch(self.env) |
|---|
| 103 | fts.optimize() |
|---|
| 104 | |
|---|
| 105 | def _do_reindex(self, realm=None): |
|---|
| 106 | self._index(realm, clean=True) |
|---|
| 107 | |
|---|
| 108 | def _do_remove(self, realm=None): |
|---|
| 109 | fts = FullTextSearch(self.env) |
|---|
| 110 | realms = realm and [realm] or fts.index_realms |
|---|
| 111 | fts.remove_index(realms) |
|---|
| 112 | |
|---|