                             table      : table
                             query      : query
                             options    : opts.options
+                            multi      : multi
                             cb         : (err, x) =>
                                 result[table] = x; cb(err)
                         return

                            filter = x
        return filter

    user_get_query: (opts) =>
        opts = defaults opts,
            account_id : required
            table      : required
            query      : required
            options    : required
            cb         : required   # cb(err, result)
        db_query = undefined
        f = (cb) =>
            q = opts.query
            table = opts.table
            switch table
                when 'accounts'
                    db_query = @table(table).getAll(opts.account_id)
                    cb()
                when 'stats'
                    db_query = @table(table)
                    if q.timestamp != null
                        # TODO
                        cb("TODO -- timestamp range query")
                    else
                        cb()
                when 'blobs'
                    if q.uuid
                        db_query = @table(table).getAll(q.uuid)
                        cb()
                    else
                        cb("must specify uuid")
                when 'file_use', 'projects', 'file_access_log'
                    f = (cb) =>
                        if q.project_id != null
                            cb(undefined, [q.project_id])
                        else
                            @get_project_ids_with_user
                                account_id : opts.account_id
                                cb         : cb
                    f (err, project_ids) =>
                        if err
                            cb(err)
                        else
                            db_query = @table(table).getAll(project_ids..., index:'project_id')
                        cb()
                else
                    cb("unknown table '#{opts.query.table}")
        f (err) =>
            if err
                opts.cb(err); return
            filter = @_query_to_filter(opts.query)
            if filter?
                db_query = db_query.filter(filter)
            fields = misc.keys(opts.query)
            db_query = db_query.pluck(fields)
            for x in opts.options
                for name, value of x
                    switch name
                        when 'limit'
                            db_query = db_query.limit(value)
                        when 'slice'
                            db_query = db_query.slice(value...)
                        when 'order_by'
                            # TODO: could optimize with an index
                            db_query = db_query.orderBy(value)
                        else
                            opts.cb("unknown option '#{name}")
                            return
            db_query.run(opts.cb)

    user_set_query: (opts) =>
        opts = defaults opts,
            account_id : required
            table      : required
            query      : required
            options    : required
            cb         : required   # cb(err, result)
        opts.cb("set_query not implemented")

            query      : required
            options    : {}
            cb         : required   # cb(err, result)

        if misc.is_array(opts.query)
            # array of queries
            result = []
            f = (query, cb) =>
                @user_query
                    account_id : opts.account_id
                    query      : query
                    options    : opts.options
                    cb         : (err, x) =>
                        result.push(x); cb(err)
            async.mapSeries(opts.query, f, (err) => opts.cb(err, result))
            return

        # individual query
        result = {}
        f = (table, cb) =>
            query = opts.query[table]
            if misc.is_array(query)
                if query.length > 1
                    cb("array of length > 1 not yet implemented")
                    return
                multi = true
                query = query[0]
            else
                multi = false
            if typeof(query) == "object"
                for k, v of query
                    if v == null
