text: # Copyright (c) 2013, Web Notes Technologies Pvt. Ltd. and Contributors
  # MIT License. See license.txt
  
  # Search
  from __future__ import unicode_literals
  import frappe
  import frappe.widgets.reportview
  from frappe.utils import cstr, unique
  
  # this is called by the Link Field
  @frappe.whitelist()
  def search_link(doctype, txt, query=None, filters=None, page_len=20, searchfield=None):
  	search_widget(doctype, txt, query, searchfield=searchfield, page_len=page_len, filters=filters)
  	frappe.response['results'] = build_for_autosuggest(frappe.response["values"])
  	del frappe.response["values"]
  
  # this is called by the search box
  @frappe.whitelist()
  def search_widget(doctype, txt, query=None, searchfield=None, start=0,
  	page_len=50, filters=None):
  	if isinstance(filters, basestring):
  		import json
  		filters = json.loads(filters)
  
  	meta = frappe.get_meta(doctype)
  
  	if not searchfield:
  		searchfield = "name"
  
  	standard_queries = frappe.get_hooks().standard_queries or []
  	if standard_queries:
  		standard_queries = dict([v.split(":") for v in standard_queries])
  
  	if query and query.split()[0].lower()!="select":
  		# by method
  		frappe.response["values"] = frappe.get_attr(query)(doctype, txt,
  			searchfield, start, page_len, filters)
  	elif not query and doctype in standard_queries:
  		# from standard queries
  		search_widget(doctype, txt, standard_queries[doctype],
  			searchfield, start, page_len, filters)
  	else:
  		if query:
  			# custom query
  			frappe.response["values"] = frappe.db.sql(scrub_custom_query(query,
  				searchfield, txt))
  		else:
  			if isinstance(filters, dict):
  				filters_items = filters.items()
  				filters = []
  				for f in filters_items:
  					if isinstance(f[1], (list, tuple)):
  						filters.append([doctype, f[0], f[1][0], f[1][1]])
  					else:
  						filters.append([doctype, f[0], "=", f[1]])
  
  			if filters==None:
  				filters = []
  			or_filters = []
  
  			# build from doctype
  			if txt:
  				if meta.search_fields:
  					for f in meta.get_search_fields():
  						or_filters.append([doctype, f.strip(), "like", "%{0}%".format(txt)])
  				else:
  					filters.append([doctype, searchfield or "name", "like", "%{0}%".format(txt)])
  
  			if meta.get("fields", {"fieldname":"enabled", "fieldtype":"Check"}):
  				filters.append([doctype, "enabled", "=", 1])
  			if meta.get("fields", {"fieldname":"disabled", "fieldtype":"Check"}):
  				filters.append([doctype, "disabled", "!=", 1])
  
  			fields = get_std_fields_list(meta, searchfield or "name")
  
  			# find relevance as location of search term from the beginning of string `name`. used for sorting results.
  			fields.append("""locate("{_txt}", `tab{doctype}`.`name`) as `_relevance`""".format(
  				_txt=frappe.db.escape((txt or "").replace("%", "")), doctype=doctype))
  
  			values = frappe.widgets.reportview.execute(doctype,
  				filters=filters, fields=fields,
  				or_filters = or_filters, limit_start = start,
  				limit_page_length=page_len,
  				order_by="if(_relevance, _relevance, 99999), name asc".format(doctype),
  				ignore_permissions = True if doctype == "DocType" else False, # for dynamic links
  				as_list=True)
  
  			# remove _relevance from results
  			frappe.response["values"] = [r[:-1] for r in values]
  
  def get_std_fields_list(meta, key):
  	# get additional search fields
  	sflist = meta.search_fields and meta.search_fields.split(",") or []
  	sflist = ['name'] + sflist
  	if not key in sflist:
  		sflist = sflist + [key]
  
  	return ['`tab%s`.`%s`' % (meta.name, f.strip()) for f in sflist]
  
  def build_for_autosuggest(res):
  	results = []
  	for r in res:
  		out = {"value": r[0], "description": ", ".join(unique(cstr(d) for d in r)[1:])}
  		results.append(out)
  	return results
  
  def scrub_custom_query(query, key, txt):
  	if '%(key)s' in query:
  		query = query.replace('%(key)s', key)
  	if '%s' in query:
  		query = query.replace('%s', ((txt or '') + '%'))
  	return query
  