%KEYVALUEOUTPUTSTORE Store key-value pairs for use with mapreduce.
%   KeyValueOutputStore objects are used within MAPREDUCE
%
%      OUTDS = MAPREDUCE(INDS,MAPFUN,REDUCEFUN,...)
% 
%   where MAPFUN and REDUCEFUN are functions:
% 
%      MAPFUN(data,info,KVstore)
%      REDUCEFUN(key,valsIter,KVstore)
% 
%   Within MAPFUN or REDUCEFUN, add one or more key-value pairs
%   to the KeyValueOutputStore KVstore using
% 
%      ADD(KVstore,key,val)
%      ADDMULTI(KVstore,{key1; key2; ...},{val1; val2; ...})
%  
%   All keys added must obey the following rules:
%       1. Keys can be numeric scalars or strings.
%       2. All keys added by the map function must have the same class.
%       3. All keys added by the reduce function must have the same class,
%          but may differ from the class of the keys added by the map
%          function.
%
%   KeyValueOutputStore Methods:
%     add      - Adds a single key-value pair to the KeyValueOutputStore.
%     addmulti - Adds multiple key-value pairs to the KeyValueOutputStore. 
% 
%   Example:
%        add(kvs,'sumAndLength',[1524234, 20000])
%        addmulti(kvs,{'sum';'length'},{1524234; 20000})
% 
% See also mapreduce.

%   Copyright 2014-2016 The MathWorks, Inc.

classdef (Sealed) KeyValueOutputStore < matlab.mapreduce.internal.AbstractKeyValueStore
    properties (GetAccess = private, SetAccess = immutable)
        KeyValueSink; % The underlying sink that will handle where the output is sent.
        KeyValueProcessor; % A helper object that encapsulates the parsing of input data.
    end
    
    methods
        % Add a single key-value pair to the next part of the Map-Reduce chain.
        function add(this, key, value)
            try
                key = this.KeyValueProcessor.processSingleKey(key);
                value = this.KeyValueProcessor.processSingleValue(value);
                this.KeyValueSink.add(key, value);
            catch err
                throw(err);
            end
        end

        % Add multiple key-value pairs to the next part of the Map-Reduce chain.
        function addmulti(this, keys, values)
            try
                [keys, values, empty] = this.KeyValueProcessor.processMultiKeysValues(keys, values);
                if empty
                    return;
                end
                this.KeyValueSink.add(keys, values);
            catch err
                throw(err);
            end
        end
    end
    
    methods (Access = private)
        % This class can only be constructed through the hidden build function.
        function this = KeyValueOutputStore(keyValueSink, processor)
            this.KeyValueSink = keyValueSink;
            this.KeyValueProcessor = processor;
        end
    end
    
    methods (Static, Hidden)
        % Hidden build function.
        function this = hBuild(keyValueSink)
            import parallel.mapreduce.KeyValueOutputStore;
            assert(isa(keyValueSink, 'parallel.internal.mapreduce.AbstractKeyValueSink'));
            
            processor = keyValueSink.createKeyValueProcessor();
            this = KeyValueOutputStore(keyValueSink, processor);
        end
    end
end
