diff --git a/trac/ticket/query.py b/trac/ticket/query.py
--- a/trac/ticket/query.py
+++ b/trac/ticket/query.py
@@ -35,7 +35,7 @@
 from trac.util.presentation import Paginator
 from trac.util.text import shorten_line
 from trac.util.translation import _
-from trac.web import IRequestHandler
+from trac.web import parse_query_string, IRequestHandler
 from trac.web.href import Href
 from trac.web.chrome import add_ctxtnav, add_link, add_script, add_stylesheet, \
                             INavigationContributor, Chrome
@@ -739,6 +739,7 @@
         req.perm.assert_permission('TICKET_VIEW')
 
         constraints = self._get_constraints(req)
+        args = req.args
         if not constraints and not 'order' in req.args:
             # If no constraints are given in the URL, use the default ones.
             if req.authname and req.authname != 'anonymous':
@@ -751,37 +752,45 @@
                 user = email or name or None
                       
             self.log.debug('QueryModule: Using default query: %s', str(qstring))
-            constraints = Query.from_string(self.env, qstring).constraints
-            # Substitute $USER, or ensure no field constraints that depend on
-            # $USER are used if we have no username.
-            for field, vals in constraints.items():
-                for (i, val) in enumerate(vals):
-                    if user:
-                        vals[i] = val.replace('$USER', user)
-                    elif val.endswith('$USER'):
-                        del constraints[field]
-                        break
+            if qstring.startswith('?'):
+                ticket_fields = [f['name'] for f in
+                                 TicketSystem(self.env).get_ticket_fields()]
+                ticket_fields.append('id')
+                args = parse_query_string(qstring[1:])
+                constraints = dict((k, args.getlist(k)) for k in args 
+                                   if k in ticket_fields)
+            else:
+                constraints = Query.from_string(self.env, qstring).constraints
+                # Substitute $USER, or ensure no field constraints that depend
+                # on $USER are used if we have no username.
+                for field, vals in constraints.items():
+                    for (i, val) in enumerate(vals):
+                        if user:
+                            vals[i] = val.replace('$USER', user)
+                        elif val.endswith('$USER'):
+                            del constraints[field]
+                            break
 
-        cols = req.args.get('col')
+        cols = args.get('col')
         if isinstance(cols, basestring):
             cols = [cols]
         # Since we don't show 'id' as an option to the user,
         # we need to re-insert it here.            
         if cols and 'id' not in cols: 
             cols.insert(0, 'id')
-        rows = req.args.get('row', [])
+        rows = args.get('row', [])
         if isinstance(rows, basestring):
             rows = [rows]
         format = req.args.get('format')
-        max = req.args.get('max')
+        max = args.get('max')
         if max is None and format in ('csv', 'tab'):
             max = 0 # unlimited unless specified explicitly
         query = Query(self.env, req.args.get('report'),
-                      constraints, cols, req.args.get('order'),
-                      'desc' in req.args, req.args.get('group'),
-                      'groupdesc' in req.args, 'verbose' in req.args,
+                      constraints, cols, args.get('order'),
+                      'desc' in args, args.get('group'),
+                      'groupdesc' in args, 'verbose' in args,
                       rows,
-                      req.args.get('page'), 
+                      args.get('page'), 
                       max)
 
         if 'update' in req.args:
diff --git a/trac/web/api.py b/trac/web/api.py
--- a/trac/web/api.py
+++ b/trac/web/api.py
@@ -26,7 +26,7 @@
 import urlparse
 
 from trac.core import Interface, TracError
-from trac.util import get_last_traceback, md5
+from trac.util import get_last_traceback, md5, unquote
 from trac.util.datefmt import http_date, localtz
 from trac.web.href import Href
 from trac.web.wsgi import _FileWrapper
@@ -97,6 +97,31 @@
         if not isinstance(val, list):
             val = [val]
         return val
+
+
+def parse_query_string(query_string):
+    """Parse a query string into a _RequestArgs."""
+    args = _RequestArgs()
+    for arg in query_string.split('&'):
+        nv = arg.split('=', 1)
+        if len(nv) == 2:
+            (name, value) = nv
+        else:
+            (name, value) = (nv[0], '')
+        name = unquote(name.replace('+', ' '))
+        if isinstance(name, unicode):
+            name = name.encode('utf-8')
+        value = unquote(value.replace('+', ' '))
+        if not isinstance(value, unicode):
+            value = unicode(value, 'utf-8')
+        if name in args:
+            if isinstance(args[name], list):
+                args[name].append(value)
+            else:
+                args[name] = [args[name], value]
+        else:
+            args[name] = value
+    return args
 
 
 class RequestDone(Exception):

