Skip to content

Commit

Permalink
The "out" option in the MapReduce command must be ordered. This was
Browse files Browse the repository at this point in the history
found after the implementation was accepting maps for a long time,
so rather than breaking the API, the implementation will fix the
order if necessary.

Details about the order requirement may be seen in MongoDB's code:

    http://goo.gl/L8jwJX

Thanks to Peter Kleiweg for reporting the issue.
  • Loading branch information
niemeyer committed Sep 4, 2013
1 parent 051c58d commit 3ed4873
Show file tree
Hide file tree
Showing 3 changed files with 55 additions and 2 deletions.
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
mgo - MongoDB driver for Go

Copyright (c) 2010-2012 - Gustavo Niemeyer <[email protected]>
Copyright (c) 2010-2013 - Gustavo Niemeyer <[email protected]>

All rights reserved.

Expand Down
32 changes: 31 additions & 1 deletion session.go
Original file line number Diff line number Diff line change
Expand Up @@ -2841,7 +2841,7 @@ func (q *Query) MapReduce(job *MapReduce, result interface{}) (info *MapReduceIn
Map: job.Map,
Reduce: job.Reduce,
Finalize: job.Finalize,
Out: job.Out,
Out: fixMROut(job.Out),
Scope: job.Scope,
Verbose: job.Verbose,
Query: op.query,
Expand Down Expand Up @@ -2895,6 +2895,36 @@ func (q *Query) MapReduce(job *MapReduce, result interface{}) (info *MapReduceIn
return info, nil
}

// The "out" option in the MapReduce command must be ordered. This was
// found after the implementation was accepting maps for a long time,
// so rather than breaking the API, we'll fix the order if necessary.
// Details about the order requirement may be seen in MongoDB's code:
//
// http://goo.gl/L8jwJX
//
func fixMROut(out interface{}) interface{} {
outv := reflect.ValueOf(out)
if outv.Kind() != reflect.Map || outv.Type().Key() != reflect.TypeOf("") {
return out
}
outs := make(bson.D, outv.Len())

outTypeIndex := -1
for i, k := range outv.MapKeys() {
ks := k.String()
outs[i].Name = ks
outs[i].Value = outv.MapIndex(k).Interface()
switch ks {
case "normal", "replace", "merge", "reduce", "inline":
outTypeIndex = i
}
}
if outTypeIndex > 0 {
outs[0], outs[outTypeIndex] = outs[outTypeIndex], outs[0]
}
return outs
}

type Change struct {
Update interface{} // The change document
Upsert bool // Whether to insert in case the document isn't found
Expand Down
23 changes: 23 additions & 0 deletions session_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2850,6 +2850,29 @@ func (s *S) TestMapReduceToOtherDb(c *C) {
c.Assert(iter.Close(), IsNil)
}

func (s *S) TestMapReduceOutOfOrder(c *C) {
session, err := mgo.Dial("localhost:40001")
c.Assert(err, IsNil)
defer session.Close()

coll := session.DB("mydb").C("mycoll")

for _, i := range []int{1, 4, 6, 2, 2, 3, 4} {
coll.Insert(M{"n": i})
}

job := &mgo.MapReduce{
Map: "function() { emit(this.n, 1); }",
Reduce: "function(key, values) { return Array.sum(values); }",
Out: bson.M{"a": "a", "z": "z", "replace": "mr", "db": "otherdb", "b": "b", "y": "y"},
}

info, err := coll.Find(nil).MapReduce(job, nil)
c.Assert(err, IsNil)
c.Assert(info.Collection, Equals, "mr")
c.Assert(info.Database, Equals, "otherdb")
}

func (s *S) TestMapReduceScope(c *C) {
session, err := mgo.Dial("localhost:40001")
c.Assert(err, IsNil)
Expand Down

0 comments on commit 3ed4873

Please sign in to comment.