Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

199 строки
5.8 KiB

  1. # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
  2. # License: GNU General Public License v3. See license.txt
  3. from __future__ import unicode_literals
  4. import frappe
  5. import re
  6. from frappe.utils import cint, strip_html_tags
  7. def setup_global_search_table():
  8. '''Creates __global_seach table'''
  9. if not '__global_search' in frappe.db.get_tables():
  10. frappe.db.sql('''create table __global_search(
  11. doctype varchar(100),
  12. name varchar(140),
  13. title varchar(140),
  14. content text,
  15. fulltext(content),
  16. route varchar(140),
  17. published int(1) not null default 0,
  18. unique (doctype, name))
  19. COLLATE=utf8mb4_unicode_ci
  20. ENGINE=MyISAM
  21. CHARACTER SET=utf8mb4''')
  22. def reset():
  23. '''Deletes all data in __global_search'''
  24. frappe.db.sql('delete from __global_search')
  25. def get_doctypes_with_global_search():
  26. '''Return doctypes with global search fields'''
  27. def _get():
  28. global_search_doctypes = []
  29. for d in frappe.get_all('DocType', 'name, module'):
  30. meta = frappe.get_meta(d.name)
  31. if len(meta.get_global_search_fields()) > 0:
  32. global_search_doctypes.append(d)
  33. installed_apps = frappe.get_installed_apps()
  34. doctypes = [d.name for d in global_search_doctypes
  35. if frappe.local.module_app[frappe.scrub(d.module)] in installed_apps]
  36. return doctypes
  37. return frappe.cache().get_value('doctypes_with_global_search', _get)
  38. def update_global_search(doc):
  39. '''Add values marked with `in_global_search` to
  40. `frappe.flags.update_global_search` from given doc
  41. :param doc: Document to be added to global search'''
  42. if cint(doc.meta.istable) == 1 and frappe.db.exists("DocType", doc.parenttype):
  43. d = frappe.get_doc(doc.parenttype, doc.parent)
  44. update_global_search(d)
  45. return
  46. if doc.docstatus > 1:
  47. return
  48. if frappe.flags.update_global_search==None:
  49. frappe.flags.update_global_search = []
  50. content = []
  51. for field in doc.meta.get_global_search_fields():
  52. if doc.get(field.fieldname):
  53. if getattr(field, 'fieldtype', None) == "Table":
  54. # Get children
  55. for d in doc.get(field.fieldname):
  56. if d.parent == doc.name:
  57. for field in d.meta.get_global_search_fields():
  58. if d.get(field.fieldname):
  59. content.append(get_field_value(d, field))
  60. else:
  61. content.append(get_field_value(doc, field))
  62. if content:
  63. published = 0
  64. if hasattr(doc, 'is_website_published') and doc.meta.allow_guest_to_view:
  65. published = 1 if doc.is_website_published() else 0
  66. frappe.flags.update_global_search.append(
  67. dict(doctype=doc.doctype, name=doc.name, content=' ||| '.join(content or ''),
  68. published=published, title=doc.get_title(), route=doc.get('route')))
  69. def get_field_value(doc, field):
  70. '''Prepare field from raw data'''
  71. from HTMLParser import HTMLParser
  72. value = doc.get(field.fieldname)
  73. if(getattr(field, 'fieldtype', None) in ["Text", "Text Editor"]):
  74. h = HTMLParser()
  75. value = h.unescape(value)
  76. value = (re.subn(r'<[\s]*(script|style).*?</\1>(?s)', '', unicode(value))[0])
  77. value = ' '.join(value.split())
  78. return field.label + " : " + strip_html_tags(unicode(value))
  79. def sync_global_search():
  80. '''Add values from `frappe.flags.update_global_search` to __global_search.
  81. This is called internally at the end of the request.'''
  82. for value in frappe.flags.update_global_search:
  83. frappe.db.sql('''
  84. insert into __global_search
  85. (doctype, name, content, published, title, route)
  86. values
  87. (%(doctype)s, %(name)s, %(content)s, %(published)s, %(title)s, %(route)s)
  88. on duplicate key update
  89. content = %(content)s''', value)
  90. frappe.flags.update_global_search = []
  91. def rebuild_for_doctype(doctype):
  92. '''Rebuild entries of doctype's documents in __global_search on change of
  93. searchable fields
  94. :param doctype: Doctype '''
  95. frappe.flags.update_global_search = []
  96. frappe.db.sql('''
  97. delete
  98. from __global_search
  99. where
  100. doctype = %s''', doctype, as_dict=True)
  101. for d in frappe.get_all(doctype):
  102. update_global_search(frappe.get_doc(doctype, d.name))
  103. sync_global_search()
  104. def delete_for_document(doc):
  105. '''Delete the __global_search entry of a document that has
  106. been deleted
  107. :param doc: Deleted document'''
  108. frappe.db.sql('''
  109. delete
  110. from __global_search
  111. where
  112. doctype = %s and
  113. name = %s''', (doc.doctype, doc.name), as_dict=True)
  114. @frappe.whitelist()
  115. def search(text, start=0, limit=20, doctype=""):
  116. '''Search for given text in __global_search
  117. :param text: phrase to be searched
  118. :param start: start results at, default 0
  119. :param limit: number of results to return, default 20
  120. :return: Array of result objects'''
  121. text = "+" + text + "*"
  122. if not doctype:
  123. results = frappe.db.sql('''
  124. select
  125. doctype, name, content
  126. from
  127. __global_search
  128. where
  129. match(content) against (%s IN BOOLEAN MODE)
  130. limit {start}, {limit}'''.format(start=start, limit=limit), text+"*", as_dict=True)
  131. else:
  132. results = frappe.db.sql('''
  133. select
  134. doctype, name, content
  135. from
  136. __global_search
  137. where
  138. doctype = %s AND
  139. match(content) against (%s IN BOOLEAN MODE)
  140. limit {start}, {limit}'''.format(start=start, limit=limit), (doctype, text), as_dict=True)
  141. for r in results:
  142. if frappe.get_meta(r.doctype).image_field:
  143. doc = frappe.get_doc(r.doctype, r.name)
  144. r.image = doc.get(doc.meta.image_field)
  145. return results
  146. @frappe.whitelist(allow_guest=True)
  147. def web_search(text, start=0, limit=20):
  148. '''Search for given text in __global_search where published = 1
  149. :param text: phrase to be searched
  150. :param start: start results at, default 0
  151. :param limit: number of results to return, default 20
  152. :return: Array of result objects'''
  153. text = "+" + text + "*"
  154. results = frappe.db.sql('''
  155. select
  156. doctype, name, content, title, route
  157. from
  158. __global_search
  159. where
  160. published = 1 and
  161. match(content) against (%s IN BOOLEAN MODE)
  162. limit {start}, {limit}'''.format(start=start, limit=limit),
  163. text, as_dict=True)
  164. return results