feat: Global Search with Priorities
This commit is contained in:
parent
7d2e664b74
commit
9d3b361da4
13 changed files with 223 additions and 14 deletions
0
frappe/desk/doctype/global_search_doctype/__init__.py
Normal file
0
frappe/desk/doctype/global_search_doctype/__init__.py
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
{
|
||||
"creation": "2019-09-13 21:33:55.551941",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"document_type"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "document_type",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Document Type",
|
||||
"options": "DocType"
|
||||
}
|
||||
],
|
||||
"istable": 1,
|
||||
"modified": "2019-09-18 17:59:44.354052",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Global Search DocType",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
# import frappe
|
||||
from frappe.model.document import Document
|
||||
|
||||
class GlobalSearchDocType(Document):
|
||||
pass
|
||||
0
frappe/desk/doctype/global_search_settings/__init__.py
Normal file
0
frappe/desk/doctype/global_search_settings/__init__.py
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
// Copyright (c) 2019, Frappe Technologies and contributors
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on('Global Search Settings', {
|
||||
refresh: function(frm) {
|
||||
frm.add_custom_button(__("Reset"), function () {
|
||||
frappe.call({
|
||||
method: "frappe.desk.doctype.global_search_settings.global_search_settings.reset_global_search_settings_doctypes",
|
||||
callback: function() {
|
||||
frappe.show_alert({
|
||||
message: __("Global Search Document Types Reset."),
|
||||
indicator: "green"
|
||||
});
|
||||
frm.refresh();
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"creation": "2019-09-03 16:08:21.333698",
|
||||
"doctype": "DocType",
|
||||
"editable_grid": 1,
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"allowed_in_global_search"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
"fieldname": "allowed_in_global_search",
|
||||
"fieldtype": "Table",
|
||||
"label": "Document Types",
|
||||
"options": "Global Search DocType"
|
||||
}
|
||||
],
|
||||
"issingle": 1,
|
||||
"modified": "2019-09-18 18:00:17.388486",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Desk",
|
||||
"name": "Global Search Settings",
|
||||
"owner": "Administrator",
|
||||
"permissions": [
|
||||
{
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"role": "System Manager",
|
||||
"share": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"quick_entry": 1,
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC",
|
||||
"track_changes": 1
|
||||
}
|
||||
|
|
@ -0,0 +1,50 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies and contributors
|
||||
# For license information, please see license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.model.document import Document
|
||||
from frappe import _
|
||||
|
||||
class GlobalSearchSettings(Document):
|
||||
|
||||
def validate(self):
|
||||
core_dts = []
|
||||
for dt in self.allowed_in_global_search:
|
||||
if frappe.get_meta(dt.document_type).module == "Core":
|
||||
core_dts.append(dt.document_type)
|
||||
|
||||
if core_dts:
|
||||
core_dts = (", ".join([frappe.bold(dt) for dt in core_dts]))
|
||||
frappe.throw(_("Core Modules {0} cannot be searched in Global Search.").format(core_dts))
|
||||
|
||||
def get_doctypes_for_global_search():
|
||||
doctypes = frappe.get_list("Global Search DocType", fields=["document_type"], order_by="idx ASC")
|
||||
if not doctypes:
|
||||
return []
|
||||
|
||||
return [d.document_type for d in doctypes]
|
||||
|
||||
@frappe.whitelist()
|
||||
def reset_global_search_settings_doctypes():
|
||||
update_global_search_doctypes()
|
||||
|
||||
def update_global_search_doctypes():
|
||||
global_search_doctypes = frappe.get_hooks("global_search_doctypes")
|
||||
allowed_in_global_search = []
|
||||
|
||||
for dt in global_search_doctypes:
|
||||
if dt.get("index"):
|
||||
allowed_in_global_search.insert(dt.get("index")-1, dt.get("doctype"))
|
||||
continue
|
||||
|
||||
allowed_in_global_search.append(dt.get("doctype"))
|
||||
|
||||
global_search_settings = frappe.get_single("Global Search Settings")
|
||||
global_search_settings.allowed_in_global_search = []
|
||||
for dt in allowed_in_global_search:
|
||||
global_search_settings.append("allowed_in_global_search", {
|
||||
"document_type": dt
|
||||
})
|
||||
global_search_settings.save(ignore_permissions=True)
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Copyright (c) 2019, Frappe Technologies and Contributors
|
||||
# See license.txt
|
||||
from __future__ import unicode_literals
|
||||
|
||||
# import frappe
|
||||
import unittest
|
||||
|
||||
class TestGlobalSearchSettings(unittest.TestCase):
|
||||
pass
|
||||
|
|
@ -4,11 +4,12 @@
|
|||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
|
||||
from frappe import _
|
||||
from frappe.desk.doctype.global_search_settings.global_search_settings import update_global_search_doctypes
|
||||
|
||||
def install():
|
||||
update_genders_and_salutations()
|
||||
update_global_search_doctypes()
|
||||
|
||||
@frappe.whitelist()
|
||||
def update_genders_and_salutations():
|
||||
|
|
|
|||
|
|
@ -300,3 +300,18 @@ user_privacy_documents = [
|
|||
},
|
||||
|
||||
]
|
||||
|
||||
global_search_doctypes = [
|
||||
{"doctype": "Contact"},
|
||||
{"doctype": "Address"},
|
||||
{"doctype": "Event"},
|
||||
{"doctype": "Note"},
|
||||
{"doctype": "Google Calendar"},
|
||||
{"doctype": "Auto Repeat"},
|
||||
{"doctype": "ToDo"},
|
||||
{"doctype": "Country"},
|
||||
{"doctype": "Currency"},
|
||||
{"doctype": "Newsletter"},
|
||||
{"doctype": "Contact Us Settings"},
|
||||
{"doctype": "Google Contacts"}
|
||||
]
|
||||
7
frappe/patches/v12_0/update_global_search.py
Normal file
7
frappe/patches/v12_0/update_global_search.py
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
import frappe
|
||||
from frappe.desk.page.setup_wizard.install_fixtures import update_global_search_doctypes
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("desk", "doctype", "global_search_doctype")
|
||||
frappe.reload_doc("desk", "doctype", "global_search_settings")
|
||||
update_global_search_doctypes()
|
||||
|
|
@ -7,10 +7,13 @@ import frappe
|
|||
|
||||
from frappe.utils import global_search
|
||||
from frappe.test_runner import make_test_objects
|
||||
from frappe.desk.page.setup_wizard.install_fixtures import update_global_search_doctypes
|
||||
|
||||
import frappe.utils
|
||||
|
||||
class TestGlobalSearch(unittest.TestCase):
|
||||
def setUp(self):
|
||||
update_global_search_doctypes()
|
||||
global_search.setup_global_search_table()
|
||||
self.assertTrue('__global_search' in frappe.db.get_tables())
|
||||
doctype = "Event"
|
||||
|
|
|
|||
|
|
@ -419,32 +419,48 @@ def search(text, start=0, limit=20, doctype=""):
|
|||
:param limit: number of results to return, default 20
|
||||
:return: Array of result objects
|
||||
"""
|
||||
from frappe.desk.doctype.global_search_settings.global_search_settings import get_doctypes_for_global_search
|
||||
|
||||
results = []
|
||||
texts = text.split('&')
|
||||
texts = [t.strip() for t in text.split('&')]
|
||||
priorities = get_doctypes_for_global_search()
|
||||
allowed_doctypes = ",".join(["'{0}'".format(dt) for dt in priorities])
|
||||
for text in texts:
|
||||
mariadb_conditions = ''
|
||||
postgres_conditions = ''
|
||||
|
||||
if doctype:
|
||||
mariadb_conditions = postgres_conditions = '`doctype` = {} AND '.format(frappe.db.escape(doctype))
|
||||
|
||||
mariadb_conditions += 'MATCH(`content`) AGAINST ({} IN BOOLEAN MODE)'.format(frappe.db.escape('+' + text + '*'))
|
||||
postgres_conditions += 'TO_TSVECTOR("content") @@ PLAINTO_TSQUERY({})'.format(frappe.db.escape(text))
|
||||
mariadb_text = frappe.db.escape('+' + text + '*')
|
||||
|
||||
common_query = '''SELECT `doctype`, `name`, `content`
|
||||
FROM `__global_search`
|
||||
WHERE {conditions}
|
||||
LIMIT {limit} OFFSET {start}'''
|
||||
mariadb_fields = '`doctype`, `name`, `content`, MATCH (`content`) AGAINST ({} IN BOOLEAN MODE) AS rank'.format(mariadb_text)
|
||||
postgres_fields = '`doctype`, `name`, `content`, TO_TSVECTOR("content") @@ PLAINTO_TSQUERY({}) AS rank'.format(frappe.db.escape(text))
|
||||
|
||||
if allowed_doctypes:
|
||||
mariadb_conditions += '`doctype` IN ({})'.format(allowed_doctypes)
|
||||
postgres_conditions += '`doctype` IN ({})'.format(allowed_doctypes)
|
||||
|
||||
common_query = """
|
||||
SELECT {fields}
|
||||
FROM `__global_search`
|
||||
WHERE {conditions}
|
||||
ORDER BY rank DESC
|
||||
LIMIT {limit}
|
||||
OFFSET {start}
|
||||
"""
|
||||
|
||||
result = frappe.db.multisql({
|
||||
'mariadb': common_query.format(conditions=mariadb_conditions, limit=limit, start=start),
|
||||
'postgres': common_query.format(conditions=postgres_conditions, limit=limit, start=start)
|
||||
'mariadb': common_query.format(fields=mariadb_fields, conditions=mariadb_conditions, limit=limit, start=start),
|
||||
'postgres': common_query.format(fields=postgres_fields, conditions=postgres_conditions, limit=limit, start=start)
|
||||
}, as_dict=True)
|
||||
|
||||
tmp_result=[]
|
||||
for i in result:
|
||||
if i in results or not results:
|
||||
tmp_result.append(i)
|
||||
results += tmp_result
|
||||
if i.rank > 0.0:
|
||||
if i in results or not results:
|
||||
tmp_result.extend([i])
|
||||
results.extend(tmp_result)
|
||||
|
||||
for r in results:
|
||||
try:
|
||||
|
|
@ -453,7 +469,17 @@ def search(text, start=0, limit=20, doctype=""):
|
|||
except Exception:
|
||||
frappe.clear_messages()
|
||||
|
||||
return results
|
||||
sorted_results = []
|
||||
|
||||
for priority in priorities:
|
||||
tmp_result = []
|
||||
for r in results:
|
||||
if r.doctype == priority:
|
||||
tmp_result.extend([r])
|
||||
|
||||
sorted_results.extend(tmp_result)
|
||||
|
||||
return sorted_results
|
||||
|
||||
|
||||
@frappe.whitelist(allow_guest=True)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue