From d47df833fbd52292d91421b370df0e3bd1932b06 Mon Sep 17 00:00:00 2001
From: Kazuhiko Shiozaki <kazuhiko@nexedi.com>
Date: Thu, 23 Oct 2014 15:54:43 +0200
Subject: [PATCH] use fulltext search in title and description.

* to quickly setup catalog_full_text table, you can use the following SQL.

  REPLACE INTO catalog_full_text SELECT uid, title, description FROM catalog;

* non fulltext queries like '=abc', '>abc', '%abc%' are supported.

* now erp5_full_text_mroonga_catalog is used for unit tests thus I recommend using it instead of erp5_full_text_myisam_catalog.

* to migrate existing MyISAM full_text table into Mroonga, you can use the following SQL.

  ALTER TABLE full_text DROP KEY SearchableText,
    ENGINE = mroonga,
    ADD FULLTEXT KEY SearchableText (`SearchableText`) COMMENT 'parser "TokenBigramSplitSymbolAlpha"';

* fulltext search score is no longer provided as (column_name) but now provided as (column_name)__score__.

* (category)_title, like source_title, related keys are automatically generated. (category)_description keys as well.
---
 .../related_key_list.xml                      |   8 +-
 .../bt/template_catalog_related_key_list      |   8 +-
 ...gZuite_createAccountingTransactionList.xml |   9 +-
 ...ountingTransactionListSalesAndPayments.xml |   7 +-
 ...teAccountingTransactionListWithPersons.xml |   9 +-
 .../AccountingZuite_reset.xml                 |   3 +-
 .../related_key_list.xml                      |   6 +-
 .../bt/template_catalog_related_key_list      |   6 +-
 bt5/erp5_bearer_token/bt/test_dependency_list |   2 +-
 .../bt/test_dependency_list                   |   2 +-
 .../bt/test_dependency_list                   |   2 +-
 .../bt/test_dependency_list                   |   2 +-
 .../bt/test_dependency_list                   |   2 +-
 .../bt/test_dependency_list                   |   2 +-
 ...entTranslationSearchQuery.catalog_keys.xml |   2 -
 ...alog_makeContentTranslationSearchQuery.xml |  84 ---------
 .../scriptable_key_list.xml                   |   2 +-
 .../bt/template_catalog_scriptable_key_list   |   2 +-
 .../related_key_list.xml                      |   2 +-
 .../bt/template_catalog_related_key_list      |   2 +-
 .../Base_getDemoSMBBusinessTemplateList.xml   |   2 +-
 .../Base_getUngBusinessTemplateList.xml       |   2 +-
 .../bt/test_dependency_list                   |   2 +-
 .../full_text_key_list.xml                    |   4 -
 .../SQLCatalog_deferFullTextIndexActivity.xml |   5 +-
 .../z0_drop_catalog_fulltext.catalog_keys.xml |   5 +
 .../z0_drop_catalog_fulltext.xml              |  70 ++++++++
 ...ncatalog_catalog_fulltext.catalog_keys.xml |   5 +
 .../z0_uncatalog_catalog_fulltext.xml         |  68 ++++++++
 ...log_catalog_fulltext_list.catalog_keys.xml |   5 +
 .../z_catalog_catalog_fulltext_list.xml       |  80 +++++++++
 .../z_catalog_fulltext_list.xml               |  27 +--
 ...z_create_catalog_fulltext.catalog_keys.xml |   5 +
 .../z_create_catalog_fulltext.xml             |  82 +++++++++
 .../z_create_content_translation.xml          |   2 +-
 .../erp5_mysql_innodb/z_create_fulltext.xml   |   2 +-
 .../related_key_list.xml                      |  10 ++
 .../result_table_list.xml                     |   1 +
 .../search_key_list.xml                       |   8 +
 .../bt/template_catalog_full_text_key_list    |   2 -
 .../bt/template_catalog_method_id_list        |   4 +
 .../bt/template_catalog_related_key_list      |   8 +
 .../bt/template_catalog_result_table_list     |   1 +
 .../bt/template_catalog_search_key_list       |   6 +
 .../full_text_key_list.xml                    |   6 +-
 .../SQLCatalog_deferFullTextIndexActivity.xml |   5 +-
 .../z0_drop_catalog_fulltext.catalog_keys.xml |   5 +
 .../z0_drop_catalog_fulltext.xml              |  70 ++++++++
 ...ncatalog_catalog_fulltext.catalog_keys.xml |   5 +
 .../z0_uncatalog_catalog_fulltext.xml         |  68 ++++++++
 ...log_catalog_fulltext_list.catalog_keys.xml |   5 +
 .../z_catalog_catalog_fulltext_list.xml       |  80 +++++++++
 .../z_catalog_fulltext_list.xml               |  27 +--
 ...z_create_catalog_fulltext.catalog_keys.xml |   5 +
 .../z_create_catalog_fulltext.xml             |  82 +++++++++
 .../related_key_list.xml                      |  10 ++
 .../result_table_list.xml                     |   1 +
 .../bt/template_catalog_full_text_key_list    |   6 +-
 .../bt/template_catalog_method_id_list        |   4 +
 .../bt/template_catalog_related_key_list      |   8 +
 .../bt/template_catalog_result_table_list     |   1 +
 bt5/erp5_ingestion/bt/test_dependency_list    |   2 +-
 .../related_key_list.xml                      |   2 +-
 .../bt/template_catalog_related_key_list      |   2 +-
 .../testTopSearchBoxClicking.xml              |   4 +-
 .../bt/test_dependency_list                   |   2 +-
 .../bt/test_dependency_list                   |   2 +-
 bt5/erp5_pdf_merge/bt/test_dependency_list    |   2 +-
 .../bt/test_dependency_list                   |   2 +-
 bt5/erp5_safeimage/bt/test_dependency_list    |   2 +-
 .../bt/test_dependency_list                   |   2 +-
 .../bt/test_dependency_list                   |   2 +-
 ...ocument.erp5.ERP5ScalabilityDistributor.py |   9 +-
 bt5/erp5_test_result/bt/test_dependency_list  |   2 +-
 .../testOxatisSynchronization.py              |   2 +-
 ...ltiRelationFieldSearchDialogChangePage.xml |  10 +-
 .../testSimpleRelationFieldWithListbox.xml    |   2 +-
 .../testSimpleRequiredRelationField.xml       |   4 +-
 bt5/erp5_upgrader/bt/test_dependency_list     |   2 +-
 ...emplateTool_checkPreUpgradeConsistency.xml |   2 +-
 bt5/erp5_web_shacache/bt/test_dependency_list |   2 +-
 bt5/erp5_web_shadir/bt/test_dependency_list   |   2 +-
 bt5/erp5_web_ung_core/bt/test_dependency_list |   2 +-
 bt5/erp5_web_ung_role/bt/test_dependency_list |   2 +-
 .../bt/test_dependency_list                   |   2 +-
 bt5/networkcache_erp5/bt/test_dependency_list |   2 +-
 .../bt/test_dependency_list                   |   2 +-
 .../ERP5ProjectUnitTestDistributor.py         |  28 ++-
 product/ERP5/Tool/TaskDistributionTool.py     |   7 +-
 .../erp5_core/bt/test_dependency_list         |   2 +-
 .../SQLCatalog_makeTranslatedTitleQuery.xml   |   8 +-
 product/ERP5/tests/testCRM.py                 |   4 +-
 product/ERP5/tests/testERP5Coordinate.py      |   2 +-
 product/ERP5/tests/testERP5Credential.py      |   2 +-
 product/ERP5/tests/testERP5eGov.py            |   2 +-
 .../tests/testFunctionalRunMyDocSample.py     |   2 +-
 product/ERP5/tests/testI18NSearch.py          |  23 ++-
 product/ERP5/tests/testKM.py                  |   2 +-
 product/ERP5/tests/testSpellChecking.py       |   2 +-
 product/ERP5/tests/testTemplateTool.py        |   4 +-
 product/ERP5Catalog/CatalogTool.py            |  22 ++-
 product/ERP5Catalog/tests/testArchive.py      |   2 +-
 product/ERP5Catalog/tests/testERP5Catalog.py  | 165 +++++++++---------
 .../testERP5CatalogSecurityUidOptimization.py |   2 +-
 .../tests/ConfiguratorTestMixin.py            |   2 +-
 .../tests/testConfiguratorItem.py             |   2 +-
 .../tests/testConfiguratorTool.py             |   2 +-
 .../tests/testFunctionalConfigurator.py       |   2 +-
 product/ERP5Form/Tool/SelectionTool.py        |   3 +-
 product/ERP5OOo/tests/testDms.py              |  10 +-
 product/ERP5OOo/tests/testOOoDynamicStyle.py  |   2 +-
 product/ERP5TioSafe/tests/testTioSafeMixin.py |   2 +-
 .../tests/testFunctionalAnonymousSelection.py |   2 +-
 product/ERP5Type/tests/testFunctionalCore.py  |   2 +-
 product/ERP5Type/tests/testFunctionalKM.py    |   2 +-
 .../testFunctionalStandaloneUserTutorial.py   |   2 +-
 .../ZMySQLDA/tests/testDeferredConnection.py  |   2 +-
 .../Operator/ComparisonOperator.py            |  54 +++++-
 product/ZSQLCatalog/Query/RelatedQuery.py     |   6 +-
 product/ZSQLCatalog/SQLExpression.py          |   3 +
 product/ZSQLCatalog/SearchKey/FullTextKey.py  |  36 +++-
 .../SearchKey/MroongaBooleanFullTextKey.py    |  40 +++++
 .../SearchKey/MroongaFullTextKey.py           |  92 ++++++++++
 product/ZSQLCatalog/tests/testSQLCatalog.py   |  94 ++++++----
 124 files changed, 1291 insertions(+), 375 deletions(-)
 delete mode 100644 bt5/erp5_content_translation/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_makeContentTranslationSearchQuery.catalog_keys.xml
 delete mode 100644 bt5/erp5_content_translation/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_makeContentTranslationSearchQuery.xml
 delete mode 100644 bt5/erp5_full_text_mroonga_catalog/CatalogFullTextKeyTemplateItem/full_text_key_list.xml
 create mode 100644 bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.catalog_keys.xml
 create mode 100644 bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.xml
 create mode 100644 bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.catalog_keys.xml
 create mode 100644 bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.xml
 create mode 100644 bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.catalog_keys.xml
 create mode 100644 bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.xml
 create mode 100644 bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.catalog_keys.xml
 create mode 100644 bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.xml
 create mode 100644 bt5/erp5_full_text_mroonga_catalog/CatalogRelatedKeyTemplateItem/related_key_list.xml
 create mode 100644 bt5/erp5_full_text_mroonga_catalog/CatalogSearchKeyTemplateItem/search_key_list.xml
 delete mode 100644 bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_full_text_key_list
 create mode 100644 bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_related_key_list
 create mode 100644 bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_search_key_list
 create mode 100644 bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.catalog_keys.xml
 create mode 100644 bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.xml
 create mode 100644 bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.catalog_keys.xml
 create mode 100644 bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.xml
 create mode 100644 bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.catalog_keys.xml
 create mode 100644 bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.xml
 create mode 100644 bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.catalog_keys.xml
 create mode 100644 bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.xml
 create mode 100644 bt5/erp5_full_text_myisam_catalog/CatalogRelatedKeyTemplateItem/related_key_list.xml
 create mode 100644 bt5/erp5_full_text_myisam_catalog/bt/template_catalog_related_key_list
 create mode 100644 product/ZSQLCatalog/SearchKey/MroongaBooleanFullTextKey.py
 create mode 100644 product/ZSQLCatalog/SearchKey/MroongaFullTextKey.py

diff --git a/bt5/erp5_accounting/CatalogRelatedKeyTemplateItem/related_key_list.xml b/bt5/erp5_accounting/CatalogRelatedKeyTemplateItem/related_key_list.xml
index debd7365b9..7016a85a28 100644
--- a/bt5/erp5_accounting/CatalogRelatedKeyTemplateItem/related_key_list.xml
+++ b/bt5/erp5_accounting/CatalogRelatedKeyTemplateItem/related_key_list.xml
@@ -1,10 +1,10 @@
 <key_list>
  <key>accounting_transaction_line_node_uid | stock/node_uid/z_related_accounting_transaction_stock_line</key>
  <key>accounting_transaction_line_total_price | stock/total_price/z_related_accounting_transaction_stock_line</key>
- <key>accounting_transaction_mirror_section_title | catalog/title/z_related_accounting_transaction_mirror_section</key>
- <key>accounting_transaction_payment_title | catalog/title/z_related_accounting_transaction_payment</key>
- <key>accounting_transaction_project_title | catalog/title/z_related_accounting_transaction_project</key>
- <key>accounting_transaction_section_title | catalog/title/z_related_accounting_transaction_section</key>
+ <key>accounting_transaction_mirror_section_title | catalog_full_text/title/z_related_accounting_transaction_mirror_section</key>
+ <key>accounting_transaction_payment_title | catalog_full_text/title/z_related_accounting_transaction_payment</key>
+ <key>accounting_transaction_project_title | catalog_full_text/title/z_related_accounting_transaction_project</key>
+ <key>accounting_transaction_section_title | catalog_full_text/title/z_related_accounting_transaction_section</key>
  <key>preferred_gap_id | category,catalog/id/z_related_preferred_gap</key>
  <key>preferred_gap_strict_membership_id | category,catalog/id/z_related_strict_membership_preferred_gap</key>
 </key_list>
\ No newline at end of file
diff --git a/bt5/erp5_accounting/bt/template_catalog_related_key_list b/bt5/erp5_accounting/bt/template_catalog_related_key_list
index dcde7ac950..1f5a0ffbcf 100644
--- a/bt5/erp5_accounting/bt/template_catalog_related_key_list
+++ b/bt5/erp5_accounting/bt/template_catalog_related_key_list
@@ -1,8 +1,8 @@
 preferred_gap_id | category,catalog/id/z_related_preferred_gap
 preferred_gap_strict_membership_id | category,catalog/id/z_related_strict_membership_preferred_gap
-accounting_transaction_mirror_section_title | catalog/title/z_related_accounting_transaction_mirror_section
-accounting_transaction_section_title | catalog/title/z_related_accounting_transaction_section
-accounting_transaction_project_title | catalog/title/z_related_accounting_transaction_project
-accounting_transaction_payment_title | catalog/title/z_related_accounting_transaction_payment
+accounting_transaction_mirror_section_title | catalog_full_text/title/z_related_accounting_transaction_mirror_section
+accounting_transaction_section_title | catalog_full_text/title/z_related_accounting_transaction_section
+accounting_transaction_project_title | catalog_full_text/title/z_related_accounting_transaction_project
+accounting_transaction_payment_title | catalog_full_text/title/z_related_accounting_transaction_payment
 accounting_transaction_line_node_uid | stock/node_uid/z_related_accounting_transaction_stock_line
 accounting_transaction_line_total_price | stock/total_price/z_related_accounting_transaction_stock_line
\ No newline at end of file
diff --git a/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_createAccountingTransactionList.xml b/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_createAccountingTransactionList.xml
index 21b349b51b..effbdeb77c 100644
--- a/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_createAccountingTransactionList.xml
+++ b/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_createAccountingTransactionList.xml
@@ -53,6 +53,7 @@
             <value> <string encoding="cdata"><![CDATA[
 
 from DateTime import DateTime\n
+from Products.ZSQLCatalog.SQLCatalog import SimpleQuery\n
 \n
 # params\n
 section_title = \'My Organisation\'\n
@@ -90,7 +91,7 @@ if 1:\n
 def getAccountByTitle(title):\n
   account_list = [x.getObject().getRelativeUrl() for x in\n
     portal.portal_catalog(portal_type=\'Account\',\n
-                          title=title)]\n
+                          title=SimpleQuery(title=title, comparison_operator=\'=\'))]\n
   assert len(account_list) == 1, \\\n
         \'%d account with title "%s"\' % (len(account_list), title)\n
   return account_list[0]\n
@@ -98,7 +99,7 @@ def getAccountByTitle(title):\n
 def getOrganisationByTitle(title):\n
   document_list = [x.getObject().getRelativeUrl() for x in\n
     portal.portal_catalog(portal_type=\'Organisation\',\n
-                          title=title)]\n
+                          title=SimpleQuery(title=title, comparison_operator=\'=\'))]\n
   assert len(document_list) == 1, \\\n
         \'%d organisation with title "%s"\' % (len(document_list), title)\n
   return document_list[0]\n
@@ -109,14 +110,14 @@ euro_resource = \'currency_module/euro\'\n
 def getBankAccountByTitle(title):\n
   document_list = [x.getObject().getRelativeUrl() for x in\n
     portal.portal_catalog(portal_type=\'Bank Account\',\n
-                          title=title)]\n
+                          title=SimpleQuery(title=title, comparison_operator=\'=\'))]\n
   assert len(document_list) == 1, \\\n
       \'%d Bank Account with title "%s"\' % (len(document_list), title)\n
   return document_list[0]\n
 \n
 product_list = [o.getObject() for o in portal.portal_catalog(\n
                                   portal_type=\'Product\',\n
-                                  title=\'Dummy Product for testing\')]\n
+                                  title=SimpleQuery(title=\'Dummy Product for testing\', comparison_operator=\'=\'))]\n
 if product_list:\n
   product = product_list[0]\n
 else:\n
diff --git a/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_createAccountingTransactionListSalesAndPayments.xml b/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_createAccountingTransactionListSalesAndPayments.xml
index 0e6934a163..3f973fb2a2 100644
--- a/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_createAccountingTransactionListSalesAndPayments.xml
+++ b/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_createAccountingTransactionListSalesAndPayments.xml
@@ -53,6 +53,7 @@
             <value> <string encoding="cdata"><![CDATA[
 
 from DateTime import DateTime\n
+from Products.ZSQLCatalog.SQLCatalog import SimpleQuery\n
 \n
 # params\n
 section_title = \'My Organisation\'\n
@@ -80,7 +81,7 @@ if 1:\n
 def getAccountByTitle(title):\n
   account_list = [x.getObject().getRelativeUrl() for x in\n
     portal.portal_catalog(portal_type=\'Account\',\n
-                          title=title)]\n
+                          title=SimpleQuery(title=title, comparison_operator=\'=\'))]\n
   assert len(account_list) == 1, \\\n
         \'%d account with title "%s"\' % (len(account_list), title)\n
   return account_list[0]\n
@@ -88,7 +89,7 @@ def getAccountByTitle(title):\n
 def getOrganisationByTitle(title):\n
   document_list = [x.getObject().getRelativeUrl() for x in\n
     portal.portal_catalog(portal_type=\'Organisation\',\n
-                          title=title)]\n
+                          title=SimpleQuery(title=title, comparison_operator=\'=\'))]\n
   assert len(document_list) == 1, \\\n
         \'%d organisation with title "%s"\' % (len(document_list), title)\n
   return document_list[0]\n
@@ -106,7 +107,7 @@ euro_resource = getCurrencyByReference(\'EUR\')\n
 def getBankAccountByTitle(title):\n
   document_list = [x.getObject().getRelativeUrl() for x in\n
     portal.portal_catalog(portal_type=\'Bank Account\',\n
-                          title=title)]\n
+                          title=SimpleQuery(title=title, comparison_operator=\'=\'))]\n
   assert len(document_list) == 1, \\\n
       \'%d Bank Account with title "%s"\' % (len(document_list), title)\n
   return document_list[0]\n
diff --git a/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_createAccountingTransactionListWithPersons.xml b/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_createAccountingTransactionListWithPersons.xml
index 14e0a758d8..2d27b310af 100644
--- a/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_createAccountingTransactionListWithPersons.xml
+++ b/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_createAccountingTransactionListWithPersons.xml
@@ -58,6 +58,7 @@ business_process = \'business_process_module/erp5_default_business_process\'\n
 portal = context.getPortalObject()\n
 accounting_module = portal.accounting_module\n
 from DateTime import DateTime\n
+from Products.ZSQLCatalog.SQLCatalog import SimpleQuery\n
 year = 2005\n
 \n
 # if the previous test didn\'t change input data, no need to recreate content\n
@@ -82,7 +83,7 @@ if 1:\n
 def getAccountByTitle(title):\n
   account_list = [x.getObject().getRelativeUrl() for x in\n
     portal.portal_catalog(portal_type=\'Account\',\n
-                          title=title)]\n
+                          title=SimpleQuery(title=title, comparison_operator=\'=\'))]\n
   assert len(account_list) == 1, \\\n
         \'%d account with title "%s"\' % (len(account_list), title)\n
   return account_list[0]\n
@@ -90,7 +91,7 @@ def getAccountByTitle(title):\n
 def getOrganisationByTitle(title):\n
   document_list = [x.getObject().getRelativeUrl() for x in\n
     portal.portal_catalog(portal_type=\'Organisation\',\n
-                          title=title)]\n
+                          title=SimpleQuery(title=title, comparison_operator=\'=\'))]\n
   assert len(document_list) == 1, \\\n
         \'%d organisation with title "%s"\' % (len(document_list), title)\n
   return document_list[0]\n
@@ -99,7 +100,7 @@ section = getOrganisationByTitle(section_title)\n
 def getPersonByTitle(title):\n
   document_list = [x.getObject().getRelativeUrl() for x in\n
     portal.portal_catalog(portal_type=\'Person\',\n
-                          title=title)]\n
+                          title=SimpleQuery(title=title, comparison_operator=\'=\'))]\n
   assert len(document_list) == 1, \\\n
         \'%d person with title "%s"\' % (len(document_list), title)\n
   return document_list[0]\n
@@ -116,7 +117,7 @@ euro_resource = getCurrencyByReference(\'EUR\')\n
 def getBankAccountByTitle(title):\n
   document_list = [x.getObject().getRelativeUrl() for x in\n
     portal.portal_catalog(portal_type=\'Bank Account\',\n
-                          title=title)]\n
+                          title=SimpleQuery(title=title, comparison_operator=\'=\'))]\n
   assert len(document_list) == 1, \\\n
       \'%d Bank Account with title "%s"\' % (len(document_list), title)\n
   return document_list[0]\n
diff --git a/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_reset.xml b/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_reset.xml
index c3932bd98b..e310984696 100644
--- a/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_reset.xml
+++ b/bt5/erp5_accounting_ui_test/SkinTemplateItem/portal_skins/erp5_accounting_ui_test/AccountingZuite_reset.xml
@@ -51,6 +51,7 @@
         <item>
             <key> <string>_body</string> </key>
             <value> <string>portal = context.getPortalObject()\n
+from Products.ZSQLCatalog.SQLCatalog import SimpleQuery\n
 \n
 # validate rules\n
 for rule in portal.portal_rules.objectValues():\n
@@ -129,7 +130,7 @@ stool.setSelectionColumns(\'account_module_selection\',\n
 \n
 # delete the "dummy account" we create in test_account_gap_parallel_list_field\n
 dummy_account_list = portal.account_module.searchFolder(\n
-                                        title=\'Dummy Account for UI Test\')\n
+  title=SimpleQuery(title=\'Dummy Account for UI Test\', comparison_operator=\'=\'))\n
 if dummy_account_list:\n
   portal.account_module.manage_delObjects([dummy_account_list[0].getId()])\n
 \n
diff --git a/bt5/erp5_base/CatalogRelatedKeyTemplateItem/related_key_list.xml b/bt5/erp5_base/CatalogRelatedKeyTemplateItem/related_key_list.xml
index 59d2ac3ede..08872aafea 100644
--- a/bt5/erp5_base/CatalogRelatedKeyTemplateItem/related_key_list.xml
+++ b/bt5/erp5_base/CatalogRelatedKeyTemplateItem/related_key_list.xml
@@ -2,8 +2,8 @@
  <key>child_address_SearchableText | catalog,full_text/SearchableText/z_related_child_address</key>
  <key>child_telephone_SearchableText | catalog,full_text/SearchableText/z_related_child_telephone</key>
  <key>default_email_text | catalog,email/url_string/z_related_default_email</key>
- <key>destination_person_title | category,catalog/title/z_related_destination_person</key>
+ <key>destination_person_title | category,catalog_full_text/title/z_related_destination_person</key>
  <key>related_resource_use_uid | category,category,catalog,catalog/uid/z_related_resource_use</key>
- <key>source_organisation_title | category,catalog/title/z_related_source_organisation</key>
- <key>source_person_title | category,catalog/title/z_related_source_person</key>
+ <key>source_organisation_title | category,catalog_full_text/title/z_related_source_organisation</key>
+ <key>source_person_title | category,catalog_full_text/title/z_related_source_person</key>
 </key_list>
\ No newline at end of file
diff --git a/bt5/erp5_base/bt/template_catalog_related_key_list b/bt5/erp5_base/bt/template_catalog_related_key_list
index 95319495c6..faf6e7a859 100644
--- a/bt5/erp5_base/bt/template_catalog_related_key_list
+++ b/bt5/erp5_base/bt/template_catalog_related_key_list
@@ -1,6 +1,6 @@
-source_organisation_title | category,catalog/title/z_related_source_organisation
-source_person_title | category,catalog/title/z_related_source_person
-destination_person_title | category,catalog/title/z_related_destination_person
+source_organisation_title | category,catalog_full_text/title/z_related_source_organisation
+source_person_title | category,catalog_full_text/title/z_related_source_person
+destination_person_title | category,catalog_full_text/title/z_related_destination_person
 default_email_text | catalog,email/url_string/z_related_default_email
 related_resource_use_uid | category,category,catalog,catalog/uid/z_related_resource_use
 child_address_SearchableText | catalog,full_text/SearchableText/z_related_child_address
diff --git a/bt5/erp5_bearer_token/bt/test_dependency_list b/bt5/erp5_bearer_token/bt/test_dependency_list
index 418a1a563a..bff77e32d8 100644
--- a/bt5/erp5_bearer_token/bt/test_dependency_list
+++ b/bt5/erp5_bearer_token/bt/test_dependency_list
@@ -1 +1 @@
-erp5_full_text_myisam_catalog
\ No newline at end of file
+erp5_full_text_mroonga_catalog
\ No newline at end of file
diff --git a/bt5/erp5_configurator_ebusiness_lotse/bt/test_dependency_list b/bt5/erp5_configurator_ebusiness_lotse/bt/test_dependency_list
index 418a1a563a..bff77e32d8 100644
--- a/bt5/erp5_configurator_ebusiness_lotse/bt/test_dependency_list
+++ b/bt5/erp5_configurator_ebusiness_lotse/bt/test_dependency_list
@@ -1 +1 @@
-erp5_full_text_myisam_catalog
\ No newline at end of file
+erp5_full_text_mroonga_catalog
\ No newline at end of file
diff --git a/bt5/erp5_configurator_maxma_demo/bt/test_dependency_list b/bt5/erp5_configurator_maxma_demo/bt/test_dependency_list
index 8165f76e00..fd4ff5b6e3 100644
--- a/bt5/erp5_configurator_maxma_demo/bt/test_dependency_list
+++ b/bt5/erp5_configurator_maxma_demo/bt/test_dependency_list
@@ -1,4 +1,4 @@
 erp5_core_proxy_field_legacy
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_base
 erp5_workflow
\ No newline at end of file
diff --git a/bt5/erp5_configurator_run_my_doc/bt/test_dependency_list b/bt5/erp5_configurator_run_my_doc/bt/test_dependency_list
index 8165f76e00..fd4ff5b6e3 100644
--- a/bt5/erp5_configurator_run_my_doc/bt/test_dependency_list
+++ b/bt5/erp5_configurator_run_my_doc/bt/test_dependency_list
@@ -1,4 +1,4 @@
 erp5_core_proxy_field_legacy
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_base
 erp5_workflow
\ No newline at end of file
diff --git a/bt5/erp5_configurator_standard/bt/test_dependency_list b/bt5/erp5_configurator_standard/bt/test_dependency_list
index 418a1a563a..bff77e32d8 100644
--- a/bt5/erp5_configurator_standard/bt/test_dependency_list
+++ b/bt5/erp5_configurator_standard/bt/test_dependency_list
@@ -1 +1 @@
-erp5_full_text_myisam_catalog
\ No newline at end of file
+erp5_full_text_mroonga_catalog
\ No newline at end of file
diff --git a/bt5/erp5_configurator_ung/bt/test_dependency_list b/bt5/erp5_configurator_ung/bt/test_dependency_list
index 8165f76e00..fd4ff5b6e3 100644
--- a/bt5/erp5_configurator_ung/bt/test_dependency_list
+++ b/bt5/erp5_configurator_ung/bt/test_dependency_list
@@ -1,4 +1,4 @@
 erp5_core_proxy_field_legacy
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_base
 erp5_workflow
\ No newline at end of file
diff --git a/bt5/erp5_content_translation/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_makeContentTranslationSearchQuery.catalog_keys.xml b/bt5/erp5_content_translation/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_makeContentTranslationSearchQuery.catalog_keys.xml
deleted file mode 100644
index a540f9431e..0000000000
--- a/bt5/erp5_content_translation/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_makeContentTranslationSearchQuery.catalog_keys.xml
+++ /dev/null
@@ -1,2 +0,0 @@
-<catalog_method>
-</catalog_method>
diff --git a/bt5/erp5_content_translation/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_makeContentTranslationSearchQuery.xml b/bt5/erp5_content_translation/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_makeContentTranslationSearchQuery.xml
deleted file mode 100644
index 0f25ec0a9e..0000000000
--- a/bt5/erp5_content_translation/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_makeContentTranslationSearchQuery.xml
+++ /dev/null
@@ -1,84 +0,0 @@
-<?xml version="1.0"?>
-<ZopeData>
-  <record id="1" aka="AAAAAAAAAAE=">
-    <pickle>
-      <global name="PythonScript" module="Products.PythonScripts.PythonScript"/>
-    </pickle>
-    <pickle>
-      <dictionary>
-        <item>
-            <key> <string>Script_magic</string> </key>
-            <value> <int>3</int> </value>
-        </item>
-        <item>
-            <key> <string>_bind_names</string> </key>
-            <value>
-              <object>
-                <klass>
-                  <global name="NameAssignments" module="Shared.DC.Scripts.Bindings"/>
-                </klass>
-                <tuple/>
-                <state>
-                  <dictionary>
-                    <item>
-                        <key> <string>_asgns</string> </key>
-                        <value>
-                          <dictionary>
-                            <item>
-                                <key> <string>name_container</string> </key>
-                                <value> <string>container</string> </value>
-                            </item>
-                            <item>
-                                <key> <string>name_context</string> </key>
-                                <value> <string>context</string> </value>
-                            </item>
-                            <item>
-                                <key> <string>name_m_self</string> </key>
-                                <value> <string>script</string> </value>
-                            </item>
-                            <item>
-                                <key> <string>name_subpath</string> </key>
-                                <value> <string>traverse_subpath</string> </value>
-                            </item>
-                          </dictionary>
-                        </value>
-                    </item>
-                  </dictionary>
-                </state>
-              </object>
-            </value>
-        </item>
-        <item>
-            <key> <string>_body</string> </key>
-            <value> <string>from Products.ZSQLCatalog.SQLCatalog import Query, SimpleQuery, AndQuery\n
-\n
-########### CONFIGURATION ######################################\n
-# This is a query for search translated properties of Person   #\n
-# documents. If you want to get other results. Customise this. # \n
-################################################################\n
-\n
-query = AndQuery(SimpleQuery(**{\'content_translation.translated_text\': value, \'comparison_operator\': \'match\'}),\n
-                 Query(**{\'content_translation.property_name\': \'title\'})\n
-                 )\n
-\n
-################################################################\n
-# Above query must make SQL condition like this.               #\n
-################################################################\n
-#     MATCH(content_translation.translated_text) AGAINST({value})\n
-# AND content_translation.property_name = \'title\'\n
-\n
-return query\n
-</string> </value>
-        </item>
-        <item>
-            <key> <string>_params</string> </key>
-            <value> <string>value</string> </value>
-        </item>
-        <item>
-            <key> <string>id</string> </key>
-            <value> <string>SQLCatalog_makeContentTranslationSearchQuery</string> </value>
-        </item>
-      </dictionary>
-    </pickle>
-  </record>
-</ZopeData>
diff --git a/bt5/erp5_content_translation/CatalogScriptableKeyTemplateItem/scriptable_key_list.xml b/bt5/erp5_content_translation/CatalogScriptableKeyTemplateItem/scriptable_key_list.xml
index fbac1af553..be73552308 100644
--- a/bt5/erp5_content_translation/CatalogScriptableKeyTemplateItem/scriptable_key_list.xml
+++ b/bt5/erp5_content_translation/CatalogScriptableKeyTemplateItem/scriptable_key_list.xml
@@ -1,3 +1,3 @@
 <key_list>
- <key>content_translation_title | SQLCatalog_makeContentTranslationSearchQuery</key>
+ <key>content_translation_title | SQLCatalog_makeTranslatedTitleQuery</key>
 </key_list>
\ No newline at end of file
diff --git a/bt5/erp5_content_translation/bt/template_catalog_scriptable_key_list b/bt5/erp5_content_translation/bt/template_catalog_scriptable_key_list
index cbacbbd1d7..7544db4c95 100644
--- a/bt5/erp5_content_translation/bt/template_catalog_scriptable_key_list
+++ b/bt5/erp5_content_translation/bt/template_catalog_scriptable_key_list
@@ -1 +1 @@
-content_translation_title | SQLCatalog_makeContentTranslationSearchQuery
\ No newline at end of file
+content_translation_title | SQLCatalog_makeTranslatedTitleQuery
\ No newline at end of file
diff --git a/bt5/erp5_crm/CatalogRelatedKeyTemplateItem/related_key_list.xml b/bt5/erp5_crm/CatalogRelatedKeyTemplateItem/related_key_list.xml
index 38675f5f79..b540f4bd62 100644
--- a/bt5/erp5_crm/CatalogRelatedKeyTemplateItem/related_key_list.xml
+++ b/bt5/erp5_crm/CatalogRelatedKeyTemplateItem/related_key_list.xml
@@ -6,5 +6,5 @@
  <key>related_source_or_source_decision_or_destination_or_destination_decision | category,catalog,catalog/uid/z_related_source_or_source_decision_or_destination_or_destination_decision</key>
  <key>related_source_or_source_section_or_destination_or_destination_section | category,catalog,catalog/uid/z_related_source_or_source_section_or_destination_or_destination_section</key>
  <key>related_source_section_or_destination_section | category,catalog,catalog/uid/z_related_source_section_or_destination_section</key>
- <key>source_organisation_title | category,catalog/title/z_related_source_organisation</key>
+ <key>source_organisation_title | category,catalog_full_text/title/z_related_source_organisation</key>
 </key_list>
\ No newline at end of file
diff --git a/bt5/erp5_crm/bt/template_catalog_related_key_list b/bt5/erp5_crm/bt/template_catalog_related_key_list
index 8825e0c679..ea64c18e6d 100644
--- a/bt5/erp5_crm/bt/template_catalog_related_key_list
+++ b/bt5/erp5_crm/bt/template_catalog_related_key_list
@@ -1,4 +1,4 @@
-source_organisation_title | category,catalog/title/z_related_source_organisation
+source_organisation_title | category,catalog_full_text/title/z_related_source_organisation
 event_causality_ticket_uid | category,catalog,category,catalog/uid/z_related_event_causality_ticket
 related_source_or_destination | category,catalog,catalog/uid/z_related_source_or_destination
 related_source_section_or_destination_section | category,catalog,catalog/uid/z_related_source_section_or_destination_section
diff --git a/bt5/erp5_demo_smb/SkinTemplateItem/portal_skins/erp5_demo_smb/Base_getDemoSMBBusinessTemplateList.xml b/bt5/erp5_demo_smb/SkinTemplateItem/portal_skins/erp5_demo_smb/Base_getDemoSMBBusinessTemplateList.xml
index c72eb11845..0c38b2b833 100644
--- a/bt5/erp5_demo_smb/SkinTemplateItem/portal_skins/erp5_demo_smb/Base_getDemoSMBBusinessTemplateList.xml
+++ b/bt5/erp5_demo_smb/SkinTemplateItem/portal_skins/erp5_demo_smb/Base_getDemoSMBBusinessTemplateList.xml
@@ -51,7 +51,7 @@
         <item>
             <key> <string>_body</string> </key>
             <value> <string>return (\'erp5_core_proxy_field_legacy\',\n
-        \'erp5_full_text_myisam_catalog\',\n
+        \'erp5_full_text_mroonga_catalog\',\n
         \'erp5_base\',\n
         \'erp5_workflow\',\n
         \'erp5_configurator\',\n
diff --git a/bt5/erp5_demo_ung/SkinTemplateItem/portal_skins/erp5_demo_ung/Base_getUngBusinessTemplateList.xml b/bt5/erp5_demo_ung/SkinTemplateItem/portal_skins/erp5_demo_ung/Base_getUngBusinessTemplateList.xml
index 4218fc05eb..9fef9538ee 100644
--- a/bt5/erp5_demo_ung/SkinTemplateItem/portal_skins/erp5_demo_ung/Base_getUngBusinessTemplateList.xml
+++ b/bt5/erp5_demo_ung/SkinTemplateItem/portal_skins/erp5_demo_ung/Base_getUngBusinessTemplateList.xml
@@ -51,7 +51,7 @@
         <item>
             <key> <string>_body</string> </key>
             <value> <string>return (\'erp5_core_proxy_field_legacy\',\n
-        \'erp5_full_text_myisam_catalog\',\n
+        \'erp5_full_text_mroonga_catalog\',\n
         \'erp5_base\',\n
         \'erp5_simulation\',\n
         \'erp5_dhtml_style\',\n
diff --git a/bt5/erp5_forum_tutorial/bt/test_dependency_list b/bt5/erp5_forum_tutorial/bt/test_dependency_list
index ac2d8e0533..9560d7ad37 100644
--- a/bt5/erp5_forum_tutorial/bt/test_dependency_list
+++ b/bt5/erp5_forum_tutorial/bt/test_dependency_list
@@ -1,4 +1,4 @@
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_base
 erp5_web
 erp5_ingestion_mysql_innodb_catalog
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogFullTextKeyTemplateItem/full_text_key_list.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogFullTextKeyTemplateItem/full_text_key_list.xml
deleted file mode 100644
index 662e00e9f7..0000000000
--- a/bt5/erp5_full_text_mroonga_catalog/CatalogFullTextKeyTemplateItem/full_text_key_list.xml
+++ /dev/null
@@ -1,4 +0,0 @@
-<key_list>
- <key>SearchableText</key>
- <key>full_text.SearchableText</key>
-</key_list>
\ No newline at end of file
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_deferFullTextIndexActivity.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_deferFullTextIndexActivity.xml
index 99f8fa0c33..38959380dd 100644
--- a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_deferFullTextIndexActivity.xml
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_deferFullTextIndexActivity.xml
@@ -68,8 +68,9 @@ for path in path_list:\n
   try:\n
     tmp_dict = {}\n
     for property in property_list:\n
-      if property == \'SearchableText\':\n
-        value = obj.SearchableText()\n
+      getter = getattr(obj, property, None)\n
+      if getter is not None and callable(getter):\n
+        value = getter()\n
       else:\n
         value = getattr(obj, \'get%s\' % UpperCase(property))()\n
       tmp_dict[property] = value\n
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.catalog_keys.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.catalog_keys.xml
new file mode 100644
index 0000000000..b0fe64af00
--- /dev/null
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.catalog_keys.xml
@@ -0,0 +1,5 @@
+<catalog_method>
+ <item key="sql_clear_catalog" type="int">
+  <value>1</value>
+ </item>
+</catalog_method>
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.xml
new file mode 100644
index 0000000000..d9dc3890ee
--- /dev/null
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="SQL" module="Products.ZSQLMethods.SQL"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_col</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>allow_simple_one_argument_traversal</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>arguments_src</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>cache_time_</string> </key>
+            <value> <int>0</int> </value>
+        </item>
+        <item>
+            <key> <string>class_file_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>class_name_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>connection_hook</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>connection_id</string> </key>
+            <value> <string>erp5_sql_connection</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>z0_drop_catalog_fulltext</string> </value>
+        </item>
+        <item>
+            <key> <string>max_cache_</string> </key>
+            <value> <int>100</int> </value>
+        </item>
+        <item>
+            <key> <string>max_rows_</string> </key>
+            <value> <int>1000</int> </value>
+        </item>
+        <item>
+            <key> <string>src</string> </key>
+            <value> <string>DROP TABLE IF EXISTS catalog_full_text</string> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string></string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.catalog_keys.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.catalog_keys.xml
new file mode 100644
index 0000000000..d29d36d9f0
--- /dev/null
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.catalog_keys.xml
@@ -0,0 +1,5 @@
+<catalog_method>
+ <item key="sql_uncatalog_object" type="int">
+  <value>1</value>
+ </item>
+</catalog_method>
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.xml
new file mode 100644
index 0000000000..d9493f4955
--- /dev/null
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="SQL" module="Products.ZSQLMethods.SQL"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>allow_simple_one_argument_traversal</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>arguments_src</string> </key>
+            <value> <string>uid</string> </value>
+        </item>
+        <item>
+            <key> <string>cache_time_</string> </key>
+            <value> <int>0</int> </value>
+        </item>
+        <item>
+            <key> <string>class_file_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>class_name_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>connection_hook</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>connection_id</string> </key>
+            <value> <string>erp5_sql_deferred_connection</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>z0_uncatalog_catalog_fulltext</string> </value>
+        </item>
+        <item>
+            <key> <string>max_cache_</string> </key>
+            <value> <int>100</int> </value>
+        </item>
+        <item>
+            <key> <string>max_rows_</string> </key>
+            <value> <int>1000</int> </value>
+        </item>
+        <item>
+            <key> <string>src</string> </key>
+            <value> <string encoding="cdata"><![CDATA[
+
+DELETE FROM catalog_full_text WHERE <dtml-sqltest uid op=eq type=int>
+
+]]></string> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string></string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.catalog_keys.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.catalog_keys.xml
new file mode 100644
index 0000000000..c95a68655e
--- /dev/null
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.catalog_keys.xml
@@ -0,0 +1,5 @@
+<catalog_method>
+ <item key="sql_catalog_object_list" type="int">
+  <value>1</value>
+ </item>
+</catalog_method>
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.xml
new file mode 100644
index 0000000000..06e374fa75
--- /dev/null
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="SQL" module="Products.ZSQLMethods.SQL"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>allow_simple_one_argument_traversal</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>arguments_src</string> </key>
+            <value> <string>uid\r\n
+getTitle\r\n
+getDescription</string> </value>
+        </item>
+        <item>
+            <key> <string>cache_time_</string> </key>
+            <value> <int>0</int> </value>
+        </item>
+        <item>
+            <key> <string>class_file_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>class_name_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>connection_hook</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>connection_id</string> </key>
+            <value> <string>erp5_sql_deferred_connection</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>z_catalog_catalog_fulltext_list</string> </value>
+        </item>
+        <item>
+            <key> <string>max_cache_</string> </key>
+            <value> <int>100</int> </value>
+        </item>
+        <item>
+            <key> <string>max_rows_</string> </key>
+            <value> <int>1000</int> </value>
+        </item>
+        <item>
+            <key> <string>src</string> </key>
+            <value> <string encoding="cdata"><![CDATA[
+
+REPLACE INTO\n
+  catalog_full_text (`uid`, `title`, `description`)\n
+VALUES\n
+<dtml-in prefix="loop" expr="_.range(_.len(uid))">\n
+(\n
+  <dtml-sqlvar expr="uid[loop_item]" type="int">,  \n
+  <dtml-sqlvar expr="getTitle[loop_item]" type="string" optional>,\n
+  <dtml-sqlvar expr="getDescription[loop_item]" type="string" optional>\n
+)<dtml-unless sequence-end>,</dtml-unless>\n
+</dtml-in>\n
+
+
+]]></string> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string></string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_fulltext_list.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_fulltext_list.xml
index fb0ecee42f..1f0e6f1e03 100644
--- a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_fulltext_list.xml
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_fulltext_list.xml
@@ -55,21 +55,16 @@ SearchableText</string> </value>
             <key> <string>src</string> </key>
             <value> <string encoding="cdata"><![CDATA[
 
-DELETE FROM\n
-  full_text\n
-WHERE\n
-<dtml-in uid>\n
-  uid=<dtml-sqlvar sequence-item type="int"><dtml-if sequence-end><dtml-else> OR </dtml-if>\n
-</dtml-in>\n
-;\n
-<dtml-var "\'\\0\'"><dtml-let document_list="[]">\n
+<dtml-let document_list="[]" delete_list="[]">\n
   <dtml-in prefix="loop" expr="_.range(_.len(uid))">\n
     <dtml-if "SearchableText[loop_item]">\n
       <dtml-call expr="document_list.append(loop_item)">\n
+    <dtml-else>\n
+      <dtml-call expr="delete_list.append(loop_item)">\n
     </dtml-if>\n
   </dtml-in>\n
   <dtml-if expr="_.len(document_list) > 0">\n
-INSERT INTO\n
+REPLACE INTO\n
   full_text\n
 VALUES\n
     <dtml-in prefix="loop" expr="document_list">\n
@@ -79,7 +74,19 @@ VALUES\n
 )<dtml-unless sequence-end>,</dtml-unless>\n
     </dtml-in>\n
   </dtml-if>\n
-</dtml-let>
+  <dtml-if expr="_.len(delete_list) > 0">\n
+<dtml-var sql_delimiter>\n
+DELETE FROM\n
+  full_text\n
+WHERE uid IN\n
+( \n
+    <dtml-in prefix="loop" expr="delete_list">\n
+  <dtml-sqlvar expr="uid[loop_item]" type="int"><dtml-unless sequence-end>,</dtml-unless>\n
+    </dtml-in>\n
+)\n
+  </dtml-if>\n
+</dtml-let>\n
+
 
 ]]></string> </value>
         </item>
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.catalog_keys.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.catalog_keys.xml
new file mode 100644
index 0000000000..b0fe64af00
--- /dev/null
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.catalog_keys.xml
@@ -0,0 +1,5 @@
+<catalog_method>
+ <item key="sql_clear_catalog" type="int">
+  <value>1</value>
+ </item>
+</catalog_method>
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.xml
new file mode 100644
index 0000000000..3cac0e0afb
--- /dev/null
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="SQL" module="Products.ZSQLMethods.SQL"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_col</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>allow_simple_one_argument_traversal</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>arguments_src</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>cache_time_</string> </key>
+            <value> <int>0</int> </value>
+        </item>
+        <item>
+            <key> <string>class_file_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>class_name_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>connection_hook</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>connection_id</string> </key>
+            <value> <string>erp5_sql_connection</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>z_create_catalog_fulltext</string> </value>
+        </item>
+        <item>
+            <key> <string>max_cache_</string> </key>
+            <value> <int>100</int> </value>
+        </item>
+        <item>
+            <key> <string>max_rows_</string> </key>
+            <value> <int>1000</int> </value>
+        </item>
+        <item>
+            <key> <string>src</string> </key>
+            <value> <string># Host:\n
+# Database: test\n
+# Table: \'catalog_full_text\'\n
+#\n
+CREATE TABLE `catalog_full_text` (\n
+  `uid` BIGINT UNSIGNED NOT NULL,\n
+  `title` varchar(255) default \'\',\n
+  `description` text,\n
+  PRIMARY KEY  (`uid`),\n
+  FULLTEXT `title` (`title`) COMMENT \'parser "TokenBigramSplitSymbolAlpha"\',\n
+  FULLTEXT `description` (`description`) COMMENT \'parser "TokenBigramSplitSymbolAlpha"\'\n
+) ENGINE=mroonga;\n
+</string> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string></string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_content_translation.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_content_translation.xml
index 6fca1aaac5..7b1a162804 100644
--- a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_content_translation.xml
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_content_translation.xml
@@ -64,7 +64,7 @@
   `content_language` VARBINARY(100),\n
   `translated_text` TEXT,\n
   PRIMARY KEY (`uid`, `property_name`, `content_language`),\n
-  FULLTEXT KEY (`translated_text`)\n
+  FULLTEXT KEY (`translated_text`) COMMENT \'parser "TokenBigramSplitSymbolAlpha"\'\n
 ) ENGINE=mroonga;\n
 </string> </value>
         </item>
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_fulltext.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_fulltext.xml
index c0ae591177..bcdfe1141f 100644
--- a/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_fulltext.xml
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_fulltext.xml
@@ -66,7 +66,7 @@ CREATE TABLE `full_text` (\n
   `uid` BIGINT UNSIGNED NOT NULL,\n
   `SearchableText` MEDIUMTEXT,\n
   PRIMARY KEY  (`uid`),\n
-  FULLTEXT `SearchableText` (`SearchableText`)\n
+  FULLTEXT `SearchableText` (`SearchableText`) COMMENT \'parser "TokenBigramSplitSymbolAlpha"\'\n
 ) ENGINE=mroonga;\n
 </string> </value>
         </item>
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogRelatedKeyTemplateItem/related_key_list.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogRelatedKeyTemplateItem/related_key_list.xml
new file mode 100644
index 0000000000..2ced6f2d72
--- /dev/null
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogRelatedKeyTemplateItem/related_key_list.xml
@@ -0,0 +1,10 @@
+<key_list>
+ <key>career_skill_title | category,catalog,catalog_full_text/title/z_related_career_skill</key>
+ <key>description | catalog_full_text/description/z_related_uid</key>
+ <key>parent_description | catalog_full_text/description/z_related_parent</key>
+ <key>parent_title | catalog_full_text/title/z_related_parent</key>
+ <key>stock_explanation_title | catalog_full_text/title/z_related_explanation_from_stock</key>
+ <key>stock_mirror_section_title | catalog_full_text/title/z_related_mirror_section_uid_from_stock</key>
+ <key>stock_node_title | catalog_full_text/title/z_related_node_uid_from_stock</key>
+ <key>title | catalog_full_text/title/z_related_uid</key>
+</key_list>
\ No newline at end of file
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogResultTableTemplateItem/result_table_list.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogResultTableTemplateItem/result_table_list.xml
index 99624bbbc6..685b76687c 100644
--- a/bt5/erp5_full_text_mroonga_catalog/CatalogResultTableTemplateItem/result_table_list.xml
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogResultTableTemplateItem/result_table_list.xml
@@ -1,3 +1,4 @@
 <key_list>
+ <key>catalog_full_text</key>
  <key>full_text</key>
 </key_list>
\ No newline at end of file
diff --git a/bt5/erp5_full_text_mroonga_catalog/CatalogSearchKeyTemplateItem/search_key_list.xml b/bt5/erp5_full_text_mroonga_catalog/CatalogSearchKeyTemplateItem/search_key_list.xml
new file mode 100644
index 0000000000..caead947b5
--- /dev/null
+++ b/bt5/erp5_full_text_mroonga_catalog/CatalogSearchKeyTemplateItem/search_key_list.xml
@@ -0,0 +1,8 @@
+<key_list>
+ <key>SearchableText | MroongaBooleanFullTextKey</key>
+ <key>catalog_full_text.description | MroongaBooleanFullTextKey</key>
+ <key>catalog_full_text.title | MroongaBooleanFullTextKey</key>
+ <key>description | MroongaBooleanFullTextKey</key>
+ <key>full_text.SearchableText | MroongaBooleanFullTextKey</key>
+ <key>title | MroongaBooleanFullTextKey</key>
+</key_list>
\ No newline at end of file
diff --git a/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_full_text_key_list b/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_full_text_key_list
deleted file mode 100644
index 5e90fc7ee3..0000000000
--- a/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_full_text_key_list
+++ /dev/null
@@ -1,2 +0,0 @@
-SearchableText
-full_text.SearchableText
\ No newline at end of file
diff --git a/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_method_id_list b/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_method_id_list
index 20156621a9..e7edc065c8 100644
--- a/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_method_id_list
+++ b/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_method_id_list
@@ -1,9 +1,13 @@
 erp5_mysql_innodb/SQLCatalog_deferFullTextIndex
 erp5_mysql_innodb/SQLCatalog_deferFullTextIndexActivity
 erp5_mysql_innodb/SQLCatalog_makeFullTextQuery
+erp5_mysql_innodb/z0_drop_catalog_fulltext
 erp5_mysql_innodb/z0_drop_content_translation
 erp5_mysql_innodb/z0_drop_fulltext
+erp5_mysql_innodb/z0_uncatalog_catalog_fulltext
 erp5_mysql_innodb/z0_uncatalog_fulltext
+erp5_mysql_innodb/z_catalog_catalog_fulltext_list
 erp5_mysql_innodb/z_catalog_fulltext_list
+erp5_mysql_innodb/z_create_catalog_fulltext
 erp5_mysql_innodb/z_create_content_translation
 erp5_mysql_innodb/z_create_fulltext
\ No newline at end of file
diff --git a/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_related_key_list b/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_related_key_list
new file mode 100644
index 0000000000..693452ae6b
--- /dev/null
+++ b/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_related_key_list
@@ -0,0 +1,8 @@
+career_skill_title | category,catalog,catalog_full_text/title/z_related_career_skill
+description | catalog_full_text/description/z_related_uid
+parent_description | catalog_full_text/description/z_related_parent
+parent_title | catalog_full_text/title/z_related_parent
+stock_explanation_title | catalog_full_text/title/z_related_explanation_from_stock
+stock_mirror_section_title | catalog_full_text/title/z_related_mirror_section_uid_from_stock
+stock_node_title | catalog_full_text/title/z_related_node_uid_from_stock
+title | catalog_full_text/title/z_related_uid
\ No newline at end of file
diff --git a/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_result_table_list b/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_result_table_list
index 37adfa26ab..0f5a728af0 100644
--- a/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_result_table_list
+++ b/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_result_table_list
@@ -1 +1,2 @@
+catalog_full_text
 full_text
\ No newline at end of file
diff --git a/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_search_key_list b/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_search_key_list
new file mode 100644
index 0000000000..3c63ebce67
--- /dev/null
+++ b/bt5/erp5_full_text_mroonga_catalog/bt/template_catalog_search_key_list
@@ -0,0 +1,6 @@
+SearchableText | MroongaBooleanFullTextKey
+catalog_full_text.description | MroongaBooleanFullTextKey
+catalog_full_text.title | MroongaBooleanFullTextKey
+description | MroongaBooleanFullTextKey
+full_text.SearchableText | MroongaBooleanFullTextKey
+title | MroongaBooleanFullTextKey
\ No newline at end of file
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogFullTextKeyTemplateItem/full_text_key_list.xml b/bt5/erp5_full_text_myisam_catalog/CatalogFullTextKeyTemplateItem/full_text_key_list.xml
index 662e00e9f7..6bee43e19e 100644
--- a/bt5/erp5_full_text_myisam_catalog/CatalogFullTextKeyTemplateItem/full_text_key_list.xml
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogFullTextKeyTemplateItem/full_text_key_list.xml
@@ -1,4 +1,8 @@
 <key_list>
  <key>SearchableText</key>
- <key>full_text.SearchableText</key>
+ <key>catalog_full_text.description</key>
+ <key>catalog_full_text.title</key>                                                                       |
+ <key>description</key>                                                                                          |
+ <key>full_text.SearchableText</key>                                                                             |
+ <key>title</key>                                                                                         |
 </key_list>
\ No newline at end of file
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_deferFullTextIndexActivity.xml b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_deferFullTextIndexActivity.xml
index 99f8fa0c33..38959380dd 100644
--- a/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_deferFullTextIndexActivity.xml
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_deferFullTextIndexActivity.xml
@@ -68,8 +68,9 @@ for path in path_list:\n
   try:\n
     tmp_dict = {}\n
     for property in property_list:\n
-      if property == \'SearchableText\':\n
-        value = obj.SearchableText()\n
+      getter = getattr(obj, property, None)\n
+      if getter is not None and callable(getter):\n
+        value = getter()\n
       else:\n
         value = getattr(obj, \'get%s\' % UpperCase(property))()\n
       tmp_dict[property] = value\n
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.catalog_keys.xml b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.catalog_keys.xml
new file mode 100644
index 0000000000..b0fe64af00
--- /dev/null
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.catalog_keys.xml
@@ -0,0 +1,5 @@
+<catalog_method>
+ <item key="sql_clear_catalog" type="int">
+  <value>1</value>
+ </item>
+</catalog_method>
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.xml b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.xml
new file mode 100644
index 0000000000..d9dc3890ee
--- /dev/null
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_drop_catalog_fulltext.xml
@@ -0,0 +1,70 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="SQL" module="Products.ZSQLMethods.SQL"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_col</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>allow_simple_one_argument_traversal</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>arguments_src</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>cache_time_</string> </key>
+            <value> <int>0</int> </value>
+        </item>
+        <item>
+            <key> <string>class_file_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>class_name_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>connection_hook</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>connection_id</string> </key>
+            <value> <string>erp5_sql_connection</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>z0_drop_catalog_fulltext</string> </value>
+        </item>
+        <item>
+            <key> <string>max_cache_</string> </key>
+            <value> <int>100</int> </value>
+        </item>
+        <item>
+            <key> <string>max_rows_</string> </key>
+            <value> <int>1000</int> </value>
+        </item>
+        <item>
+            <key> <string>src</string> </key>
+            <value> <string>DROP TABLE IF EXISTS catalog_full_text</string> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string></string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.catalog_keys.xml b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.catalog_keys.xml
new file mode 100644
index 0000000000..d29d36d9f0
--- /dev/null
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.catalog_keys.xml
@@ -0,0 +1,5 @@
+<catalog_method>
+ <item key="sql_uncatalog_object" type="int">
+  <value>1</value>
+ </item>
+</catalog_method>
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.xml b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.xml
new file mode 100644
index 0000000000..d9493f4955
--- /dev/null
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z0_uncatalog_catalog_fulltext.xml
@@ -0,0 +1,68 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="SQL" module="Products.ZSQLMethods.SQL"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>allow_simple_one_argument_traversal</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>arguments_src</string> </key>
+            <value> <string>uid</string> </value>
+        </item>
+        <item>
+            <key> <string>cache_time_</string> </key>
+            <value> <int>0</int> </value>
+        </item>
+        <item>
+            <key> <string>class_file_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>class_name_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>connection_hook</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>connection_id</string> </key>
+            <value> <string>erp5_sql_deferred_connection</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>z0_uncatalog_catalog_fulltext</string> </value>
+        </item>
+        <item>
+            <key> <string>max_cache_</string> </key>
+            <value> <int>100</int> </value>
+        </item>
+        <item>
+            <key> <string>max_rows_</string> </key>
+            <value> <int>1000</int> </value>
+        </item>
+        <item>
+            <key> <string>src</string> </key>
+            <value> <string encoding="cdata"><![CDATA[
+
+DELETE FROM catalog_full_text WHERE <dtml-sqltest uid op=eq type=int>
+
+]]></string> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string></string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.catalog_keys.xml b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.catalog_keys.xml
new file mode 100644
index 0000000000..c95a68655e
--- /dev/null
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.catalog_keys.xml
@@ -0,0 +1,5 @@
+<catalog_method>
+ <item key="sql_catalog_object_list" type="int">
+  <value>1</value>
+ </item>
+</catalog_method>
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.xml b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.xml
new file mode 100644
index 0000000000..06e374fa75
--- /dev/null
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_catalog_fulltext_list.xml
@@ -0,0 +1,80 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="SQL" module="Products.ZSQLMethods.SQL"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>allow_simple_one_argument_traversal</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>arguments_src</string> </key>
+            <value> <string>uid\r\n
+getTitle\r\n
+getDescription</string> </value>
+        </item>
+        <item>
+            <key> <string>cache_time_</string> </key>
+            <value> <int>0</int> </value>
+        </item>
+        <item>
+            <key> <string>class_file_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>class_name_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>connection_hook</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>connection_id</string> </key>
+            <value> <string>erp5_sql_deferred_connection</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>z_catalog_catalog_fulltext_list</string> </value>
+        </item>
+        <item>
+            <key> <string>max_cache_</string> </key>
+            <value> <int>100</int> </value>
+        </item>
+        <item>
+            <key> <string>max_rows_</string> </key>
+            <value> <int>1000</int> </value>
+        </item>
+        <item>
+            <key> <string>src</string> </key>
+            <value> <string encoding="cdata"><![CDATA[
+
+REPLACE INTO\n
+  catalog_full_text (`uid`, `title`, `description`)\n
+VALUES\n
+<dtml-in prefix="loop" expr="_.range(_.len(uid))">\n
+(\n
+  <dtml-sqlvar expr="uid[loop_item]" type="int">,  \n
+  <dtml-sqlvar expr="getTitle[loop_item]" type="string" optional>,\n
+  <dtml-sqlvar expr="getDescription[loop_item]" type="string" optional>\n
+)<dtml-unless sequence-end>,</dtml-unless>\n
+</dtml-in>\n
+
+
+]]></string> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string></string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_fulltext_list.xml b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_fulltext_list.xml
index fb0ecee42f..1f0e6f1e03 100644
--- a/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_fulltext_list.xml
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_catalog_fulltext_list.xml
@@ -55,21 +55,16 @@ SearchableText</string> </value>
             <key> <string>src</string> </key>
             <value> <string encoding="cdata"><![CDATA[
 
-DELETE FROM\n
-  full_text\n
-WHERE\n
-<dtml-in uid>\n
-  uid=<dtml-sqlvar sequence-item type="int"><dtml-if sequence-end><dtml-else> OR </dtml-if>\n
-</dtml-in>\n
-;\n
-<dtml-var "\'\\0\'"><dtml-let document_list="[]">\n
+<dtml-let document_list="[]" delete_list="[]">\n
   <dtml-in prefix="loop" expr="_.range(_.len(uid))">\n
     <dtml-if "SearchableText[loop_item]">\n
       <dtml-call expr="document_list.append(loop_item)">\n
+    <dtml-else>\n
+      <dtml-call expr="delete_list.append(loop_item)">\n
     </dtml-if>\n
   </dtml-in>\n
   <dtml-if expr="_.len(document_list) > 0">\n
-INSERT INTO\n
+REPLACE INTO\n
   full_text\n
 VALUES\n
     <dtml-in prefix="loop" expr="document_list">\n
@@ -79,7 +74,19 @@ VALUES\n
 )<dtml-unless sequence-end>,</dtml-unless>\n
     </dtml-in>\n
   </dtml-if>\n
-</dtml-let>
+  <dtml-if expr="_.len(delete_list) > 0">\n
+<dtml-var sql_delimiter>\n
+DELETE FROM\n
+  full_text\n
+WHERE uid IN\n
+( \n
+    <dtml-in prefix="loop" expr="delete_list">\n
+  <dtml-sqlvar expr="uid[loop_item]" type="int"><dtml-unless sequence-end>,</dtml-unless>\n
+    </dtml-in>\n
+)\n
+  </dtml-if>\n
+</dtml-let>\n
+
 
 ]]></string> </value>
         </item>
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.catalog_keys.xml b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.catalog_keys.xml
new file mode 100644
index 0000000000..b0fe64af00
--- /dev/null
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.catalog_keys.xml
@@ -0,0 +1,5 @@
+<catalog_method>
+ <item key="sql_clear_catalog" type="int">
+  <value>1</value>
+ </item>
+</catalog_method>
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.xml b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.xml
new file mode 100644
index 0000000000..ce9ad33f91
--- /dev/null
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/z_create_catalog_fulltext.xml
@@ -0,0 +1,82 @@
+<?xml version="1.0"?>
+<ZopeData>
+  <record id="1" aka="AAAAAAAAAAE=">
+    <pickle>
+      <global name="SQL" module="Products.ZSQLMethods.SQL"/>
+    </pickle>
+    <pickle>
+      <dictionary>
+        <item>
+            <key> <string>_col</string> </key>
+            <value>
+              <tuple/>
+            </value>
+        </item>
+        <item>
+            <key> <string>allow_simple_one_argument_traversal</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>arguments_src</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>cache_time_</string> </key>
+            <value> <int>0</int> </value>
+        </item>
+        <item>
+            <key> <string>class_file_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>class_name_</string> </key>
+            <value> <string></string> </value>
+        </item>
+        <item>
+            <key> <string>connection_hook</string> </key>
+            <value>
+              <none/>
+            </value>
+        </item>
+        <item>
+            <key> <string>connection_id</string> </key>
+            <value> <string>erp5_sql_connection</string> </value>
+        </item>
+        <item>
+            <key> <string>id</string> </key>
+            <value> <string>z_create_catalog_fulltext</string> </value>
+        </item>
+        <item>
+            <key> <string>max_cache_</string> </key>
+            <value> <int>100</int> </value>
+        </item>
+        <item>
+            <key> <string>max_rows_</string> </key>
+            <value> <int>1000</int> </value>
+        </item>
+        <item>
+            <key> <string>src</string> </key>
+            <value> <string># Host:\n
+# Database: test\n
+# Table: \'catalog_full_text\'\n
+#\n
+CREATE TABLE `catalog_full_text` (\n
+  `uid` BIGINT UNSIGNED NOT NULL,\n
+  `title` varchar(255) default \'\',\n
+  `description` text,\n
+  PRIMARY KEY  (`uid`),\n
+  FULLTEXT `title` (`title`),\n
+  FULLTEXT `description` (`description`)\n
+) ENGINE=MyISAM;\n
+</string> </value>
+        </item>
+        <item>
+            <key> <string>title</string> </key>
+            <value> <string></string> </value>
+        </item>
+      </dictionary>
+    </pickle>
+  </record>
+</ZopeData>
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogRelatedKeyTemplateItem/related_key_list.xml b/bt5/erp5_full_text_myisam_catalog/CatalogRelatedKeyTemplateItem/related_key_list.xml
new file mode 100644
index 0000000000..2ced6f2d72
--- /dev/null
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogRelatedKeyTemplateItem/related_key_list.xml
@@ -0,0 +1,10 @@
+<key_list>
+ <key>career_skill_title | category,catalog,catalog_full_text/title/z_related_career_skill</key>
+ <key>description | catalog_full_text/description/z_related_uid</key>
+ <key>parent_description | catalog_full_text/description/z_related_parent</key>
+ <key>parent_title | catalog_full_text/title/z_related_parent</key>
+ <key>stock_explanation_title | catalog_full_text/title/z_related_explanation_from_stock</key>
+ <key>stock_mirror_section_title | catalog_full_text/title/z_related_mirror_section_uid_from_stock</key>
+ <key>stock_node_title | catalog_full_text/title/z_related_node_uid_from_stock</key>
+ <key>title | catalog_full_text/title/z_related_uid</key>
+</key_list>
\ No newline at end of file
diff --git a/bt5/erp5_full_text_myisam_catalog/CatalogResultTableTemplateItem/result_table_list.xml b/bt5/erp5_full_text_myisam_catalog/CatalogResultTableTemplateItem/result_table_list.xml
index 99624bbbc6..685b76687c 100644
--- a/bt5/erp5_full_text_myisam_catalog/CatalogResultTableTemplateItem/result_table_list.xml
+++ b/bt5/erp5_full_text_myisam_catalog/CatalogResultTableTemplateItem/result_table_list.xml
@@ -1,3 +1,4 @@
 <key_list>
+ <key>catalog_full_text</key>
  <key>full_text</key>
 </key_list>
\ No newline at end of file
diff --git a/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_full_text_key_list b/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_full_text_key_list
index 5e90fc7ee3..163cb04a95 100644
--- a/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_full_text_key_list
+++ b/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_full_text_key_list
@@ -1,2 +1,6 @@
 SearchableText
-full_text.SearchableText
\ No newline at end of file
+catalog_full_text.description
+catalog_full_text.title
+description
+full_text.SearchableText
+title
\ No newline at end of file
diff --git a/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_method_id_list b/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_method_id_list
index 20156621a9..e7edc065c8 100644
--- a/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_method_id_list
+++ b/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_method_id_list
@@ -1,9 +1,13 @@
 erp5_mysql_innodb/SQLCatalog_deferFullTextIndex
 erp5_mysql_innodb/SQLCatalog_deferFullTextIndexActivity
 erp5_mysql_innodb/SQLCatalog_makeFullTextQuery
+erp5_mysql_innodb/z0_drop_catalog_fulltext
 erp5_mysql_innodb/z0_drop_content_translation
 erp5_mysql_innodb/z0_drop_fulltext
+erp5_mysql_innodb/z0_uncatalog_catalog_fulltext
 erp5_mysql_innodb/z0_uncatalog_fulltext
+erp5_mysql_innodb/z_catalog_catalog_fulltext_list
 erp5_mysql_innodb/z_catalog_fulltext_list
+erp5_mysql_innodb/z_create_catalog_fulltext
 erp5_mysql_innodb/z_create_content_translation
 erp5_mysql_innodb/z_create_fulltext
\ No newline at end of file
diff --git a/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_related_key_list b/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_related_key_list
new file mode 100644
index 0000000000..693452ae6b
--- /dev/null
+++ b/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_related_key_list
@@ -0,0 +1,8 @@
+career_skill_title | category,catalog,catalog_full_text/title/z_related_career_skill
+description | catalog_full_text/description/z_related_uid
+parent_description | catalog_full_text/description/z_related_parent
+parent_title | catalog_full_text/title/z_related_parent
+stock_explanation_title | catalog_full_text/title/z_related_explanation_from_stock
+stock_mirror_section_title | catalog_full_text/title/z_related_mirror_section_uid_from_stock
+stock_node_title | catalog_full_text/title/z_related_node_uid_from_stock
+title | catalog_full_text/title/z_related_uid
\ No newline at end of file
diff --git a/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_result_table_list b/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_result_table_list
index 37adfa26ab..0f5a728af0 100644
--- a/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_result_table_list
+++ b/bt5/erp5_full_text_myisam_catalog/bt/template_catalog_result_table_list
@@ -1 +1,2 @@
+catalog_full_text
 full_text
\ No newline at end of file
diff --git a/bt5/erp5_ingestion/bt/test_dependency_list b/bt5/erp5_ingestion/bt/test_dependency_list
index 105482554a..2f97a2f2c2 100644
--- a/bt5/erp5_ingestion/bt/test_dependency_list
+++ b/bt5/erp5_ingestion/bt/test_dependency_list
@@ -1,4 +1,4 @@
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_ingestion_mysql_innodb_catalog
 erp5_web
 erp5_crm
diff --git a/bt5/erp5_item/CatalogRelatedKeyTemplateItem/related_key_list.xml b/bt5/erp5_item/CatalogRelatedKeyTemplateItem/related_key_list.xml
index e34497d8c0..4c324fb3d7 100644
--- a/bt5/erp5_item/CatalogRelatedKeyTemplateItem/related_key_list.xml
+++ b/bt5/erp5_item/CatalogRelatedKeyTemplateItem/related_key_list.xml
@@ -1,6 +1,6 @@
 <key_list>
  <key>item_catalog_portal_type | catalog/portal_type/z_related_item_catalog</key>
  <key>item_catalog_reference | catalog/reference/z_related_item_catalog</key>
- <key>item_catalog_title | catalog/title/z_related_item_catalog</key>
+ <key>item_catalog_title | catalog_full_text/title/z_related_item_catalog</key>
  <key>item_catalog_validation_state | catalog/validation_state/z_related_item_catalog</key>
 </key_list>
\ No newline at end of file
diff --git a/bt5/erp5_item/bt/template_catalog_related_key_list b/bt5/erp5_item/bt/template_catalog_related_key_list
index eecc7c1038..0057439296 100644
--- a/bt5/erp5_item/bt/template_catalog_related_key_list
+++ b/bt5/erp5_item/bt/template_catalog_related_key_list
@@ -1,4 +1,4 @@
-item_catalog_title | catalog/title/z_related_item_catalog
+item_catalog_title | catalog_full_text/title/z_related_item_catalog
 item_catalog_portal_type | catalog/portal_type/z_related_item_catalog
 item_catalog_reference | catalog/reference/z_related_item_catalog
 item_catalog_validation_state | catalog/validation_state/z_related_item_catalog
\ No newline at end of file
diff --git a/bt5/erp5_km_ui_test/PathTemplateItem/portal_tests/erp5_km_zuite/testTopSearchBoxClicking.xml b/bt5/erp5_km_ui_test/PathTemplateItem/portal_tests/erp5_km_zuite/testTopSearchBoxClicking.xml
index ecfe64f3c1..4e2e984d63 100644
--- a/bt5/erp5_km_ui_test/PathTemplateItem/portal_tests/erp5_km_zuite/testTopSearchBoxClicking.xml
+++ b/bt5/erp5_km_ui_test/PathTemplateItem/portal_tests/erp5_km_zuite/testTopSearchBoxClicking.xml
@@ -96,7 +96,7 @@
 <tr>\n
   <td>type</td>\n
   <td>search_text</td>\n
-  <td>Pouet_NO_EXISTS</td>\n
+  <td>Poueet</td>\n
 </tr>\n
 <tr>\n
   <td>clickAndWait</td>\n
@@ -111,7 +111,7 @@
 <tr>\n
   <td>verifyValue</td>\n
   <td>search_text</td>\n
-  <td>Pouet_NO_EXISTS</td>\n
+  <td>Poueet</td>\n
 </tr>\n
 <tr>\n
   <td>type</td>\n
diff --git a/bt5/erp5_paypal_secure_payment/bt/test_dependency_list b/bt5/erp5_paypal_secure_payment/bt/test_dependency_list
index 418a1a563a..bff77e32d8 100644
--- a/bt5/erp5_paypal_secure_payment/bt/test_dependency_list
+++ b/bt5/erp5_paypal_secure_payment/bt/test_dependency_list
@@ -1 +1 @@
-erp5_full_text_myisam_catalog
\ No newline at end of file
+erp5_full_text_mroonga_catalog
\ No newline at end of file
diff --git a/bt5/erp5_payzen_secure_payment/bt/test_dependency_list b/bt5/erp5_payzen_secure_payment/bt/test_dependency_list
index 418a1a563a..bff77e32d8 100644
--- a/bt5/erp5_payzen_secure_payment/bt/test_dependency_list
+++ b/bt5/erp5_payzen_secure_payment/bt/test_dependency_list
@@ -1 +1 @@
-erp5_full_text_myisam_catalog
\ No newline at end of file
+erp5_full_text_mroonga_catalog
\ No newline at end of file
diff --git a/bt5/erp5_pdf_merge/bt/test_dependency_list b/bt5/erp5_pdf_merge/bt/test_dependency_list
index 117dc3e54f..bc72f3a1b7 100644
--- a/bt5/erp5_pdf_merge/bt/test_dependency_list
+++ b/bt5/erp5_pdf_merge/bt/test_dependency_list
@@ -1,4 +1,4 @@
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_base
 erp5_web
 erp5_ingestion
diff --git a/bt5/erp5_research_item/bt/test_dependency_list b/bt5/erp5_research_item/bt/test_dependency_list
index 72f980091a..ff050e6eaa 100644
--- a/bt5/erp5_research_item/bt/test_dependency_list
+++ b/bt5/erp5_research_item/bt/test_dependency_list
@@ -1,4 +1,4 @@
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_simulation
 erp5_configurator_standard_trade_template
 erp5_simulation_test
diff --git a/bt5/erp5_safeimage/bt/test_dependency_list b/bt5/erp5_safeimage/bt/test_dependency_list
index 73ffa2a14b..665e8c44c9 100644
--- a/bt5/erp5_safeimage/bt/test_dependency_list
+++ b/bt5/erp5_safeimage/bt/test_dependency_list
@@ -1,3 +1,3 @@
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_ingestion_mysql_innodb_catalog
 erp5_ingestion
\ No newline at end of file
diff --git a/bt5/erp5_social_contracts/bt/test_dependency_list b/bt5/erp5_social_contracts/bt/test_dependency_list
index 418a1a563a..bff77e32d8 100644
--- a/bt5/erp5_social_contracts/bt/test_dependency_list
+++ b/bt5/erp5_social_contracts/bt/test_dependency_list
@@ -1 +1 @@
-erp5_full_text_myisam_catalog
\ No newline at end of file
+erp5_full_text_mroonga_catalog
\ No newline at end of file
diff --git a/bt5/erp5_syncml_test_data/bt/test_dependency_list b/bt5/erp5_syncml_test_data/bt/test_dependency_list
index 418a1a563a..bff77e32d8 100644
--- a/bt5/erp5_syncml_test_data/bt/test_dependency_list
+++ b/bt5/erp5_syncml_test_data/bt/test_dependency_list
@@ -1 +1 @@
-erp5_full_text_myisam_catalog
\ No newline at end of file
+erp5_full_text_mroonga_catalog
\ No newline at end of file
diff --git a/bt5/erp5_test_result/DocumentTemplateItem/portal_components/document.erp5.ERP5ScalabilityDistributor.py b/bt5/erp5_test_result/DocumentTemplateItem/portal_components/document.erp5.ERP5ScalabilityDistributor.py
index c68f2c010f..34e048f0cb 100644
--- a/bt5/erp5_test_result/DocumentTemplateItem/portal_components/document.erp5.ERP5ScalabilityDistributor.py
+++ b/bt5/erp5_test_result/DocumentTemplateItem/portal_components/document.erp5.ERP5ScalabilityDistributor.py
@@ -26,6 +26,7 @@
 ##############################################################################
 
 from Products.ERP5.Document.ERP5ProjectUnitTestDistributor import ERP5ProjectUnitTestDistributor
+from Products.ZSQLCatalog.SQLCatalog import SimpleQuery
 from zLOG import LOG,ERROR
 
 from AccessControl import ClassSecurityInfo
@@ -110,7 +111,10 @@ class ERP5ScalabilityDistributor(ERP5ProjectUnitTestDistributor):
 
     tag = "%s_%s" % (self.getRelativeUrl(), title)
     if portal.portal_activities.countMessageWithTag(tag) == 0:
-      test_node_list = test_node_module.searchFolder(portal_type="Test Node",title=title)
+      test_node_list = test_node_module.searchFolder(
+        portal_type="Test Node",
+        title=SimpleQuery(comparison_operator='=', title=title),
+      )
       assert len(test_node_list) in (0, 1), "Unable to find testnode : %s" % title
       test_node = None
       if len(test_node_list) == 1:
@@ -129,7 +133,8 @@ class ERP5ScalabilityDistributor(ERP5ProjectUnitTestDistributor):
     isMasterTestnode : return True if the node given in parameter exists and is a validated master
     """
     test_node_module = self._getTestNodeModule()
-    test_node_master = [ node for node in test_node_module.searchFolder(portal_type="Test Node", title=title,
+    test_node_master = [ node for node in test_node_module.searchFolder(portal_type="Test Node",
+                                      title=SimpleQuery(comparison_operator='=', title=title),
                                       specialise_uid=self.getUid(),
                                       validation_state="validated") if node.getMaster() == 1 ]
     if len(test_node_master) == 1:
diff --git a/bt5/erp5_test_result/bt/test_dependency_list b/bt5/erp5_test_result/bt/test_dependency_list
index 9a77167a96..8149202de5 100644
--- a/bt5/erp5_test_result/bt/test_dependency_list
+++ b/bt5/erp5_test_result/bt/test_dependency_list
@@ -1,2 +1,2 @@
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_base
\ No newline at end of file
diff --git a/bt5/erp5_tiosafe_oxatis_test/TestTemplateItem/testOxatisSynchronization.py b/bt5/erp5_tiosafe_oxatis_test/TestTemplateItem/testOxatisSynchronization.py
index a115c892fd..3ec187b2d7 100644
--- a/bt5/erp5_tiosafe_oxatis_test/TestTemplateItem/testOxatisSynchronization.py
+++ b/bt5/erp5_tiosafe_oxatis_test/TestTemplateItem/testOxatisSynchronization.py
@@ -41,7 +41,7 @@ class TestOxatisSynchronization(ERP5TypeTestCase):
     """ Return the list of BT required by unit tests. """
     return (
       'erp5_core_proxy_field_legacy',
-      'erp5_full_text_myisam_catalog',
+      'erp5_full_text_mroonga_catalog',
       'erp5_base',
       'erp5_pdm',
       'erp5_simulation',
diff --git a/bt5/erp5_ui_test/PathTemplateItem/portal_tests/relation_field_zuite/testMultiRelationFieldSearchDialogChangePage.xml b/bt5/erp5_ui_test/PathTemplateItem/portal_tests/relation_field_zuite/testMultiRelationFieldSearchDialogChangePage.xml
index 043e0280ef..cd2aa03273 100644
--- a/bt5/erp5_ui_test/PathTemplateItem/portal_tests/relation_field_zuite/testMultiRelationFieldSearchDialogChangePage.xml
+++ b/bt5/erp5_ui_test/PathTemplateItem/portal_tests/relation_field_zuite/testMultiRelationFieldSearchDialogChangePage.xml
@@ -102,7 +102,7 @@
 <tr>\n
   <td>assertValue</td>\n
   <td>listbox_title</td>\n
-  <td>"%1%" OR "%2%"</td>\n
+  <td>("%1%" OR "%2%")</td>\n
 </tr>\n
 <tr>\n
   <td>clickAndWait</td>\n
@@ -119,7 +119,7 @@
 <tr>\n
   <td>assertValue</td>\n
   <td>listbox_title</td>\n
-  <td>"%1%" OR "%2%"</td>\n
+  <td>("%1%" OR "%2%")</td>\n
 </tr>\n
 \n
 <tr>\n
@@ -137,7 +137,7 @@
 <tr>\n
   <td>assertValue</td>\n
   <td>listbox_title</td>\n
-  <td>"%1%" OR "%2%"</td>\n
+  <td>("%1%" OR "%2%")</td>\n
 </tr>\n
 \n
 <tr>\n
@@ -181,7 +181,7 @@
 <tr>\n
   <td>assertValue</td>\n
   <td>listbox_title</td>\n
-  <td>"%1%" OR "%2%"</td>\n
+  <td>("%1%" OR "%2%")</td>\n
 </tr>\n
 \n
 <tal:block tal:condition="python: context.TestTool_getSkinName()!=\'Mobile\'">\n
@@ -213,7 +213,7 @@
 <tr>\n
   <td>assertValue</td>\n
   <td>listbox_title</td>\n
-  <td>"%1%" OR "%2%"</td>\n
+  <td>("%1%" OR "%2%")</td>\n
 </tr>\n
 \n
 <tr>\n
diff --git a/bt5/erp5_ui_test/PathTemplateItem/portal_tests/relation_field_zuite/testSimpleRelationFieldWithListbox.xml b/bt5/erp5_ui_test/PathTemplateItem/portal_tests/relation_field_zuite/testSimpleRelationFieldWithListbox.xml
index 97a0450b98..2f9cc69494 100644
--- a/bt5/erp5_ui_test/PathTemplateItem/portal_tests/relation_field_zuite/testSimpleRelationFieldWithListbox.xml
+++ b/bt5/erp5_ui_test/PathTemplateItem/portal_tests/relation_field_zuite/testSimpleRelationFieldWithListbox.xml
@@ -78,7 +78,7 @@
 <tr>\n
   <td>type</td>\n
   <td>field_my_foo_category_title</td>\n
-  <td>b</td>\n
+  <td>=b</td>\n
 </tr>\n
 <tr>\n
   <td>type</td>\n
diff --git a/bt5/erp5_ui_test/PathTemplateItem/portal_tests/relation_field_zuite/testSimpleRequiredRelationField.xml b/bt5/erp5_ui_test/PathTemplateItem/portal_tests/relation_field_zuite/testSimpleRequiredRelationField.xml
index 758b44a191..af43c7f6d7 100644
--- a/bt5/erp5_ui_test/PathTemplateItem/portal_tests/relation_field_zuite/testSimpleRequiredRelationField.xml
+++ b/bt5/erp5_ui_test/PathTemplateItem/portal_tests/relation_field_zuite/testSimpleRequiredRelationField.xml
@@ -126,7 +126,7 @@ metal:use-macro="here/RelationFieldZuite_CommonTemplate/macros/init"\n
 <tr>\n
   <td>type</td>\n
   <td>listbox_title</td>\n
-  <td>a</td>\n
+  <td>=a</td>\n
 </tr>\n
 <tr>\n
   <td>clickAndWait</td>\n
@@ -135,7 +135,7 @@ metal:use-macro="here/RelationFieldZuite_CommonTemplate/macros/init"\n
 </tr>\n
 <tr>\n
   <td>click</td>\n
-  <td>//*[@class=\'listbox-data-line-0 DataA\']//input[@type="checkbox"]</td>\n
+  <td>//tr//a[.="a"]/parent::td/preceding-sibling::td/input[@type="checkbox"]</td>\n
   <td></td>\n
 </tr>\n
 <tr>\n
diff --git a/bt5/erp5_upgrader/bt/test_dependency_list b/bt5/erp5_upgrader/bt/test_dependency_list
index 576e46274b..eb1f0199f1 100644
--- a/bt5/erp5_upgrader/bt/test_dependency_list
+++ b/bt5/erp5_upgrader/bt/test_dependency_list
@@ -1,3 +1,3 @@
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_base
 erp5_upgrader_test
\ No newline at end of file
diff --git a/bt5/erp5_upgrader_test/SkinTemplateItem/portal_skins/erp5_upgrader_test/TemplateTool_checkPreUpgradeConsistency.xml b/bt5/erp5_upgrader_test/SkinTemplateItem/portal_skins/erp5_upgrader_test/TemplateTool_checkPreUpgradeConsistency.xml
index 306f9b908f..9d2cf99bbb 100644
--- a/bt5/erp5_upgrader_test/SkinTemplateItem/portal_skins/erp5_upgrader_test/TemplateTool_checkPreUpgradeConsistency.xml
+++ b/bt5/erp5_upgrader_test/SkinTemplateItem/portal_skins/erp5_upgrader_test/TemplateTool_checkPreUpgradeConsistency.xml
@@ -52,7 +52,7 @@
             <key> <string>_body</string> </key>
             <value> <string>template_tool = context\n
 \n
-return template_tool.upgradeSite((\'erp5_full_text_myisam_catalog\',),\n
+return template_tool.upgradeSite((\'erp5_full_text_mroonga_catalog\',),\n
   dry_run=(not fixit))\n
 </string> </value>
         </item>
diff --git a/bt5/erp5_web_shacache/bt/test_dependency_list b/bt5/erp5_web_shacache/bt/test_dependency_list
index 418a1a563a..bff77e32d8 100644
--- a/bt5/erp5_web_shacache/bt/test_dependency_list
+++ b/bt5/erp5_web_shacache/bt/test_dependency_list
@@ -1 +1 @@
-erp5_full_text_myisam_catalog
\ No newline at end of file
+erp5_full_text_mroonga_catalog
\ No newline at end of file
diff --git a/bt5/erp5_web_shadir/bt/test_dependency_list b/bt5/erp5_web_shadir/bt/test_dependency_list
index d7bf03237e..6d48b667bc 100644
--- a/bt5/erp5_web_shadir/bt/test_dependency_list
+++ b/bt5/erp5_web_shadir/bt/test_dependency_list
@@ -1,4 +1,4 @@
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_base
 erp5_jquery
 erp5_ingestion_mysql_innodb_catalog
diff --git a/bt5/erp5_web_ung_core/bt/test_dependency_list b/bt5/erp5_web_ung_core/bt/test_dependency_list
index 418a1a563a..bff77e32d8 100644
--- a/bt5/erp5_web_ung_core/bt/test_dependency_list
+++ b/bt5/erp5_web_ung_core/bt/test_dependency_list
@@ -1 +1 @@
-erp5_full_text_myisam_catalog
\ No newline at end of file
+erp5_full_text_mroonga_catalog
\ No newline at end of file
diff --git a/bt5/erp5_web_ung_role/bt/test_dependency_list b/bt5/erp5_web_ung_role/bt/test_dependency_list
index fb492e5d53..85d0a05770 100644
--- a/bt5/erp5_web_ung_role/bt/test_dependency_list
+++ b/bt5/erp5_web_ung_role/bt/test_dependency_list
@@ -1,4 +1,4 @@
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_base
 erp5_web
 erp5_ingestion_mysql_innodb_catalog
diff --git a/bt5/erp5_web_ung_theme/bt/test_dependency_list b/bt5/erp5_web_ung_theme/bt/test_dependency_list
index 768ccd338e..bdc5ea937b 100644
--- a/bt5/erp5_web_ung_theme/bt/test_dependency_list
+++ b/bt5/erp5_web_ung_theme/bt/test_dependency_list
@@ -1,5 +1,5 @@
 erp5_ingestion_mysql_innodb_catalog
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_base
 erp5_jquery
 erp5_web
diff --git a/bt5/networkcache_erp5/bt/test_dependency_list b/bt5/networkcache_erp5/bt/test_dependency_list
index 1dafe8b1e0..eb7f29e9d5 100644
--- a/bt5/networkcache_erp5/bt/test_dependency_list
+++ b/bt5/networkcache_erp5/bt/test_dependency_list
@@ -1,3 +1,3 @@
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_pdm
 erp5_data_set
\ No newline at end of file
diff --git a/bt5/test_conflict_resolution/bt/test_dependency_list b/bt5/test_conflict_resolution/bt/test_dependency_list
index 418a1a563a..bff77e32d8 100644
--- a/bt5/test_conflict_resolution/bt/test_dependency_list
+++ b/bt5/test_conflict_resolution/bt/test_dependency_list
@@ -1 +1 @@
-erp5_full_text_myisam_catalog
\ No newline at end of file
+erp5_full_text_mroonga_catalog
\ No newline at end of file
diff --git a/product/ERP5/Document/ERP5ProjectUnitTestDistributor.py b/product/ERP5/Document/ERP5ProjectUnitTestDistributor.py
index 6f229dfb59..c37b4a8b1b 100644
--- a/product/ERP5/Document/ERP5ProjectUnitTestDistributor.py
+++ b/product/ERP5/Document/ERP5ProjectUnitTestDistributor.py
@@ -37,7 +37,7 @@ import string
 from zLOG import LOG,INFO,ERROR
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions
-from Products.ZSQLCatalog.SQLCatalog import Query
+from Products.ZSQLCatalog.SQLCatalog import SimpleQuery
 TEST_SUITE_MAX = 4
 # Depending on the test suite priority, we will affect
 # more or less cores
@@ -210,7 +210,10 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
 
     tag = "%s_%s" % (self.getRelativeUrl(), title)
     if portal.portal_activities.countMessageWithTag(tag) == 0:
-      test_node_list = test_node_module.searchFolder(portal_type="Test Node",title=title)
+      test_node_list = test_node_module.searchFolder(
+        portal_type="Test Node",
+        title=SimpleQuery(comparison_operator='=', title=title),
+      )
       assert len(test_node_list) in (0, 1), "Unable to find testnode : %s" % title
       test_node = None
       if len(test_node_list) == 1:
@@ -244,9 +247,11 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
     from_date = now - 30
     def getTestSuiteSortKey(test_suite):
       test_result = portal.portal_catalog(portal_type="Test Result",
-                                          title='="%s"' % test_suite.getTitle(),
-                                          modification_date=Query(**{"creation_date": from_date,
-                                                                  "range": "min"}),
+                                          title=SimpleQuery(title=test_suite.getTitle()),
+                                          creation_date=SimpleQuery(
+                                            creation_date=from_date,
+                                            comparison_operator='>=',
+                                          ),
                                           sort_on=[("modification_date", "descending")],
                                           limit=1)
       if len(test_result):
@@ -304,7 +309,10 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
     config_list = []
     tag = "%s_%s" % (self.getRelativeUrl(), title)
     if portal.portal_activities.countMessageWithTag(tag) == 0:
-      test_node_list = test_node_module.searchFolder(portal_type="Test Node",title=title)
+      test_node_list = test_node_module.searchFolder(
+        portal_type="Test Node",
+        title=SimpleQuery(comparison_operator='=', title=title),
+      )
       assert len(test_node_list) in (0, 1), "Unable to find testnode : %s" % title
       test_node = None
       if len(test_node_list) == 1:
@@ -365,7 +373,9 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
 
   def _getTestNodeFromTitle(self, node_title):
     test_node_list = self._getTestNodeModule().searchFolder(
-                       portal_type='Test Node', title="='%s'" % node_title)
+      portal_type="Test Node",
+      title=SimpleQuery(comparison_operator='=', title=node_title),
+    )
     assert len(test_node_list) == 1, "We found %i test nodes for %s" % (
                                       len(test_node_list), node_title)
     test_node = test_node_list[0].getObject()
@@ -373,7 +383,9 @@ class ERP5ProjectUnitTestDistributor(XMLObject):
 
   def _getTestSuiteFromTitle(self, suite_title):
     test_suite_list = self._getTestSuiteModule().searchFolder(
-                       portal_type='Test Suite', title="='%s'" % suit_tile, validation_state="validated")
+      portal_type='Test Suite',
+      title=SimpleQuery(comparison_operator='=', title=suite_title),
+      validation_state='validated')
     assert len(test_suite_list) == 1, "We found %i test suite for %s" % (
                                       len(test_suite_list), name)
     test_suite = test_suite_list[0].getObject()
diff --git a/product/ERP5/Tool/TaskDistributionTool.py b/product/ERP5/Tool/TaskDistributionTool.py
index edd65d23e5..961e02d9d5 100644
--- a/product/ERP5/Tool/TaskDistributionTool.py
+++ b/product/ERP5/Tool/TaskDistributionTool.py
@@ -30,6 +30,7 @@ import random
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions, PropertySheet, Constraint, interfaces
 from Products.ERP5Type.Tool.BaseTool import BaseTool
+from Products.ZSQLCatalog.SQLCatalog import SimpleQuery
 from zLOG import LOG
 from xmlrpclib import Binary
 
@@ -91,7 +92,7 @@ class TaskDistributionTool(BaseTool):
     def createTestResultLineList(test_result, test_name_list):
       duration_list = []
       previous_test_result_list = portal.test_result_module.searchFolder(
-             title='="%s"' % test_result.getTitle(),
+             title=SimpleQuery(comparison_operator='=', title=test_result.getTitle()),
              sort_on=[('creation_date','descending')],
              simulation_state=('stopped', 'public_stopped'),
              limit=1)
@@ -126,7 +127,7 @@ class TaskDistributionTool(BaseTool):
       int_index, reference = revision
     result_list = portal.test_result_module.searchFolder(
                          portal_type="Test Result",
-                         title='="%s"' % test_title,
+                         title=SimpleQuery(comparison_operator='=', title=test_title),
                          sort_on=(("creation_date","descending"),),
                          limit=1)
     if result_list:
@@ -162,7 +163,7 @@ class TaskDistributionTool(BaseTool):
       test_result._setIntIndex(int_index)
     if project_title is not None:
       project_list = portal.portal_catalog(portal_type='Project',
-                                           title='="%s"' % project_title)
+                                           title=SimpleQuery(comparison_operator='=', title=project_title))
       if len(project_list) != 1:
         raise ValueError('found this list of project : %r for title %r' % \
                       ([x.path for x in project_list], project_title))
diff --git a/product/ERP5/bootstrap/erp5_core/bt/test_dependency_list b/product/ERP5/bootstrap/erp5_core/bt/test_dependency_list
index 9a77167a96..8149202de5 100644
--- a/product/ERP5/bootstrap/erp5_core/bt/test_dependency_list
+++ b/product/ERP5/bootstrap/erp5_core/bt/test_dependency_list
@@ -1,2 +1,2 @@
-erp5_full_text_myisam_catalog
+erp5_full_text_mroonga_catalog
 erp5_base
\ No newline at end of file
diff --git a/product/ERP5/bootstrap/erp5_mysql_innodb_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_makeTranslatedTitleQuery.xml b/product/ERP5/bootstrap/erp5_mysql_innodb_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_makeTranslatedTitleQuery.xml
index 60c0f3ff7f..925c1de5d5 100644
--- a/product/ERP5/bootstrap/erp5_mysql_innodb_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_makeTranslatedTitleQuery.xml
+++ b/product/ERP5/bootstrap/erp5_mysql_innodb_catalog/CatalogMethodTemplateItem/portal_catalog/erp5_mysql_innodb/SQLCatalog_makeTranslatedTitleQuery.xml
@@ -56,8 +56,12 @@ portal = context.getPortalObject()\n
 # This scriptable key supports content_translation if the table is present\n
 catalog = portal.portal_catalog.getSQLCatalog()\n
 if \'content_translation\' in catalog.getProperty(\'sql_search_tables\'):\n
-  return AndQuery(SimpleQuery(**{\'content_translation.translated_text\': value, \'comparison_operator\': \'match\'}),\n
-                  Query(**{\'content_translation.property_name\': \'title\'}))\n
+  if [x for x in catalog.getProperty(\'sql_catalog_search_keys\', []) if \'Mroonga\' in x]:\n
+    return AndQuery(SimpleQuery(**{\'content_translation.translated_text\': value, \'comparison_operator\': \'mroonga_boolean\'}),\n
+                    Query(**{\'content_translation.property_name\': \'title\'}))\n
+  else:\n
+    return AndQuery(SimpleQuery(**{\'content_translation.translated_text\': value, \'comparison_operator\': \'match_boolean\'}),\n
+                    Query(**{\'content_translation.property_name\': \'title\'}))\n
 \n
 # Otherwise it simply use title\n
 return Query(title=value)\n
diff --git a/product/ERP5/tests/testCRM.py b/product/ERP5/tests/testCRM.py
index ce0cba0eb2..e716aacddf 100644
--- a/product/ERP5/tests/testCRM.py
+++ b/product/ERP5/tests/testCRM.py
@@ -82,7 +82,7 @@ class TestCRM(BaseTestCRM):
     return "CRM"
 
   def getBusinessTemplateList(self):
-    return ('erp5_full_text_myisam_catalog',
+    return ('erp5_full_text_mroonga_catalog',
             'erp5_core_proxy_field_legacy',
             'erp5_base',
             'erp5_ingestion',
@@ -571,7 +571,7 @@ class TestCRMMailIngestion(BaseTestCRM):
   def getBusinessTemplateList(self):
     # Mail Ingestion must work with CRM alone.
     return ('erp5_core_proxy_field_legacy',
-            'erp5_full_text_myisam_catalog',
+            'erp5_full_text_mroonga_catalog',
             'erp5_base',
             'erp5_ingestion',
             'erp5_ingestion_mysql_innodb_catalog',
diff --git a/product/ERP5/tests/testERP5Coordinate.py b/product/ERP5/tests/testERP5Coordinate.py
index a847ff78bd..70aa9b8c4c 100644
--- a/product/ERP5/tests/testERP5Coordinate.py
+++ b/product/ERP5/tests/testERP5Coordinate.py
@@ -48,7 +48,7 @@ class TestERP5Coordinate(ERP5TypeTestCase):
       Return the list of required business templates.
     """
     return ('erp5_core_proxy_field_legacy',
-            'erp5_full_text_myisam_catalog',
+            'erp5_full_text_mroonga_catalog',
             'erp5_base',)
 
   def beforeTearDown(self):
diff --git a/product/ERP5/tests/testERP5Credential.py b/product/ERP5/tests/testERP5Credential.py
index a7ca271007..872dd082eb 100644
--- a/product/ERP5/tests/testERP5Credential.py
+++ b/product/ERP5/tests/testERP5Credential.py
@@ -55,7 +55,7 @@ class TestERP5Credential(ERP5TypeTestCase):
 
   def getBusinessTemplateList(self):
     return (
-      'erp5_full_text_myisam_catalog',
+      'erp5_full_text_mroonga_catalog',
       'erp5_core_proxy_field_legacy',
       'erp5_base',
       'erp5_jquery',
diff --git a/product/ERP5/tests/testERP5eGov.py b/product/ERP5/tests/testERP5eGov.py
index 8a48034a5e..9424c682e2 100644
--- a/product/ERP5/tests/testERP5eGov.py
+++ b/product/ERP5/tests/testERP5eGov.py
@@ -63,7 +63,7 @@ class TestEgov(ERP5TypeTestCase):
   def getBusinessTemplateList(self):
     """return list of business templates to be installed. """
     bt_list = ['erp5_core_proxy_field_legacy',
-               'erp5_full_text_myisam_catalog',
+               'erp5_full_text_mroonga_catalog',
                'erp5_base',
                'erp5_web',
                'erp5_ingestion_mysql_innodb_catalog',
diff --git a/product/ERP5/tests/testFunctionalRunMyDocSample.py b/product/ERP5/tests/testFunctionalRunMyDocSample.py
index 84d3348c4c..84831c2fa0 100644
--- a/product/ERP5/tests/testFunctionalRunMyDocSample.py
+++ b/product/ERP5/tests/testFunctionalRunMyDocSample.py
@@ -56,7 +56,7 @@ class TestZeleniumRunMyDocSample(ERP5TypeFunctionalTestCase):
     """
       Return the list of business templates.
     """
-    return ('erp5_core_proxy_field_legacy', 'erp5_full_text_myisam_catalog',
+    return ('erp5_core_proxy_field_legacy', 'erp5_full_text_mroonga_catalog',
             'erp5_base', 'erp5_ui_test_core','erp5_web', 'erp5_ingestion',
             'erp5_accounting',
             'erp5_jquery', 'erp5_dms', 'erp5_jquery_ui', 'erp5_web',
diff --git a/product/ERP5/tests/testI18NSearch.py b/product/ERP5/tests/testI18NSearch.py
index 930087f35a..f27485fe40 100644
--- a/product/ERP5/tests/testI18NSearch.py
+++ b/product/ERP5/tests/testI18NSearch.py
@@ -43,21 +43,42 @@ class TestI18NSearch(ERP5TypeTestCase):
       portal_type='Person',
       first_name='Gabriel',
       last_name='Fauré',
+      description='Quick brown fox jumps over the lazy dog.',
       )
     person2 = person_module.newContent(
       portal_type='Person',
       first_name='武者小路',
-      last_name='実篤'
+      last_name='実篤',
+      description='Slow white fox jumps over the diligent dog.',
       )
     self.tic()
+
     # check if 'é' == 'e' collation works
     result = person_module.searchFolder(SearchableText='Faure')
     self.assertEqual(len(result), 1)
     self.assertEqual(result[0].getPath(), person1.getPath())
+    result = person_module.searchFolder(title='Faure')
+    self.assertEqual(len(result), 1)
+    self.assertEqual(result[0].getPath(), person1.getPath())
+
     # check if a partial string of CJK string matches
     result = person_module.searchFolder(SearchableText='武者')
     self.assertEqual(len(result), 1)
     self.assertEqual(result[0].getPath(), person2.getPath())
+    result = person_module.searchFolder(title='武者')
+    self.assertEqual(len(result), 1)
+    self.assertEqual(result[0].getPath(), person2.getPath())
+
+    # check boolean language mode search
+    result = person_module.searchFolder(SearchableText='+quick +fox +dog')
+    self.assertEqual(len(result), 1)
+    self.assertEqual(result[0].getPath(), person1.getPath())
+    result = person_module.searchFolder(description='+quick +fox +dog')
+    self.assertEqual(len(result), 1)
+    self.assertEqual(result[0].getPath(), person1.getPath())
+
+    # check fulltext search for automatically generated related keys.
+    self.assertTrue('MATCH' in self.portal.portal_catalog(destination_title='Faure', src__=1))
 
 def test_suite():
   suite = unittest.TestSuite()
diff --git a/product/ERP5/tests/testKM.py b/product/ERP5/tests/testKM.py
index 2669aa310f..e796716f0e 100644
--- a/product/ERP5/tests/testKM.py
+++ b/product/ERP5/tests/testKM.py
@@ -50,7 +50,7 @@ class TestKMMixIn(TestDocumentMixin):
   manager_password = ''
   website_id = 'km_test'
   business_template_list = ['erp5_core_proxy_field_legacy',
-                            'erp5_full_text_myisam_catalog','erp5_base',
+                            'erp5_full_text_mroonga_catalog','erp5_base',
                             'erp5_jquery', 'erp5_jquery_ui', 'erp5_knowledge_pad',
                             'erp5_ingestion_mysql_innodb_catalog', 'erp5_ingestion',
                             'erp5_web', 'erp5_dms',
diff --git a/product/ERP5/tests/testSpellChecking.py b/product/ERP5/tests/testSpellChecking.py
index 3c67e102ca..68d58633bf 100644
--- a/product/ERP5/tests/testSpellChecking.py
+++ b/product/ERP5/tests/testSpellChecking.py
@@ -68,7 +68,7 @@ class TestSpellChecking(ERP5TypeTestCase):
     return "Spell Checking Test"
 
   def getBusinessTemplateList(self):
-    return ('erp5_full_text_myisam_catalog',
+    return ('erp5_full_text_mroonga_catalog',
             'erp5_base',
             'erp5_simulation',
             'erp5_accounting',
diff --git a/product/ERP5/tests/testTemplateTool.py b/product/ERP5/tests/testTemplateTool.py
index 01ebb39f81..dc9d6da28c 100644
--- a/product/ERP5/tests/testTemplateTool.py
+++ b/product/ERP5/tests/testTemplateTool.py
@@ -52,7 +52,7 @@ class TestTemplateTool(ERP5TypeTestCase):
 
   def getBusinessTemplateList(self):
     return ('erp5_core_proxy_field_legacy',
-            'erp5_full_text_myisam_catalog',
+            'erp5_full_text_mroonga_catalog',
             'erp5_base',
             'erp5_stock_cache',
             'erp5_csv_style')
@@ -650,7 +650,7 @@ class TestTemplateTool(ERP5TypeTestCase):
       'erp5_core_proxy_field_legacy': first_group,
       'erp5_mysql_innodb_catalog': first_group,
       'erp5_core': first_group,
-      'erp5_full_text_myisam_catalog': first_group,
+      'erp5_full_text_mroonga_catalog': first_group,
       'erp5_xhtml_style': first_group,
       'erp5_ingestion_mysql_innodb_catalog': second_group,
       'erp5_base': second_group,
diff --git a/product/ERP5Catalog/CatalogTool.py b/product/ERP5Catalog/CatalogTool.py
index 95b031349f..bbc75ad133 100644
--- a/product/ERP5Catalog/CatalogTool.py
+++ b/product/ERP5Catalog/CatalogTool.py
@@ -881,9 +881,10 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
       by looking at the category tree.
 
       For exemple it will generate:
-      destination_title | category,catalog/title/z_related_destination
-      default_destination_title | category,catalog/title/z_related_destination
-      strict_destination_title | category,catalog/title/z_related_strict_destination
+      destination_reference | category,catalog/reference/z_related_destination
+      default_destination_reference | category,catalog/reference/z_related_destination
+      strict_destination_reference | category,catalog/reference/z_related_strict_destination
+      destination_title | category,catalog_full_text/title/z_related_destination
 
       strict_ related keys only returns documents which are strictly member of
       the category.
@@ -917,9 +918,18 @@ class CatalogTool (UniqueObject, ZCatalog, CMFCoreCatalogTool, ActiveObject):
             if related:
               end_key = end_key[len(related_string):]
             # XXX: joining with non-catalog tables is not trivial and requires
-            # ZSQLCatalog's ColumnMapper cooperation, so only allow catalog
-            # columns.
-            if 'catalog' in column_map.get(end_key, ()):
+            # ZSQLCatalog's ColumnMapper cooperation, so only allow columns in
+            # catalog or catalog_full_text tables.
+            if end_key != 'uid' and 'catalog_full_text' in column_map.get(end_key, ()):
+              related_key_list.append(
+                prefix + key + ' | category,catalog_full_text/' +
+                end_key +
+                '/z_related_' +
+                ('strict_' if strict else '') +
+                expected_base_cat_id +
+                ('_related' if related else '')
+              )
+            elif 'catalog' in column_map.get(end_key, ()):
               is_uid = end_key == 'uid'
               if is_uid:
                 end_key = 'uid' if related else 'category_uid'
diff --git a/product/ERP5Catalog/tests/testArchive.py b/product/ERP5Catalog/tests/testArchive.py
index 14c3243a15..306f6beedf 100644
--- a/product/ERP5Catalog/tests/testArchive.py
+++ b/product/ERP5Catalog/tests/testArchive.py
@@ -49,7 +49,7 @@ class TestArchive(InventoryAPITestCase):
   def getBusinessTemplateList(self):
     return InventoryAPITestCase.getBusinessTemplateList(self) + (
       'erp5_archive',
-      'erp5_full_text_myisam_catalog',
+      'erp5_full_text_mroonga_catalog',
     )
 
   # Different variables used for this test
diff --git a/product/ERP5Catalog/tests/testERP5Catalog.py b/product/ERP5Catalog/tests/testERP5Catalog.py
index dea8a17360..f74fd1dd91 100644
--- a/product/ERP5Catalog/tests/testERP5Catalog.py
+++ b/product/ERP5Catalog/tests/testERP5Catalog.py
@@ -44,7 +44,7 @@ from Products.ERP5Type.tests.utils import createZODBPythonScript, todo_erp5, \
 from Products.ZSQLCatalog.ZSQLCatalog import HOT_REINDEXING_FINISHED_STATE,\
       HOT_REINDEXING_RECORDING_STATE, HOT_REINDEXING_DOUBLE_INDEXING_STATE
 from Products.CMFActivity.Errors import ActivityFlushError
-from Products.ZSQLCatalog.SQLCatalog import Query, ComplexQuery
+from Products.ZSQLCatalog.SQLCatalog import Query, ComplexQuery, SimpleQuery
 
 
 from OFS.ObjectManager import ObjectManager
@@ -92,7 +92,7 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     return "ERP5Catalog"
 
   def getBusinessTemplateList(self):
-    return ('erp5_full_text_myisam_catalog', 'erp5_base',)
+    return ('erp5_full_text_mroonga_catalog', 'erp5_base',)
 
   # Different variables used for this test
   username = 'seb'
@@ -124,6 +124,7 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
                     self.getCategoryTool().region,
                     self.getCategoryTool().group ]:
       module.manage_delObjects(list(module.objectIds()))
+      module.reindexObject()
     # Remove copied sql_connector and catalog
     if self.new_erp5_sql_connection in self.portal.objectIds():
       self.portal.manage_delObjects([self.new_erp5_sql_connection])
@@ -189,7 +190,7 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     person = person_module.newContent(id='1',portal_type='Person')
     path_list = [person.getRelativeUrl()]
     self.checkRelativeUrlNotInSQLPathList(path_list)
-    person.immediateReindexObject()
+    self.tic()
     self.checkRelativeUrlInSQLPathList(path_list)
     person_module.manage_delObjects('1')
     self.tic()
@@ -197,10 +198,10 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     # Now we will ask to immediatly reindex
     person = person_module.newContent(id='2',
                                       portal_type='Person',)
-    person.immediateReindexObject()
+    self.tic()
     path_list = [person.getRelativeUrl()]
     self.checkRelativeUrlInSQLPathList(path_list)
-    person.immediateReindexObject()
+    self.tic()
     self.checkRelativeUrlInSQLPathList(path_list)
     person_module.manage_delObjects('2')
     self.tic()
@@ -209,7 +210,7 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     person = person_module.newContent(id='3',portal_type='Person')
     path_list = [person.getRelativeUrl()]
     self.checkRelativeUrlNotInSQLPathList(path_list)
-    person.immediateReindexObject()
+    self.tic()
     self.checkRelativeUrlInSQLPathList(path_list)
     person_module.deleteContent('3')
     # Now delete things is made with activities
@@ -223,10 +224,10 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     folder_object_list = [x.getObject().getId() for x in person_module.searchFolder()]
     self.assertEqual([],folder_object_list)
     person = person_module.newContent(id='4',portal_type='Person',)
-    person.immediateReindexObject()
+    self.tic()
     folder_object_list = [x.getObject().getId() for x in person_module.searchFolder()]
     self.assertEqual(['4'],folder_object_list)
-    person.immediateReindexObject()
+    self.tic()
     person_module.manage_delObjects('4')
     self.tic()
     folder_object_list = [x.getObject().getId() for x in person_module.searchFolder()]
@@ -240,7 +241,7 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     self.assertEqual([],folder_object_list)
 
     person = person_module.newContent(id='4',portal_type='Person')
-    person.immediateReindexObject()
+    self.tic()
     folder_object_list = [x.getObject().getId() for x in person_module.searchFolder()]
     self.assertEqual(['4'],folder_object_list)
 
@@ -274,7 +275,7 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     portal_catalog.manage_catalogClear()
 
     person = person_module.newContent(id='4',portal_type='Person')
-    person.immediateReindexObject()
+    self.tic()
     folder_object_list = [x.getObject().getId() for x in person_module.searchFolder()]
     self.assertEqual(['4'],folder_object_list)
 
@@ -298,7 +299,7 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     portal_catalog.manage_catalogClear()
 
     person = person_module.newContent(id='4',portal_type='Person')
-    person.immediateReindexObject()
+    self.tic()
     folder_object_list = [x.getObject().getId() for x in person_module.searchFolder()]
     self.assertEqual(['4'],folder_object_list)
 
@@ -310,11 +311,11 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     portal_catalog.manage_catalogClear()
 
     person = person_module.newContent(id='a',portal_type='Person',title='a',description='z')
-    person.immediateReindexObject()
+    self.tic()
     person = person_module.newContent(id='b',portal_type='Person',title='a',description='y')
-    person.immediateReindexObject()
+    self.tic()
     person = person_module.newContent(id='c',portal_type='Person',title='a',description='x')
-    person.immediateReindexObject()
+    self.tic()
     folder_object_list = [x.getObject().getId()
               for x in person_module.searchFolder(sort_on=[('id','ascending')])]
     self.assertEqual(['a','b','c'],folder_object_list)
@@ -335,11 +336,11 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     portal_catalog.manage_catalogClear()
 
     person = person_module.newContent(id='a',portal_type='Person',title='1')
-    person.immediateReindexObject()
+    self.tic()
     person = person_module.newContent(id='b',portal_type='Person',title='2')
-    person.immediateReindexObject()
+    self.tic()
     person = person_module.newContent(id='c',portal_type='Person',title='12')
-    person.immediateReindexObject()
+    self.tic()
     folder_object_list = [x.getObject().getTitle() for x in person_module.searchFolder(sort_on=[('title','ascending')])]
     self.assertEqual(['1','12','2'],folder_object_list)
     folder_object_list = [x.getObject().getTitle() for x in person_module.searchFolder(sort_on=[('title','ascending','int')])]
@@ -654,7 +655,7 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
 
     title = 'Sébastien'
     person = person_module.newContent(id='5',portal_type='Person',title=title)
-    person.immediateReindexObject()
+    self.tic()
     folder_object_list = [x.getObject().getId() for x in person_module.searchFolder()]
     self.assertEqual(['5'],folder_object_list)
     folder_object_list = [x.getObject().getId() for x in
@@ -666,7 +667,7 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
 
     title = 'Sébastien'
     person = person_module.newContent(id='5',portal_type='Person', title=title)
-    person.immediateReindexObject()
+    self.tic()
     folder_object_list = [x.getObject().getId() for x in
                               person_module.searchFolder(title=title)]
     self.assertEqual(['5'],folder_object_list)
@@ -690,10 +691,10 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
         portal_category.group.objectIds()])
     group_nexedi_category = portal_category.group\
                                 .newContent( id = 'nexedi', title='Nexedi',
-                                             description='a')
+                                             reference='a')
     group_nexedi_category2 = portal_category.group\
                                 .newContent( id = 'storever', title='Storever',
-                                             description='b')
+                                             reference='b')
     module = portal.getDefaultModule('Organisation')
     organisation = module.newContent(portal_type='Organisation',)
     organisation.setGroup('nexedi')
@@ -716,17 +717,17 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     organisation_list = [x.getObject() for x in
                          module.searchFolder(group_id='storever')]
     self.assertEqual(organisation_list,[organisation2])
-    # Try to get the organisation with the group description 'a'
+    # Try to get the organisation with the group reference 'a'
     organisation_list = [x.getObject() for x in
-                         module.searchFolder(group_description='a')]
+                         module.searchFolder(group_reference='a')]
     self.assertEqual(organisation_list,[organisation])
-    # Try to get the organisation with the group description 'c'
+    # Try to get the organisation with the group reference 'c'
     organisation_list = [x.getObject() for x in
-                         module.searchFolder(group_description='c')]
+                         module.searchFolder(group_reference='c')]
     self.assertEqual(organisation_list,[])
-    # Try to get the organisation with the default group description 'c'
+    # Try to get the organisation with the default group reference 'c'
     organisation_list = [x.getObject() for x in
-                         module.searchFolder(default_group_description='c')]
+                         module.searchFolder(default_group_reference='c')]
     self.assertEqual(organisation_list,[])
     # Try to get the organisation with group relative_url
     group_relative_url = group_nexedi_category.getRelativeUrl()
@@ -752,10 +753,10 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
         portal_category.group.objectIds()])
     group_nexedi_category = portal_category.group\
                                 .newContent( id = 'nexedi', title='Nexedi',
-                                             description='a')
+                                             reference='a')
     sub_group_nexedi = group_nexedi_category\
                                 .newContent( id = 'erp5', title='ERP5',
-                                             description='b')
+                                             reference='b')
     module = portal.getDefaultModule('Organisation')
     organisation = module.newContent(portal_type='Organisation',)
     organisation.setGroup('nexedi/erp5')
@@ -771,13 +772,13 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     organisation_list = [x.getObject() for x in
                          module.searchFolder(strict_group_title='ERP5')]
     self.assertEqual(organisation_list,[organisation])
-    # Try to get the organisation with the group description a
+    # Try to get the organisation with the group reference a
     organisation_list = [x.getObject() for x in
-                         module.searchFolder(strict_group_description='a')]
+                         module.searchFolder(strict_group_reference='a')]
     self.assertEqual(organisation_list,[])
-    # Try to get the organisation with the group description b
+    # Try to get the organisation with the group reference b
     organisation_list = [x.getObject() for x in
-                         module.searchFolder(strict_group_description='b')]
+                         module.searchFolder(strict_group_reference='b')]
     self.assertEqual(organisation_list,[organisation])
 
   def test_22_SearchingWithUnicode(self):
@@ -793,7 +794,7 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     # Now we will ask to immediatly reindex
     person = person_module.newContent(id='2',
                                       portal_type='Person',)
-    person.immediateReindexObject()
+    self.tic()
     path_list = [person.getRelativeUrl()]
     self.checkRelativeUrlInSQLPathList(path_list)
     # We will delete the connector
@@ -855,19 +856,19 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     """Sort-on parameter and related key. (Assumes that region_title is a \
     valid related key)"""
     self.assertTrue(
-              self.getCatalogTool().buildSQLQuery(region_title='foo',
-              sort_on=(('region_title', 'ascending'),))['order_by_expression'].endswith('.`title` ASC'))
+              self.getCatalogTool().buildSQLQuery(region_reference='foo',
+              sort_on=(('region_reference', 'ascending'),))['order_by_expression'].endswith('.`reference` ASC'))
     self.assertTrue(
-              self.getCatalogTool().buildSQLQuery(region_title='foo',
-              sort_on=(('region_title', 'descending'),))['order_by_expression'].endswith('.`title` DESC'))
+              self.getCatalogTool().buildSQLQuery(region_reference='foo',
+              sort_on=(('region_reference', 'descending'),))['order_by_expression'].endswith('.`reference` DESC'))
     self.assertTrue(
               self.getCatalogTool().buildSQLQuery(
-              sort_on=(('region_title', 'ascending'),))['order_by_expression'].endswith('.`title` ASC'),
+              sort_on=(('region_reference', 'ascending'),))['order_by_expression'].endswith('.`reference` ASC'),
               'sort_on parameter must be taken into account even if related key '
               'is not a parameter of the current query')
     self.assertTrue(
               self.getCatalogTool().buildSQLQuery(
-              sort_on=(('region_title', 'descending'),))['order_by_expression'].endswith('.`title` DESC'),
+              sort_on=(('region_reference', 'descending'),))['order_by_expression'].endswith('.`reference` DESC'),
               'sort_on parameter must be taken into account even if related key '
               'is not a parameter of the current query')
 
@@ -957,8 +958,8 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
 
     self.assertEqual([organisation.getPath()],
         [x.path for x in self.getCatalogTool()(
-                title={'query': (organisation_title, 'something else'),
-                       'operator': 'or'})])
+                **{'catalog.title':{'query': (organisation_title, 'something else'),
+                                    'operator': 'or'}})])
 
   def test_33_SimpleQueryDictWithAndOperator(self):
     """use a dict as a keyword parameter, with AND operator.
@@ -1149,20 +1150,19 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
                                                 SearchableText='title')[0][0])
 
     # 'different' is found in more than 50% of records
-    # MySQL ignores such a word, but Tritonn does not ignore.
-    try:
-      self.portal.erp5_sql_connection.manage_test('SHOW SENNA STATUS')
-    except ProgrammingError:
+    # MySQL ignores such a word, but Mroonga does not ignore.
+    if 'ENGINE=Mroonga' in self.portal.erp5_sql_connection.manage_test(
+        'SHOW CREATE TABLE full_text')[0][1]:
+      # Mroonga
+      self.assertEqual(10, self.getCatalogTool().countResults(
+                portal_type='Organisation', SearchableText='different')[0][0])
+    else:
       # MySQL
       self.assertEqual([],
           [x.getObject for x in self.getCatalogTool()(
                   portal_type='Organisation', SearchableText='different')])
       self.assertEqual(0, self.getCatalogTool().countResults(
                 portal_type='Organisation', SearchableText='different')[0][0])
-    else:
-      # Tritonn
-      self.assertEqual(10, self.getCatalogTool().countResults(
-                portal_type='Organisation', SearchableText='different')[0][0])
 
   def test_43_ManagePasteObject(self):
     portal_catalog = self.getCatalogTool()
@@ -1221,11 +1221,11 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     # Recursive Complex Query
     # (title='abc' and description='abc') OR
     #  title='foo' and description='bar'
-    catalog_kw = {'query':ComplexQuery(ComplexQuery(Query(title='abc'),
-                                                    Query(description='abc'),
+    catalog_kw = {'query':ComplexQuery(ComplexQuery(SimpleQuery(title='abc'),
+                                                    SimpleQuery(description='abc'),
                                                     operator='AND'),
-                                       ComplexQuery(Query(title='foo'),
-                                                    Query(description='bar'),
+                                       ComplexQuery(SimpleQuery(title='foo'),
+                                                    SimpleQuery(description='bar'),
                                                     operator='AND'),
                                        operator='OR')}
     self.failIfDifferentSet([org_a.getPath(), org_f.getPath()],
@@ -1578,11 +1578,11 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     catalog = portal_catalog.objectValues()[0]
 
     person = person_module.newContent(id='a',portal_type='Person',title='a',description='z')
-    person.immediateReindexObject()
+    self.tic()
     person = person_module.newContent(id='b',portal_type='Person',title='a',description='y')
-    person.immediateReindexObject()
+    self.tic()
     person = person_module.newContent(id='c',portal_type='Person',title='a',description='x')
-    person.immediateReindexObject()
+    self.tic()
     index_columns = getattr(catalog, 'sql_catalog_index_on_order_keys', None)
     self.assertNotEqual(index_columns, None)
     self.assertEqual(len(index_columns), 0)
@@ -2105,7 +2105,7 @@ class TestERP5Catalog(ERP5TypeTestCase, LogInterceptor):
     # description is not a keyword by default. (This might change in the
     # future, in this case, this test have to be updated)
     self.assertSameSet([doc], [x.getObject() for x in
-        ctool(portal_type='Organisation', description='Foo')])
+        ctool(portal_type='Organisation', description='=Foo')])
     self.assertEqual({doc, other_doc}, {x.getObject() for x in
       ctool(portal_type='Organisation', description=dict(query='Foo',
                                                          key='Keyword'))})
@@ -3546,19 +3546,19 @@ VALUES
         portal_category.group.objectIds()])
     group_nexedi_category = portal_category.group\
                                 .newContent( id = 'nexedi', title='Nexedi',
-                                             description='a')
+                                             reference='a')
     group_nexedi_category2 = portal_category.group\
                                 .newContent( id = 'storever', title='Storever',
-                                             description='b')
+                                             reference='b')
     module = portal.getDefaultModule('Organisation')
     organisation = module.newContent(portal_type='Organisation',
                                      title='Nexedi Orga',
-                                     description='c')
+                                     reference='c')
     organisation.setGroup('nexedi')
     self.assertEqual(organisation.getGroupValue(), group_nexedi_category)
     organisation2 = module.newContent(portal_type='Organisation',
                                       title='Storever Orga',
-                                      description='d')
+                                      reference='d')
     organisation2.setGroup('storever')
     organisation2.setTitle('Organisation 2')
     self.assertEqual(organisation2.getGroupValue(), group_nexedi_category2)
@@ -3580,19 +3580,19 @@ VALUES
     category_list = [x.getObject() for x in
                          base_category.searchFolder(group_related_id='storever')]
     self.assertEqual(category_list,[group_nexedi_category2])
-    # Try to get the category with the group related organisation description 'd'
+    # Try to get the category with the group related organisation reference 'd'
     category_list = [x.getObject() for x in
-                         base_category.searchFolder(group_related_description='d')]
+                         base_category.searchFolder(group_related_reference='d')]
     self.assertEqual(category_list,[group_nexedi_category2])
-    # Try to get the category with the group related organisation description
+    # Try to get the category with the group related organisation reference
     # 'e'
     category_list = [x.getObject() for x in
-                         base_category.searchFolder(group_related_description='e')]
+                         base_category.searchFolder(group_related_reference='e')]
     self.assertEqual(category_list,[])
-    # Try to get the category with the default group related organisation description
+    # Try to get the category with the default group related organisation reference
     # 'e'
     category_list = [x.getObject() for x in
-                         base_category.searchFolder(default_group_related_description='e')]
+                         base_category.searchFolder(default_group_related_reference='e')]
     self.assertEqual(category_list,[])
     # Try to get the category with the group related organisation relative_url
     organisation_relative_url = organisation.getRelativeUrl()
@@ -3618,19 +3618,19 @@ VALUES
         portal_category.group.objectIds()])
     group_nexedi_category = portal_category.group\
                                 .newContent( id = 'nexedi', title='Nexedi',
-                                             description='a')
+                                             reference='a')
     sub_group_nexedi = group_nexedi_category\
                                 .newContent( id = 'erp5', title='ERP5',
-                                             description='b')
+                                             reference='b')
     module = portal.getDefaultModule('Organisation')
     organisation = module.newContent(portal_type='Organisation',
                                      title='ERP5 Orga',
-                                     description='c')
+                                     reference='c')
     organisation.setGroup('nexedi/erp5')
     self.assertEqual(organisation.getGroupValue(), sub_group_nexedi)
     organisation2 = module.newContent(portal_type='Organisation',
                                      title='Nexedi Orga',
-                                     description='d')
+                                     reference='d')
     organisation2.setGroup('nexedi')
     # Flush message queue
     self.tic()
@@ -3649,15 +3649,15 @@ VALUES
                          base_category.portal_catalog(
                            strict_group_related_title='ERP5 Orga')]
     self.assertEqual(category_list,[sub_group_nexedi])
-    # Try to get the category with the group related organisation description d
+    # Try to get the category with the group related organisation reference d
     category_list = [x.getObject() for x in
                          base_category.portal_catalog(
-                           strict_group_related_description='d')]
+                           strict_group_related_reference='d')]
     self.assertEqual(category_list,[group_nexedi_category])
-    # Try to get the category with the group related organisation description c
+    # Try to get the category with the group related organisation reference c
     category_list = [x.getObject() for x in
                          base_category.portal_catalog(
-                           strict_group_related_description='c')]
+                           strict_group_related_reference='c')]
     self.assertEqual(category_list,[sub_group_nexedi])
 
   def test_EscapingLoginInSescurityQuery(self):
@@ -3768,7 +3768,7 @@ VALUES
     title='foo (bar)'
     person = person_module.newContent(portal_type='Person',title=title)
     person_id = person.getId()
-    person.immediateReindexObject()
+    self.tic()
     folder_object_list = [x.getObject().getId() for x in person_module.searchFolder()]
     self.assertTrue(person_id in folder_object_list)
     folder_object_list = [x.getObject().getId() for x in
@@ -3781,15 +3781,16 @@ VALUES
     # Make sure that the catalog will not split it with such research :
     # title=foo AND title=bar
     title='foo bar'
-    person_module.newContent(portal_type='Person',title=title).immediateReindexObject()
+    person_module.newContent(portal_type='Person',title=title)
+    self.tic()
     title = title.replace(' ', '  ')
     person = person_module.newContent(portal_type='Person',title=title)
     person_id = person.getId()
-    person.immediateReindexObject()
+    self.tic()
     folder_object_list = [x.getObject().getId() for x in person_module.searchFolder()]
     self.assertTrue(person_id in folder_object_list)
     folder_object_list = [x.getObject().getId() for x in
-                              person_module.searchFolder(title=title)]
+                              person_module.searchFolder(**{'catalog.title':title})]
     self.assertEqual([person_id],folder_object_list)
 
   def test_SearchFolderWithSingleQuote(self):
@@ -3800,7 +3801,7 @@ VALUES
     title="foo 'bar"
     person = person_module.newContent(portal_type='Person',title=title)
     person_id = person.getId()
-    person.immediateReindexObject()
+    self.tic()
     folder_object_list = [x.getObject().getId() for x in person_module.searchFolder()]
     self.assertTrue(person_id in folder_object_list)
     folder_object_list = [x.getObject().getId() for x in
@@ -3817,7 +3818,7 @@ VALUES
     person = person_module.newContent(portal_type='Person',title=title,
                                       description=description)
     person_uid = person.getUid()
-    person.immediateReindexObject()
+    self.tic()
     folder_object_list = person_module.searchFolder(uid=person_uid, select_dict={'title': None})
     new_title = 'bar'
     new_description = 'foobarfoo'
diff --git a/product/ERP5Catalog/tests/testERP5CatalogSecurityUidOptimization.py b/product/ERP5Catalog/tests/testERP5CatalogSecurityUidOptimization.py
index dcfc5f9fde..9e1a75fc57 100644
--- a/product/ERP5Catalog/tests/testERP5CatalogSecurityUidOptimization.py
+++ b/product/ERP5Catalog/tests/testERP5CatalogSecurityUidOptimization.py
@@ -42,7 +42,7 @@ class TestERP5CatalogSecurityUidOptimization(ERP5TypeTestCase):
     XXX: Inherit from TestERP5Catalog so we test default and security_uid optmization with same tests.
   """
   business_template_list = ['erp5_security_uid_innodb_catalog',
-                            'erp5_full_text_myisam_catalog','erp5_base']
+                            'erp5_full_text_mroonga_catalog','erp5_base']
 
   def getBusinessTemplateList(self):
     return self.business_template_list
diff --git a/product/ERP5Configurator/tests/ConfiguratorTestMixin.py b/product/ERP5Configurator/tests/ConfiguratorTestMixin.py
index ca550ee966..70b306758b 100644
--- a/product/ERP5Configurator/tests/ConfiguratorTestMixin.py
+++ b/product/ERP5Configurator/tests/ConfiguratorTestMixin.py
@@ -85,7 +85,7 @@ class TestLiveConfiguratorWorkflowMixin(SecurityTestCase):
 
   def getBusinessTemplateList(self):
     return ('erp5_core_proxy_field_legacy',
-        'erp5_full_text_myisam_catalog',
+        'erp5_full_text_mroonga_catalog',
         'erp5_base',
         'erp5_workflow',
         'erp5_configurator',
diff --git a/product/ERP5Configurator/tests/testConfiguratorItem.py b/product/ERP5Configurator/tests/testConfiguratorItem.py
index 132bf77da6..87d79d023f 100644
--- a/product/ERP5Configurator/tests/testConfiguratorItem.py
+++ b/product/ERP5Configurator/tests/testConfiguratorItem.py
@@ -43,7 +43,7 @@ class TestConfiguratorItem(TestLiveConfiguratorWorkflowMixin):
 
   def getBusinessTemplateList(self):
     return ('erp5_core_proxy_field_legacy',
-            'erp5_full_text_myisam_catalog',
+            'erp5_full_text_mroonga_catalog',
             'erp5_base',
             'erp5_workflow',
             'erp5_configurator',
diff --git a/product/ERP5Configurator/tests/testConfiguratorTool.py b/product/ERP5Configurator/tests/testConfiguratorTool.py
index f04b8cd989..57f29f4de9 100644
--- a/product/ERP5Configurator/tests/testConfiguratorTool.py
+++ b/product/ERP5Configurator/tests/testConfiguratorTool.py
@@ -38,7 +38,7 @@ class TestConfiguratorTool(TestLiveConfiguratorWorkflowMixin):
 
   def getBusinessTemplateList(self):
     return ('erp5_core_proxy_field_legacy',
-            'erp5_full_text_myisam_catalog',
+            'erp5_full_text_mroonga_catalog',
             'erp5_base',
             'erp5_workflow',
             'erp5_configurator',
diff --git a/product/ERP5Configurator/tests/testFunctionalConfigurator.py b/product/ERP5Configurator/tests/testFunctionalConfigurator.py
index e66f646f2b..fcb01f0d04 100644
--- a/product/ERP5Configurator/tests/testFunctionalConfigurator.py
+++ b/product/ERP5Configurator/tests/testFunctionalConfigurator.py
@@ -47,7 +47,7 @@ class TestZeleniumConfiguratorStandard(ERP5TypeFunctionalTestCase):
     """
       Return the list of business templates.
     """
-    return ('erp5_core_proxy_field_legacy', 'erp5_full_text_myisam_catalog',
+    return ('erp5_core_proxy_field_legacy', 'erp5_full_text_mroonga_catalog',
             'erp5_base', 'erp5_workflow', 'erp5_configurator',
             'erp5_configurator_standard', 'erp5_jquery',
             'erp5_ui_test_core', 'erp5_accounting',
diff --git a/product/ERP5Form/Tool/SelectionTool.py b/product/ERP5Form/Tool/SelectionTool.py
index 9619155831..c86f72d466 100644
--- a/product/ERP5Form/Tool/SelectionTool.py
+++ b/product/ERP5Form/Tool/SelectionTool.py
@@ -1271,7 +1271,8 @@ class SelectionTool( BaseTool, SimpleItem ):
           if len(field_value):
             sql_catalog = self.portal_catalog.getSQLCatalog()
             field_value = sql_catalog.buildQuery({
-              catalog_index: field_value.splitlines()
+              catalog_index:{'query':field_value.splitlines(),
+                             'key':'ExactMatch',},
             }).asSearchTextExpression(sql_catalog, column='')
 
           REQUEST.form[field_key] = field_value
diff --git a/product/ERP5OOo/tests/testDms.py b/product/ERP5OOo/tests/testDms.py
index 81abcd9617..7d34cda0cd 100644
--- a/product/ERP5OOo/tests/testDms.py
+++ b/product/ERP5OOo/tests/testDms.py
@@ -103,7 +103,7 @@ class TestDocumentMixin(ERP5TypeTestCase):
 
   business_template_list = ['erp5_core_proxy_field_legacy',
                             'erp5_jquery',
-                            'erp5_full_text_myisam_catalog',
+                            'erp5_full_text_mroonga_catalog',
                             'erp5_base',
                             'erp5_ingestion_mysql_innodb_catalog',
                             'erp5_ingestion',
@@ -1155,13 +1155,13 @@ class TestDocument(TestDocumentMixin):
     self.assertSameSet([document_1,web_page_1], getAdvancedSearchStringResultList(**kw))
 
     # exact word search
-    kw = {'searchabletext_any': '*',
+    kw = {'searchabletext_any': '',
           'searchabletext_phrase': 'linux python'}
     self.assertSameSet([document_1], getAdvancedSearchStringResultList(**kw))
-    kw = {'searchabletext_any': '*',
+    kw = {'searchabletext_any': '',
           'searchabletext_phrase': 'python linux'}
     self.assertSameSet([document_2], getAdvancedSearchStringResultList(**kw))
-    kw = {'searchabletext_any': '*',
+    kw = {'searchabletext_any': '',
           'searchabletext_phrase': 'python linux knowledge system'}
     self.assertSameSet([document_2], getAdvancedSearchStringResultList(**kw))
 
@@ -1243,7 +1243,7 @@ class TestDocument(TestDocumentMixin):
     # should return all documents matching a word no matter of contributor
     self.assertSameSet([web_page_1, document_4], getAdvancedSearchStringResultList(**kw))
     kw = {'searchabletext_any': 'owner',
-          'contributor_title': '%Contributor%'}
+          'contributor_title': 'Contributor'}
     self.assertSameSet([document_4], getAdvancedSearchStringResultList(**kw))
 
     # multiple portal_type specified
diff --git a/product/ERP5OOo/tests/testOOoDynamicStyle.py b/product/ERP5OOo/tests/testOOoDynamicStyle.py
index c1b180823d..c57f82753f 100644
--- a/product/ERP5OOo/tests/testOOoDynamicStyle.py
+++ b/product/ERP5OOo/tests/testOOoDynamicStyle.py
@@ -52,7 +52,7 @@ class TestOooDynamicStyle(ERP5TypeTestCase):
 
   def getBusinessTemplateList(self):
     return ('erp5_core_proxy_field_legacy',
-            'erp5_full_text_myisam_catalog',
+            'erp5_full_text_mroonga_catalog',
             'erp5_base',
             'erp5_ingestion_mysql_innodb_catalog',
             'erp5_ingestion',
diff --git a/product/ERP5TioSafe/tests/testTioSafeMixin.py b/product/ERP5TioSafe/tests/testTioSafeMixin.py
index 43a6efe656..75133d63ac 100644
--- a/product/ERP5TioSafe/tests/testTioSafeMixin.py
+++ b/product/ERP5TioSafe/tests/testTioSafeMixin.py
@@ -49,7 +49,7 @@ class testTioSafeMixin(ERP5TypeTestCase):
     """ Return the list of BT required by unit tests. """
     return (
         'erp5_core_proxy_field_legacy',
-        'erp5_full_text_myisam_catalog',
+        'erp5_full_text_mroonga_catalog',
         'erp5_base',
         'erp5_pdm',
         'erp5_simulation',
diff --git a/product/ERP5Type/tests/testFunctionalAnonymousSelection.py b/product/ERP5Type/tests/testFunctionalAnonymousSelection.py
index 436f19a34e..8dd6675ec7 100644
--- a/product/ERP5Type/tests/testFunctionalAnonymousSelection.py
+++ b/product/ERP5Type/tests/testFunctionalAnonymousSelection.py
@@ -41,7 +41,7 @@ class TestAnonymousSelection(TestZeleniumCore):
         """
           Return the list of business templates.
         """
-        return ('erp5_core_proxy_field_legacy', 'erp5_full_text_myisam_catalog',
+        return ('erp5_core_proxy_field_legacy', 'erp5_full_text_mroonga_catalog',
                 'erp5_base', 'erp5_ui_test_core', 'erp5_ui_test', 'erp5_forge',
                 )
 
diff --git a/product/ERP5Type/tests/testFunctionalCore.py b/product/ERP5Type/tests/testFunctionalCore.py
index 7e78098f54..22b8c6238d 100644
--- a/product/ERP5Type/tests/testFunctionalCore.py
+++ b/product/ERP5Type/tests/testFunctionalCore.py
@@ -39,7 +39,7 @@ class TestZeleniumCore(ERP5TypeFunctionalTestCase):
         """
           Return the list of business templates.
         """
-        return ('erp5_core_proxy_field_legacy', 'erp5_full_text_myisam_catalog',
+        return ('erp5_core_proxy_field_legacy', 'erp5_full_text_mroonga_catalog',
                 'erp5_base', 'erp5_ui_test_core', 'erp5_ui_test', 'erp5_forge',
                 'erp5_dhtml_style', 'erp5_dhtml_ui_test',
                 'erp5_jquery', 'erp5_jquery_ui',
diff --git a/product/ERP5Type/tests/testFunctionalKM.py b/product/ERP5Type/tests/testFunctionalKM.py
index d01f2a162f..49140e6888 100644
--- a/product/ERP5Type/tests/testFunctionalKM.py
+++ b/product/ERP5Type/tests/testFunctionalKM.py
@@ -43,7 +43,7 @@ class TestZeleniumKM(ERP5TypeFunctionalTestCase):
         """
         # XXX This is a rough list, we should drop as much as we can, and
         # keep only minimal
-        return ('erp5_core_proxy_field_legacy', 'erp5_full_text_myisam_catalog',
+        return ('erp5_core_proxy_field_legacy', 'erp5_full_text_mroonga_catalog',
                 'erp5_base', 'erp5_ui_test_core', 'erp5_ui_test', 'erp5_forge',
                 'erp5_dhtml_style', 'erp5_dhtml_ui_test',
                 'erp5_jquery', 'erp5_jquery_ui',
diff --git a/product/ERP5Type/tests/testFunctionalStandaloneUserTutorial.py b/product/ERP5Type/tests/testFunctionalStandaloneUserTutorial.py
index 42c84fe845..736300a595 100644
--- a/product/ERP5Type/tests/testFunctionalStandaloneUserTutorial.py
+++ b/product/ERP5Type/tests/testFunctionalStandaloneUserTutorial.py
@@ -48,7 +48,7 @@ class TestZeleniumStandaloneUserTutorial(ERP5TypeFunctionalTestCase):
     """
       Return the list of business templates.
     """
-    return ('erp5_core_proxy_field_legacy', 'erp5_full_text_myisam_catalog',
+    return ('erp5_core_proxy_field_legacy', 'erp5_full_text_mroonga_catalog',
             'erp5_base', 'erp5_ui_test_core', 'erp5_forge',
             'erp5_dhtml_style',
             'erp5_jquery', 'erp5_jquery_ui',
diff --git a/product/ZMySQLDA/tests/testDeferredConnection.py b/product/ZMySQLDA/tests/testDeferredConnection.py
index 53207836a7..771d028210 100644
--- a/product/ZMySQLDA/tests/testDeferredConnection.py
+++ b/product/ZMySQLDA/tests/testDeferredConnection.py
@@ -65,7 +65,7 @@ class TestDeferredConnection(ERP5TypeTestCase):
   """
 
   def getBusinessTemplateList(self):
-    return 'erp5_full_text_myisam_catalog',
+    return 'erp5_full_text_mroonga_catalog',
 
   def getTitle(self):
     return "Deferred Connection"
diff --git a/product/ZSQLCatalog/Operator/ComparisonOperator.py b/product/ZSQLCatalog/Operator/ComparisonOperator.py
index f152ccea8d..40a8fcd432 100644
--- a/product/ZSQLCatalog/Operator/ComparisonOperator.py
+++ b/product/ZSQLCatalog/Operator/ComparisonOperator.py
@@ -33,6 +33,7 @@ from Products.ZSQLCatalog.SQLExpression import SQLExpression
 from Products.ZSQLCatalog.interfaces.operator import IOperator
 from zope.interface.verify import verifyClass
 from Products.ZSQLCatalog.SQLCatalog import list_type_list
+import re
 
 class ComparisonOperatorBase(OperatorBase):
   def asSQLExpression(self, column, value_list, only_group_columns):
@@ -105,13 +106,17 @@ class MatchComparisonOperator(MonovaluedComparisonOperator):
       This operator can emit a select expression, so it overrides
       asSQLExpression inseatd of just defining a render method.
     """
+    # No need to do full text search for an empty string.
+    if value_list == '':
+      column, value_list = self.render(column, value_list)
+      return SQLExpression(self, where_expression='%s %s %s' % (column, '=', value_list))
     match_string = self.where_expression_format_string % {
       'column': column,
       'value_list': self.renderValue(value_list),
     }
     select_dict = {}
     if not only_group_columns:
-      select_dict[column.replace('`', '').split('.')[-1]] = match_string
+      select_dict['%s__score__' % column.replace('`', '').rsplit('.', 1)[-1]] = match_string
     # Sort on this column uses relevance.
     # TODO: Add a way to allow sorting by raw column value.
     order_by_dict = {
@@ -127,6 +132,50 @@ class MatchComparisonOperator(MonovaluedComparisonOperator):
 
 verifyClass(IOperator, MatchComparisonOperator)
 
+class MroongaComparisonOperator(MatchComparisonOperator):
+  fulltext_boolean_splitter = re.compile(r'(\s|\(.+?\)|".+?")')
+  fulltext_boolean_detector = re.compile(r'(^[+-]|^.+\*$|^["(].+[")]$)')
+
+  def __init__(self, operator, force_boolean=False):
+    MatchComparisonOperator.__init__(self, operator, ' IN BOOLEAN MODE')
+    self.force_boolean = force_boolean
+
+  def renderValue(self, value_list):
+    """
+      Special Query renderer for MroongaFullText queries:
+      * by default 'AND' search by using '*D+' pragma.
+      * similarity search for non-boolean queries by using '*S"..."' operator.
+    """
+    if isinstance(value_list, list_type_list):
+      try:
+        value_list, = value_list
+      except ValueError:
+        raise ValueError, '%r: value_list must not contain more than one item. Got %r' % (self, value_list)
+
+    if self.force_boolean:
+      fulltext_query = '*D+ %s' % value_list
+      return self._renderValue(fulltext_query)
+    else:
+      match_query_list = []
+      match_boolean_query_list = []
+      for token in self.fulltext_boolean_splitter.split(value_list):
+        token = token.strip()
+        if not token:
+          continue
+        elif self.fulltext_boolean_detector.match(token):
+          match_boolean_query_list.append(token)
+        else:
+          match_query_list.append(token)
+      # Always use BOOLEAN MODE to combine similarity search and boolean search.
+      fulltext_query = '*D+'
+      if match_query_list:
+        fulltext_query += ' *S"%s"' % ' '.join(match_query_list)
+      if match_boolean_query_list:
+        fulltext_query += ' %s' % ' '.join(match_boolean_query_list)
+      return self._renderValue(fulltext_query)
+
+verifyClass(IOperator, MroongaComparisonOperator)
+
 class SphinxSEComparisonOperator(MonovaluedComparisonOperator):
   def __init__(self, operator, mode=''):
     MonovaluedComparisonOperator.__init__(self, operator, '')
@@ -173,9 +222,10 @@ operator_dict = {
   'match': MatchComparisonOperator('match'),
   'match_boolean': MatchComparisonOperator('match_boolean', mode=' IN BOOLEAN MODE'),
   'match_expansion': MatchComparisonOperator('match_expansion', mode=' WITH QUERY EXPANSION'),
+  'mroonga': MroongaComparisonOperator('mroonga'),
+  'mroonga_boolean': MroongaComparisonOperator('mroonga_boolean', force_boolean=True),
   'sphinxse': SphinxSEComparisonOperator('sphinxse'),
   'in': MultivaluedComparisonOperator('in'),
   'is': MonovaluedComparisonOperator('is'),
   'is not': MonovaluedComparisonOperator('is not', '!='),
 }
-
diff --git a/product/ZSQLCatalog/Query/RelatedQuery.py b/product/ZSQLCatalog/Query/RelatedQuery.py
index 859f65bcf7..1ddb4f44f4 100644
--- a/product/ZSQLCatalog/Query/RelatedQuery.py
+++ b/product/ZSQLCatalog/Query/RelatedQuery.py
@@ -62,12 +62,14 @@ class RelatedQuery(Query):
     self.table_alias_list = table_alias_list
 
   def _asSearchTextExpression(self, sql_catalog, column=None):
-    assert column is None
+    assert column in (None, '')
+    if column is None:
+      column = self.search_key.getColumn()
     join_condition = self.join_condition
     if join_condition is None:
       result = None
     else:
-      result = join_condition.asSearchTextExpression(sql_catalog, column=self.search_key.getColumn())
+      result = join_condition.asSearchTextExpression(sql_catalog, column=column)
     return False, result
 
   def asSQLExpression(self, sql_catalog, column_map, only_group_columns):
diff --git a/product/ZSQLCatalog/SQLExpression.py b/product/ZSQLCatalog/SQLExpression.py
index 6750c3dac0..128bbe6aae 100644
--- a/product/ZSQLCatalog/SQLExpression.py
+++ b/product/ZSQLCatalog/SQLExpression.py
@@ -334,6 +334,9 @@ class SQLExpression(object):
           if can_merge_sql_expression and alias in mergeable_set:
             # Custom conflict resolution
             column = '%s + %s' % (existing_value, column)
+          elif alias.endswith('__score__'):
+            # We only support the first full text score in select dict.
+            pass
           else:
             message = '%r is a known alias for column %r, can\'t alias it now to column %r' % (alias, existing_value, column)
             if DEBUG:
diff --git a/product/ZSQLCatalog/SearchKey/FullTextKey.py b/product/ZSQLCatalog/SearchKey/FullTextKey.py
index f01e84976c..fb868a48b1 100644
--- a/product/ZSQLCatalog/SearchKey/FullTextKey.py
+++ b/product/ZSQLCatalog/SearchKey/FullTextKey.py
@@ -28,29 +28,30 @@
 #
 ##############################################################################
 
-from SearchKey import SearchKey
+from DefaultKey import DefaultKey
 from Products.ZSQLCatalog.Query.SimpleQuery import SimpleQuery
-from Products.ZSQLCatalog.SearchText import parse
 from Products.ZSQLCatalog.interfaces.search_key import ISearchKey
+from Products.ZSQLCatalog.SearchText import dequote
 from zope.interface.verify import verifyClass
 import re
 
 FULLTEXT_BOOLEAN_DETECTOR = re.compile(r'.*((^|\s)[\+\-<>\(\~]|[\*\)](\s|$))')
 
-class FullTextKey(SearchKey):
+class FullTextKey(DefaultKey):
   """
     This SearchKey generates SQL fulltext comparisons.
   """
   default_comparison_operator = 'match'
   get_operator_from_value = False
 
-  def parseSearchText(self, value, is_column):
-    return parse(value, is_column)
-
   def dequoteParsedText(self):
     return False
 
   def _renderValueAsSearchText(self, value, operator):
+    # XXX:
+    # return value for 'a b' here is '(a b), but keyword search with
+    # '(a b)' means fulltext search with (a OR b), that is different
+    # from fulltext='a b' that means fulltext search with (a AND b).
     return '(%s)' % (value, )
 
   def _processSearchValue(self, search_value, logical_operator,
@@ -61,8 +62,8 @@ class FullTextKey(SearchKey):
       mode, make the operator for that value be 'match_boolean'.
     """
     operator_value_dict, logical_operator, parsed = \
-      SearchKey._processSearchValue(self, search_value, logical_operator,
-                                    comparison_operator)
+      super(FullTextKey, self)._processSearchValue(
+        search_value, logical_operator, comparison_operator)
     new_value_list = []
     append = new_value_list.append
     for value in operator_value_dict.pop('match', []):
@@ -77,6 +78,13 @@ class FullTextKey(SearchKey):
         operator_value_dict['match_boolean'].extend(new_value_list)
       else:
         operator_value_dict['match'] = new_value_list
+    # Dequote for non full-text queries.
+    for comparison_operator, value_list in operator_value_dict.iteritems():
+      if comparison_operator not in ('match', 'match_boolean'):
+        operator_value_dict[comparison_operator] = [
+          isinstance(value, basestring) and dequote(value) or value
+          for value in value_list
+        ]
     return operator_value_dict, logical_operator, parsed
 
   def _buildQuery(self, operator_value_dict, logical_operator, parsed, group):
@@ -88,10 +96,20 @@ class FullTextKey(SearchKey):
     column = self.getColumn()
     query_list = []
     append = query_list.append
-    for comparison_operator, value_list in operator_value_dict.iteritems():
+    for comparison_operator in ('match', 'match_boolean'):
+      value_list = operator_value_dict.pop(comparison_operator, [])
+      if not value_list:
+        continue
+      # XXX:
+      # In MySQL FTS, no operator implies OR so that we should not merge
+      # AND queries into one...
       append(SimpleQuery(search_key=self,
                          comparison_operator=comparison_operator,
                          group=group, **{column: ' '.join(value_list)}))
+    # Other comparison operators are handled by the super class.
+    if operator_value_dict:
+      query_list += super(FullTextKey, self)._buildQuery(
+        operator_value_dict, logical_operator, parsed, group)
     return query_list
 
 verifyClass(ISearchKey, FullTextKey)
diff --git a/product/ZSQLCatalog/SearchKey/MroongaBooleanFullTextKey.py b/product/ZSQLCatalog/SearchKey/MroongaBooleanFullTextKey.py
new file mode 100644
index 0000000000..d6977a4371
--- /dev/null
+++ b/product/ZSQLCatalog/SearchKey/MroongaBooleanFullTextKey.py
@@ -0,0 +1,40 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2014 Nexedi SA and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+##############################################################################
+
+from MroongaFullTextKey import MroongaFullTextKey
+from Products.ZSQLCatalog.interfaces.search_key import ISearchKey
+from zope.interface.verify import verifyClass
+
+class MroongaBooleanFullTextKey(MroongaFullTextKey):
+  """
+    This SearchKey generates SQL fulltext comparisons for Mroonga whose
+    default comparison operator is mroonga_boolean.
+  """
+  default_comparison_operator = 'mroonga_boolean'
+
+verifyClass(ISearchKey, MroongaBooleanFullTextKey)
diff --git a/product/ZSQLCatalog/SearchKey/MroongaFullTextKey.py b/product/ZSQLCatalog/SearchKey/MroongaFullTextKey.py
new file mode 100644
index 0000000000..e941790a0b
--- /dev/null
+++ b/product/ZSQLCatalog/SearchKey/MroongaFullTextKey.py
@@ -0,0 +1,92 @@
+# -*- coding: utf-8 -*-
+##############################################################################
+#
+# Copyright (c) 2014-2006 Nexedi SA and Contributors. All Rights Reserved.
+#
+# WARNING: This program as such is intended to be used by professional
+# programmers who take the whole responsibility of assessing all potential
+# consequences resulting from its eventual inadequacies and bugs
+# End users who are looking for a ready-to-use solution with commercial
+# guarantees and support are strongly adviced to contract a Free Software
+# Service Company
+#
+# This program is Free Software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+#
+##############################################################################
+
+from DefaultKey import DefaultKey
+from Products.ZSQLCatalog.Query.SimpleQuery import SimpleQuery
+from Products.ZSQLCatalog.interfaces.search_key import ISearchKey
+from Products.ZSQLCatalog.SearchText import dequote
+from zope.interface.verify import verifyClass
+
+class MroongaFullTextKey(DefaultKey):
+  default_comparison_operator = 'mroonga'
+
+  def dequoteParsedText(self):
+    return False
+
+  def _renderValueAsSearchText(self, value, operator):
+    # XXX:
+    # return value for 'a b' here is '(a b), but keyword search with
+    # '(a b)' means fulltext search with (a OR b), that is different
+    # from fulltext='a b' that means fulltext search with (a AND b).
+    return '(%s)' % (value, )
+
+  def _processSearchValue(self, search_value, logical_operator,
+                          comparison_operator):
+    operator_value_dict, logical_operator, parsed = \
+      super(MroongaFullTextKey, self)._processSearchValue(
+        search_value, logical_operator, comparison_operator)
+    # Dequote for non full-text queries.
+    for comparison_operator, value_list in operator_value_dict.iteritems():
+      if comparison_operator not in ('mroonga', 'mroonga_boolean'):
+        operator_value_dict[comparison_operator] = [
+          isinstance(value, basestring) and dequote(value) or value
+          for value in value_list
+        ]
+    return operator_value_dict, logical_operator, parsed
+
+  def _buildQuery(self, operator_value_dict, logical_operator, parsed, group):
+    """
+      Special Query builder for FullText queries: merge all values having the
+      same operator into just one query, to save SQL server from the burden to
+      do multiple fulltext lookups when one would suit the purpose.
+    """
+    column = self.getColumn()
+    query_list = []
+    append = query_list.append
+    for comparison_operator in ('mroonga', 'mroonga_boolean'):
+      value_list = operator_value_dict.pop(comparison_operator, [])
+      if not value_list:
+        continue
+      if logical_operator == 'and':
+        joined_value = ' '.join(value_list)
+        append(SimpleQuery(search_key=self,
+                           comparison_operator=comparison_operator,
+                           group=group, **{column:joined_value}))
+      else:
+        # TODO : We can join to one query like 'aaa OR (bbb ccc) OR "ddd eee"'.
+        for value in value_list:
+          append(SimpleQuery(search_key=self,
+                             comparison_operator=comparison_operator,
+                             group=group, **{column:value}))
+    # Other comparison operators are handled by the super class.
+    if operator_value_dict:
+      query_list += super(MroongaFullTextKey, self)._buildQuery(
+        operator_value_dict, logical_operator, parsed, group)
+    return query_list
+
+verifyClass(ISearchKey, MroongaFullTextKey)
diff --git a/product/ZSQLCatalog/tests/testSQLCatalog.py b/product/ZSQLCatalog/tests/testSQLCatalog.py
index 0837aa9949..5ad28f4440 100644
--- a/product/ZSQLCatalog/tests/testSQLCatalog.py
+++ b/product/ZSQLCatalog/tests/testSQLCatalog.py
@@ -168,8 +168,10 @@ class DummyCatalog(SQLCatalog):
 
   sql_catalog_keyword_search_keys = ('keyword', )
   sql_catalog_datetime_search_keys = ('date', )
-  sql_catalog_full_text_search_keys = ('fulltext', )
+  sql_catalog_full_text_search_keys = ('old_fulltext', )
   sql_catalog_scriptable_keys = ('scriptable_keyword | scriptableKeyScript', )
+  sql_catalog_search_keys = ('fulltext | MroongaFullTextKey',
+                             'fulltext_boolean | MroongaBooleanFullTextKey',)
 
   def getColumnMap(self):
     """
@@ -180,7 +182,9 @@ class DummyCatalog(SQLCatalog):
       'default': ['foo', ],
       'keyword': ['foo', ],
       'date': ['foo', ],
+      'old_fulltext': ['foo', ],
       'fulltext': ['foo', ],
+      'fulltext_boolean': ['foo', ],
       'other_uid': ['bar', ],
       'ambiguous_mapping': ['foo', 'bar'],
     }
@@ -495,8 +499,8 @@ class TestSQLCatalog(ERP5TypeTestCase):
     # This example introduces impossible-to-merge search text criterion, which
     # is allowed as long as
     reference_query = ReferenceQuery(
-        ReferenceQuery(ReferenceQuery(operator='match', fulltext='a'),
-        ReferenceQuery(ReferenceQuery(operator='match', fulltext='b'),
+        ReferenceQuery(ReferenceQuery(operator='mroonga', fulltext='a'),
+        ReferenceQuery(ReferenceQuery(operator='mroonga', fulltext='b'),
       operator='not'), operator='and'), operator='and')
     self.catalog(reference_query, {'fulltext': 'a NOT b'})
     # The same, with an order by, must raise
@@ -504,15 +508,15 @@ class TestSQLCatalog(ERP5TypeTestCase):
       {'fulltext': 'a NOT b', 'order_by_list': [('fulltext', ), ]},
       check_search_text=False)
     # If one want to sort on, he must use the equivalent FullText syntax:
-    self.catalog(ReferenceQuery(ReferenceQuery(operator='match_boolean',
+    self.catalog(ReferenceQuery(ReferenceQuery(operator='mroonga',
       fulltext=MatchList(['a -b', '-b a'])), operator='and'),
       {'fulltext': 'a -b', 'order_by_list': [('fulltext', ), ]},
       check_search_text=False)
-    self.catalog(ReferenceQuery(ReferenceQuery(ReferenceQuery(operator='match', fulltext='a'),
-                                               ReferenceQuery(ReferenceQuery(operator='match', fulltext='b'), operator='not'), operator='or'), operator='and'),
+    self.catalog(ReferenceQuery(ReferenceQuery(ReferenceQuery(operator='mroonga', fulltext='a'),
+                                               ReferenceQuery(ReferenceQuery(operator='mroonga', fulltext='b'), operator='not'), operator='or'), operator='and'),
                  {'fulltext': 'a OR NOT b'})
-    self.catalog(ReferenceQuery(ReferenceQuery(ReferenceQuery(operator='match', fulltext='a'),
-                                               ReferenceQuery(ReferenceQuery(operator='match', fulltext='b'), operator='not'), operator='and'), operator='and'),
+    self.catalog(ReferenceQuery(ReferenceQuery(ReferenceQuery(operator='mroonga', fulltext='a'),
+                                               ReferenceQuery(ReferenceQuery(operator='mroonga', fulltext='b'), operator='not'), operator='and'), operator='and'),
                  {'fulltext': 'a AND NOT b'})
 
   def test_006_testRelatedKey_with_multiple_join(self):
@@ -540,7 +544,7 @@ class TestSQLCatalog(ERP5TypeTestCase):
                  check_search_text=False)
 
   def test_009_testFullTextKey(self):
-    self.catalog(ReferenceQuery(ReferenceQuery(operator='match', fulltext='a'), operator='and'),
+    self.catalog(ReferenceQuery(ReferenceQuery(operator='mroonga', fulltext='a'), operator='and'),
                  {'fulltext': 'a'})
 
   def test_isAdvancedSearchText(self):
@@ -551,15 +555,20 @@ class TestSQLCatalog(ERP5TypeTestCase):
 
   def test_FullTextSearchMergesQueries(self):
     """
+      XXX this test is for old FullTextKey, not for MroongaFullTextKey
+      that merges queries only when logical_operator is 'and'. Also
+      _renderValueAsSearchText it not perfect so that we cannot use the
+      test codes below for mroonga search key.
+
       FullText criterion on the same scope must be merged into one query.
       Logical operator is ignored, as fulltext operators are expected instead.
     """
-    self.catalog(ReferenceQuery(ReferenceQuery(operator='match', fulltext='a b'), operator='and'),
-                 {'fulltext': 'a AND b'})
-    self.catalog(ReferenceQuery(ReferenceQuery(operator='match', fulltext='a b'), operator='and'),
-                 {'fulltext': 'a OR b'})
-    self.catalog(ReferenceQuery(ReferenceQuery(ReferenceQuery(operator='match', fulltext='a b'), operator='not'), operator='and'),
-                 {'fulltext': 'NOT (a b)'})
+    self.catalog(ReferenceQuery(ReferenceQuery(operator='match', old_fulltext='a b'), operator='and'),
+                 {'old_fulltext': 'a AND b'})
+    self.catalog(ReferenceQuery(ReferenceQuery(operator='match', old_fulltext='a b'), operator='and'),
+                 {'old_fulltext': 'a OR b'})
+    self.catalog(ReferenceQuery(ReferenceQuery(ReferenceQuery(operator='match', old_fulltext='a b'), operator='not'), operator='and'),
+                 {'old_fulltext': 'NOT (a b)'})
 
   def test_NoneValueToSimpleQuery(self):
     """
@@ -583,60 +592,69 @@ class TestSQLCatalog(ERP5TypeTestCase):
 
   def test_FullTextBooleanMode(self):
     """
+      XXX this test is for old FullTextKey, not for MroongaFullTextKey
+      that does no automatic mode switch.
+
       Fulltext searches must switch automatically to boolean mode if boolean
       operators are found in search value.
     """
     self.catalog(ReferenceQuery(ReferenceQuery(operator='match_boolean',
-                                fulltext=MatchList(['a*'])), operator='and'),
-                 {'fulltext': 'a*'})
+                                old_fulltext=MatchList(['a*'])), operator='and'),
+                 {'old_fulltext': 'a*'})
 
     self.catalog(ReferenceQuery(ReferenceQuery(operator='match_boolean',
-                                fulltext=MatchList(['a* b'])), operator='and'),
-                 {'fulltext': 'a* b'})
+                                old_fulltext=MatchList(['a* b'])), operator='and'),
+                 {'old_fulltext': 'a* b'})
 
-    self.catalog(ReferenceQuery(ReferenceQuery(operator='match', fulltext='*a'),
+    self.catalog(ReferenceQuery(ReferenceQuery(operator='match', old_fulltext='*a'),
                                 operator='and'),
-                 {'fulltext': '*a'})
+                 {'old_fulltext': '*a'})
 
-    self.catalog(ReferenceQuery(ReferenceQuery(operator='match', fulltext='a'),
+    self.catalog(ReferenceQuery(ReferenceQuery(operator='match', old_fulltext='a'),
                                 operator='and'),
-                 {'fulltext': 'a'})
+                 {'old_fulltext': 'a'})
 
-    self.catalog(ReferenceQuery(ReferenceQuery(operator='match', fulltext='a+b'), operator='and'),
-                 {'fulltext': 'a+b'})
+    self.catalog(ReferenceQuery(ReferenceQuery(operator='match', old_fulltext='a+b'), operator='and'),
+                 {'old_fulltext': 'a+b'})
 
     self.catalog(ReferenceQuery(ReferenceQuery(operator='match_boolean',
-      fulltext=MatchList(['a +b', '+b a'])), operator='and'),
-                 {'fulltext': 'a +b'}, check_search_text=False)
+      old_fulltext=MatchList(['a +b', '+b a'])), operator='and'),
+                 {'old_fulltext': 'a +b'}, check_search_text=False)
 
     self.catalog(ReferenceQuery(ReferenceQuery(
         ReferenceQuery(operator='=', uid='foo'),
         ReferenceQuery(operator='match_boolean',
-          fulltext=MatchList(['+a b', 'b +a'])),
-      operator='and'), operator='and'), {'fulltext': '+a b uid:foo'})
+          old_fulltext=MatchList(['+a b', 'b +a'])),
+      operator='and'), operator='and'), {'old_fulltext': '+a b uid:foo'})
 
   def test_FullTextQuoting(self):
+    """
+      XXX this test is for old FullTextKey, not for MroongaFullTextKey
+      that merges queries only when logical_operator is 'and'. Also
+      _renderValueAsSearchText it not perfect so that we cannot use the
+      test codes below for mroonga search key.
+    """
     # Quotes must be kept
     self.catalog(ReferenceQuery(ReferenceQuery(operator='match',
-      fulltext='"a"'), operator='and'),
-      {'fulltext': '"a"'})
+      old_fulltext='"a"'), operator='and'),
+      {'old_fulltext': '"a"'})
     self.catalog(ReferenceQuery(ReferenceQuery(operator='match',
-      fulltext='"foo" bar "baz"'), operator='and'),
-      {'fulltext': '"foo" bar "baz"'})
+      old_fulltext='"foo" bar "baz"'), operator='and'),
+      {'old_fulltext': '"foo" bar "baz"'})
     # ...But each column must follow rules defined in configured SearchKey for
     # that column (in this case: quotes must be stripped).
     ref_query = ReferenceQuery(ReferenceQuery(ReferenceQuery(operator='match',
-      fulltext='"foo" bar'), ReferenceQuery(operator='=',
+      old_fulltext='"foo" bar'), ReferenceQuery(operator='=',
       default='hoge \"pon'), operator='and'), operator='and')
     self.catalog(ref_query, {
-      'keyword': 'default:"hoge \\"pon" AND fulltext:("foo" bar)'})
+      'keyword': 'default:"hoge \\"pon" AND old_fulltext:("foo" AND bar)'})
     self.catalog(ref_query, {
-      'fulltext': '"foo" bar AND default:"hoge \\"pon"'})
+      'old_fulltext': '"foo" bar AND default:"hoge \\"pon"'})
     ref_query = ReferenceQuery(ReferenceQuery(ReferenceQuery(operator='match',
-      fulltext='"\\"foo\\" bar"'), ReferenceQuery(operator='=',
+      old_fulltext='"\\"foo\\" bar"'), ReferenceQuery(operator='=',
       default='hoge \"pon'), operator='and'), operator='and')
     self.catalog(ref_query, {
-      'keyword': 'default:"hoge \\"pon" AND fulltext:"\\"foo\\" bar"'})
+      'keyword': 'default:"hoge \\"pon" AND old_fulltext:"\\"foo\\" bar"'})
 
   def test_DefaultKeyTextRendering(self):
     self.catalog(ReferenceQuery(ReferenceQuery(operator='like', default='a% b'), operator='and'),
-- 
2.30.9