The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/bin/env perl

use strict;
use warnings;

use Test::More;
use Test::Data::Riak;

use Data::Riak;
use Data::Riak::Bucket;

skip_unless_riak;

my $riak = riak_transport;

# Implement the example from the Riak docs.
# http://wiki.basho.com/MapReduce.html#MapReduce-via-the-HTTP-API

my $bucket_name = create_test_bucket_name;

my $bucket = Data::Riak::Bucket->new({
    name => $bucket_name,
    riak => $riak
});

my $text1 = "
Alice was beginning to get very tired of sitting by her sister on the
bank, and of having nothing to do: once or twice she had peeped into the
book her sister was reading, but it had no pictures or conversations in
it, 'and what is the use of a book,' thought Alice 'without pictures or
conversation?'
";

my $text2 = "
So she was considering in her own mind (as well as she could, for the
hot day made her feel very sleepy and stupid), whether the pleasure
of making a daisy-chain would be worth the trouble of getting up and
picking the daisies, when suddenly a White Rabbit with pink eyes ran
close by her.
";

my $text3 = "
The rabbit-hole went straight on like a tunnel for some way, and then
dipped suddenly down, so suddenly that Alice had not a moment to think
about stopping herself before she found herself falling down a very deep
well.
";

$bucket->add('p1', $text1);
$bucket->add('p2', $text2);
$bucket->add('p5', $text3);

# setup some data for the tests

my $MAP_REDUCE_RESULTS = {
    'dipped' => 1, 'on' => 2, 'alice' => 3, 'ran' => 1, 'day' => 1, 'own' => 1, 'deep' => 1,
    'trouble' => 1, 'what' => 1, 'conversations' => 1, 'bank' => 1, 'moment' => 1,
    'daisies' => 1, 'but' => 1, 'some' => 1, 'with' => 1, 'suddenly' => 3, 'and' => 5,
    'of' => 5, 'do' => 1, 'into' => 1, 'is' => 1, 'found' => 1, 'she' => 4, 'herself' => 2,
    'stupid' => 1, 'to' => 3, 'making' => 1, 'think' => 1, 'her' => 5, 'when' => 1,
    'it' => 2, 'sleepy' => 1, 'hole' => 1, 'tunnel' => 1, 'then' => 1, 'reading' => 1,
    'hot' => 1, 'thought' => 1, 'the' => 8, 'made' => 1, 'way' => 1, 'a' => 6, 'would' => 1,
    'no' => 1, 'twice' => 1, 'like' => 1, 'white' => 1, 'or' => 3, 'went' => 1, 'in' => 2,
    'could' => 1, 'sitting' => 1, 'down' => 2, 'about' => 1, 'before' => 1, 'so' => 2,
    'once' => 1, 'very' => 3, 'sister' => 2, 'for' => 2, 'by' => 2, 'chain' => 1, 'be' => 1,
    'daisy' => 1, 'feel' => 1, 'whether' => 1, 'eyes' => 1, 'mind' => 1, 'pink' => 1, 'up' => 1,
    'having' => 1, 'considering' => 1, 'conversation' => 1, 'close' => 1, 'pleasure' => 1,
    'use' => 1, 'straight' => 1, 'picking' => 1, 'tired' => 1, 'peeped' => 1, 'get' => 1,
    'had' => 3, 'beginning' => 1, 'without' => 1, 'getting' => 1, 'well' => 2, 'as' => 2,
    'was' => 3, 'rabbit' => 2, 'book' => 2, 'pictures' => 2, 'falling' => 1, 'nothing' => 1,
    'stopping' => 1, 'worth' => 1, 'not' => 1, 'that' => 1,
};

my $MAP_SOURCE = q[
    function(v) {
      var m = v.values[0].data.toLowerCase().match(/\w*/g);
      var r = [];
      for(var i in m) {
        if(m[i] != '') {
          var o = {};
          o[m[i]] = 1;
          r.push(o);
        }
      }
      return r;
    }
];

my $REDUCE_SOURCE = q[
function(v) {
  var r = {};
  for(var i in v) {
    for(var w in v[i]) {
      if(w in r) r[w] += v[i][w];
      else r[w] = v[i][w];
    }
  }
  return [r];
}
];

sub test_map_reduce {
    my $mr = shift;

    my $results = $mr->mapreduce;
    isa_ok($results, 'Data::Riak::ResultSet');

    my $result = $results->first;
    isa_ok($result, 'Data::Riak::Result');

    isa_ok($result->content_type, 'HTTP::Headers::ActionPack::MediaType');
    is($result->content_type->type, 'application/json', '... got the right content type');

    my $value = JSON::XS->new->decode( $result->value );
    is_deeply(
        $value,
        [ $MAP_REDUCE_RESULTS ],
        '... got the expected results'
    );
}

# test with buckets and keys specified
test_map_reduce(
    Data::Riak::MapReduce->new({
        riak => $riak,
        inputs => [ [ $bucket_name, "p1" ], [ $bucket_name, "p2" ], [ $bucket_name, "p5" ] ],
        phases => [
          Data::Riak::MapReduce::Phase::Map->new(
              language => 'javascript',
              source => $MAP_SOURCE
          ),
          Data::Riak::MapReduce::Phase::Reduce->new(
              language => 'javascript',
              source => $REDUCE_SOURCE
          )
        ]
    })
);

# test with just a bucket name
test_map_reduce(
    Data::Riak::MapReduce->new({
        riak => $riak,
        inputs => $bucket_name,
        phases => [
            Data::Riak::MapReduce::Phase::Map->new(
                language => 'javascript',
                source => $MAP_SOURCE
            ),
            Data::Riak::MapReduce::Phase::Reduce->new(
                language => 'javascript',
                source => $REDUCE_SOURCE
            )
        ]
    })
);

{
    my $mr = Data::Riak::MapReduce->new({
        riak => $riak,
        inputs => $bucket_name,
        phases => [
            Data::Riak::MapReduce::Phase::Map->new(
                language => 'javascript',
                source => $MAP_SOURCE
            ),
            Data::Riak::MapReduce::Phase::Reduce->new(
                language => 'javascript',
                source => $REDUCE_SOURCE
            )
        ]
    });

    my $results = $mr->mapreduce( chunked => 1 );
    isa_ok($results, 'Data::Riak::ResultSet');

    my $result = $results->first;
    isa_ok($result, 'Data::Riak::Result');

    isa_ok($result->content_type, 'HTTP::Headers::ActionPack::MediaType');
    is($result->content_type->type, 'application/json', '... got the right content type');

    my $value = JSON::XS->new->decode( $result->value );
    is_deeply(
        $value,
        {
            phase => 1,
            data => [ $MAP_REDUCE_RESULTS ],
        },
        '... got the expected results'
    );
}


remove_test_bucket($bucket);

done_testing;