[#8197] better matching on site admin searches
diff --git a/Allura/allura/lib/search.py b/Allura/allura/lib/search.py
index 1cf26c2..736b42b 100644
--- a/Allura/allura/lib/search.py
+++ b/Allura/allura/lib/search.py
@@ -194,7 +194,12 @@
         q = obj.translate_query(q, fields)
     else:
         # construct query for a specific selected field
-        q = obj.translate_query(u'%s:%s' % (field, q), fields)
+        # use parens to group all the parts of the query with the field
+        # escaping spaces with '\ ' isn't sufficient for display_name_t since its stored as text_general (why??)
+        # and wouldn't handle foo@bar.com split on @ either
+        # This should work, but doesn't for unknown reasons: q = u'{!term f=%s}%s' % (field, q)
+        q = obj.translate_query(u'%s:(%s)' % (field, q), fields)
+        kw['q.op'] = 'AND'  # so that all terms within the () are required
     fq = [u'type_s:%s' % model.type_s]
     return search(q, fq=fq, ignore_errors=False, **kw)
 
diff --git a/Allura/allura/templates/site_admin_search.html b/Allura/allura/templates/site_admin_search.html
index c43982d..d7d11eb 100644
--- a/Allura/allura/templates/site_admin_search.html
+++ b/Allura/allura/templates/site_admin_search.html
@@ -31,6 +31,7 @@
 
   <div class="grid-23">
     {{ c.search_form.display(q=q, f=f) }}
+    <p>Use quotes for an exact literal match, or <code>*</code> for wildcard.  E.g. <code>"john doe"</code> or <code>test*</code></p>
   </div>
 
   {% if objects %}
diff --git a/Allura/allura/tests/unit/test_solr.py b/Allura/allura/tests/unit/test_solr.py
index 3d5317c..af59ae9 100644
--- a/Allura/allura/tests/unit/test_solr.py
+++ b/Allura/allura/tests/unit/test_solr.py
@@ -98,7 +98,7 @@
         fq = ['type_s:Project']
         site_admin_search(Project, 'test', 'shortname', rows=25)
         search.assert_called_once_with(
-            'shortname_s:test', fq=fq, ignore_errors=False, rows=25)
+            'shortname_s:(test)', fq=fq, ignore_errors=False, rows=25, **{'q.op': 'AND'})
 
         search.reset_mock()
         site_admin_search(Project, 'shortname:test || shortname:test2', '__custom__')
@@ -109,7 +109,7 @@
         search.reset_mock()
         site_admin_search(User, 'test-user', 'username', rows=25)
         search.assert_called_once_with(
-            'username_s:test-user', fq=fq, ignore_errors=False, rows=25)
+            'username_s:(test-user)', fq=fq, ignore_errors=False, rows=25, **{'q.op': 'AND'})
 
         search.reset_mock()
         site_admin_search(User, 'username:admin1 || username:root', '__custom__')