public AsyncQueryExecutor
        (
            AsyncCluster cluster,
            QueryPolicy policy,
            RecordSequenceListener listener,
            Statement statement,
            Node[] nodes
        ) : base(cluster)
        {
            this.listener = listener;
            statement.Prepare(true);

            // Create commands.
            AsyncQuery[] tasks = new AsyncQuery[nodes.Length];
            int          count = 0;

            foreach (Node node in nodes)
            {
                tasks[count++] = new AsyncQuery(this, cluster, (AsyncNode)node, policy, listener, statement);
            }

            // Dispatch commands to nodes.
            if (policy.failOnClusterChange)
            {
                ExecuteValidate(tasks, policy.maxConcurrentNodes, statement.ns);
            }
            else
            {
                Execute(tasks, policy.maxConcurrentNodes);
            }
        }
        public AsyncQueryExecutor(AsyncCluster cluster, QueryPolicy policy, RecordSequenceListener listener, Statement statement)
        {
            this.listener = listener;
            statement.Prepare(true);

            Node[] nodes = cluster.Nodes;
            if (nodes.Length == 0)
            {
                throw new AerospikeException(ResultCode.SERVER_NOT_AVAILABLE, "Query failed because cluster is empty.");
            }

            // Create commands.
            AsyncQuery[] tasks            = new AsyncQuery[nodes.Length];
            int          count            = 0;
            bool         hasClusterStable = true;

            foreach (Node node in nodes)
            {
                if (!node.HasClusterStable)
                {
                    hasClusterStable = false;
                }
                tasks[count++] = new AsyncQuery(this, cluster, (AsyncNode)node, policy, listener, statement);
            }

            // Dispatch commands to nodes.
            if (policy.failOnClusterChange && hasClusterStable)
            {
                ExecuteValidate(cluster, tasks, policy.maxConcurrentNodes, statement.ns);
            }
            else
            {
                Execute(tasks, policy.maxConcurrentNodes);
            }
        }
        public AsyncQueryExecutor(AsyncCluster cluster, QueryPolicy policy, RecordSequenceListener listener, Statement statement)
        {
            this.listener = listener;
            statement.Prepare(true);

            Node[] nodes = cluster.Nodes;
            if (nodes.Length == 0)
            {
                throw new AerospikeException(ResultCode.SERVER_NOT_AVAILABLE, "Query failed because cluster is empty.");
            }

            // Create commands.
            AsyncQuery[] tasks = new AsyncQuery[nodes.Length];
            int count = 0;

            foreach (Node node in nodes)
            {
                tasks[count++] = new AsyncQuery(this, cluster, (AsyncNode)node, policy, listener, statement);
            }
            // Dispatch commands to nodes.
            Execute(tasks, policy.maxConcurrentNodes);
        }
        //----------------------------------------------------------
        // Query/Execute UDF (Supported by Aerospike 3 servers only)
        //----------------------------------------------------------
        /// <summary>
        /// Apply user defined function on records that match the statement filter.
        /// Records are not returned to the client.
        /// This asynchronous server call will return before command is complete.  
        /// The user can optionally wait for command completion by using the returned 
        /// ExecuteTask instance.
        /// <para>
        /// This method is only supported by Aerospike 3 servers.
        /// </para>
        /// </summary>
        /// <param name="policy">configuration parameters, pass in null for defaults</param>
        /// <param name="statement">record filter</param>
        /// <param name="packageName">server package where user defined function resides</param>
        /// <param name="functionName">function name</param>
        /// <param name="functionArgs">to pass to function name, if any</param>
        /// <exception cref="AerospikeException">if command fails</exception>
        public ExecuteTask Execute(WritePolicy policy, Statement statement, string packageName, string functionName, params Value[] functionArgs)
        {
            if (policy == null)
            {
                policy = writePolicyDefault;
            }

            statement.SetAggregateFunction(packageName, functionName, functionArgs);
            statement.Prepare(false);

            Node[] nodes = cluster.Nodes;
            if (nodes.Length == 0)
            {
                throw new AerospikeException(ResultCode.SERVER_NOT_AVAILABLE, "Command failed because cluster is empty.");
            }

            Executor executor = new Executor(nodes.Length);

            foreach (Node node in nodes)
            {
                ServerCommand command = new ServerCommand(node, policy, statement);
                executor.AddCommand(command);
            }

            executor.Execute(nodes.Length);
            return new ExecuteTask(cluster, policy, statement);
        }
        /// <summary>
        /// Execute query, apply statement's aggregation function, and return result iterator. 
        /// The aggregation function should be initialized via the statement's SetAggregateFunction()
        /// and should be located in a Lua resource file located in an assembly.
        /// <para>
        /// The query executor puts results on a queue in separate threads.  The calling thread 
        /// concurrently pops results off the queue through the ResultSet iterator.
        /// The aggregation function is called on both server and client (final reduce).
        /// Therefore, the Lua script file must also reside on both server and client.
        /// </para>
        /// <para>
        /// This method is only supported by Aerospike 3 servers.
        /// </para>
        /// </summary>
        /// <param name="policy">generic configuration parameters, pass in null for defaults</param>
        /// <param name="statement">database query command with aggregate functions already initialized by SetAggregateFunction()</param>
        /// <exception cref="AerospikeException">if query fails</exception>
        public ResultSet QueryAggregate(QueryPolicy policy, Statement statement)
        {
            if (policy == null)
            {
                policy = queryPolicyDefault;
            }
            statement.Prepare(true);

            QueryAggregateExecutor executor = new QueryAggregateExecutor(cluster, policy, statement);
            executor.Execute();
            return executor.ResultSet;
        }