                         sub = false
                         break
                 if sub
-                    selector[k] = @_query_to_field_selector(v)
+                    selector[k] = @_query_to_field_selector(v, primary_key)
         return selector
 
     _query_get: (table, query, account_id) =>
         x = {}
-        switch table
-            when 'server_settings', 'central_log', 'client_error_log'
+        keys = misc.keys(query)
+        if keys.length == 0
+            x.err = "must specify at least one field"
+            return x
+
+        t = TABLES[table]
+        if not t?
+            x.err = "unknown table '#{table}'"
+            return x
+
+        for k in keys
+            if t.user_set?[k] or t.user_get?[k]
+                continue
+            if t.admin_get?[k]
                 x.require_admin = true
-            when 'accounts'
-                x.get_all = [account_id]
-            when 'stats'
-                if query.timestamp != null
-                    # TODO
-                    x.error = "TODO -- timestamp range query"
-            when 'blobs'
-                if query.uuid
-                    x.get_all = [query.uuid]
-                else
-                    x.error = "must specify uuid"
-            when 'file_use', 'projects', 'file_access_log'
-                if query.project_id? and query.project_id != null
-                    single = false
-                    if typeof(query.project_id) == 'object'
-                        for k, v of query.project_id
-                            if k == '=='
-                                query.project_id = v
-                                single = true
-                                break
-                    if single
-                        x.get_all = x.require_project_ids_read_access = [query.project_id]
-                    else
-                        x.get_all = 'all_projects'
-                else
-                    x.get_all = 'all_projects'
+                continue
+            x.err = "reading #{table}.#{k} not allowed"
+            return x
+
+        if not t.user_get_all?
+            x.err = "filtering all from #{table} not allowed"
+            return x
+
+        if t.user_get_all == 'all_projects_read' and query.project_id?
+            {get_all, err} = @_primary_key_query('project_id', query)
+            if err
+                x.err = err
+                return x
             else
-                x.error = "unknown table '#{table}'"
+                x.get_all = get_all
+                x.require_project_read_access = get_all
+        if not x.get_all?
+            x.get_all = t.user_get_all
         return x
 
     _require_is_admin: (account_id, cb) =>

            options    : required
            cb         : required   # cb(err, result)
        results = undefined
        {require_admin, get_all, require_project_read_access, err} = @_query_get(opts.table, opts.query, opts.account_id)
        if err
            cb(err); return
        async.series([
            (cb) =>
                async.parallel([

        admin_set :
            name : true
            value : true
        admin_get :
            name : true
    stats :
        timestamp : []
        admin_get :
            timestamp : true
            accounts : true
            projects : true
            active_projects : true
            last_day_projects : true
            last_week_projects : true
            last_month_projects : true

# these fields are arrays of account id's, which
# we need indexed:
