-
Notifications
You must be signed in to change notification settings - Fork 25
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #76 from RagibHasin/master
Added mapReduce on Model and supporting tests, decorators and types.
- Loading branch information
Showing
7 changed files
with
252 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
import * as MongoDB from "mongodb" | ||
|
||
import { Instance } from "./Instance" | ||
|
||
declare global { | ||
/** | ||
* Emits a key-value pair for mapping. | ||
* This is NOT a global function but is a MongoDB srever internal function. | ||
* Only use in map function and nowhere else. | ||
* | ||
* @param {any} key Key to emit | ||
* @param {any} value Value to emit | ||
*/ | ||
function emit(key: any, value: any): void | ||
} | ||
|
||
/** | ||
* A function to map values. | ||
* Values are mapped by calling a MongoDB server internal function `emit`. | ||
* | ||
* @param TDocument Interface of the document it works on. | ||
* @return {void} Nothing | ||
*/ | ||
export interface MapFunction<TDocument> { | ||
(this: TDocument): void | ||
} | ||
|
||
/** | ||
* A function to reduce mapped values. | ||
* | ||
* @param Key Type of the key | ||
* @param Value Type of the value | ||
* | ||
* @param {Key} key The key to reduce | ||
* @param {Value[]} values The values to reduce | ||
*/ | ||
export interface ReduceFunction<Key, Value> { | ||
(key: Key, values: Value[]): Value | ||
} | ||
|
||
/** | ||
* Interface of the document returned as result of a mapReduce operation. | ||
* | ||
* @param Key Type of the key | ||
* @param Value Type of the value | ||
*/ | ||
export interface MapReducedDocument<Key, Value> { | ||
_id: Key | ||
value: Value | ||
} | ||
|
||
/** | ||
* MapReduce Options. | ||
* | ||
* @param TDocument Interface of the document it works on. | ||
* @param Key Type of the key to reduce | ||
* @param Value Type of the values to reduce | ||
* | ||
* Extends `MongoDB.MapReduceOptions` with additional map and reduce field. | ||
*/ | ||
export interface MapReduceFunctions<TDocument, Key, Value> { | ||
map: MapFunction<TDocument> | ||
reduce: ReduceFunction<Key, Value> | ||
} | ||
|
||
export interface MapReduceOptions extends MongoDB.MapReduceOptions { | ||
out?: "inline" | "replace" | "merge" | "reduce" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
import * as Iridium from "../iridium"; | ||
import * as MongoDB from "mongodb"; | ||
import { Cursor } from "../lib/Cursor"; | ||
import * as Promise from "bluebird"; | ||
import * as _ from "lodash"; | ||
import * as chai from "chai"; | ||
|
||
// This test folows the example depicted on https://docs.mongodb.com/manual/core/map-reduce/ | ||
|
||
interface TestDocument { | ||
_id?: string; | ||
cust_id: string; | ||
amount: number; | ||
status: string; | ||
} | ||
|
||
class Test extends Iridium.Instance<TestDocument, Test> implements TestDocument { | ||
static collection = "test"; | ||
static schema: Iridium.Schema = { | ||
_id: MongoDB.ObjectID, | ||
cust_id: String, | ||
amount: Number, | ||
status: String, | ||
}; | ||
|
||
_id: string; | ||
cust_id: string; | ||
amount: number; | ||
status: string; | ||
} | ||
|
||
class MapReducedInstance extends Iridium.Instance<Iridium.MapReducedDocument<string, number>, MapReducedInstance>{ | ||
static collection = "mapReduced"; | ||
static mapReduceOptions = { | ||
map: function (this: TestDocument) { | ||
emit(this.cust_id, this.amount); | ||
}, | ||
reduce: function (key: string, values: number[]) { | ||
return values.reduce((sum, val) => sum + val, 0) | ||
} | ||
} | ||
_id: string | ||
value: number | ||
} | ||
|
||
describe("Model", () => { | ||
let core = new Iridium.Core({ database: "test" }); | ||
|
||
before(() => core.connect()); | ||
|
||
describe("mapReduce()", () => { | ||
let model = new Iridium.Model<TestDocument, Test>(core, Test); | ||
|
||
beforeEach(() => { | ||
return core.connect().then(() => model.remove()).then(() => model.insert([ | ||
{ cust_id: "A123", amount: 500, status: "A" }, | ||
{ cust_id: "A123", amount: 250, status: "A" }, | ||
{ cust_id: "A123", amount: 300, status: "B" }, | ||
{ cust_id: "B212", amount: 200, status: "A" } | ||
])); | ||
}); | ||
|
||
it("should correctly map and reduce with model", () => { | ||
let reducedModel = new Iridium.Model<Iridium.MapReducedDocument<string, number>, MapReducedInstance>(core, MapReducedInstance); | ||
let t = reducedModel.remove().then(() => model.mapReduce(MapReducedInstance, { | ||
out: "replace", query: { status: "A" } | ||
}).then(() => reducedModel.find().toArray())) | ||
return chai.expect(t).to.eventually.exist.and.have.length(2); | ||
}); | ||
|
||
it("should correctly map and reduce inline without specifying out option", () => { | ||
let t = model.mapReduce({ | ||
map: function (this: TestDocument) { | ||
emit(this.cust_id, this.amount); | ||
}, reduce: function (k: string, v: number[]) { | ||
return v.reduce((sum, val) => sum + val, 0) | ||
} | ||
}, { query: { status: "A" } }) | ||
return chai.expect(t).to.eventually.exist.and.have.length(2); | ||
}); | ||
|
||
it("should reject with wrong out option for inline", () => { | ||
let t = model.mapReduce({ | ||
map: function (this: TestDocument) { | ||
emit(this.cust_id, this.amount); | ||
}, reduce: function (k: string, v: number[]) { | ||
return v.reduce((sum, val) => sum + val, 0) | ||
} | ||
}, { out: "replace", query: { status: "A" } }) | ||
return chai.expect(t).to.eventually.be.rejected | ||
}); | ||
|
||
it("should reject with wrong out option for model", () => { | ||
let t = model.mapReduce(MapReducedInstance, { | ||
out: "inline", query: { status: "A" } | ||
}) | ||
return chai.expect(t).to.eventually.be.rejected | ||
}); | ||
}); | ||
}); |