Mateusz Miotk
04.01.2015
Sprzęt:
Laptop ACER ASPIRE ONE 5820TG
Procesor: Intel core I5-430M
Ilość pamięci RAM: 8 GB
Dysk twardy: SSD SanDisk 128 GB
System Operacyjny: Linux Mint 17 x64
Wersja MongoDB: 2.6.5 oraz 2.8.0.rc0
Tytuł: Użycie Map Reduce w MongoDB do sprawdzenia rozkładów liter w sentencjach polskich i angielskich
Coraz częściej słyszymy o atakach na różne serwisy www oraz o nieodpowiedzialności administratorów, którzy mają bardzo słabe hasła. Jak stworzyć silne hasło: jest opinia, która mówi że należy wymyśleć sobie jakieś zdanie i za hasło wziąść pierwsze litery wyrazów w tym zdaniu. Eksperyment, który przeprowadziłem sprawdził czy ta metoda ma sens poprzez sprawdzenie czy hasła są unikalne to znaczy czy są zdania które posiadają ten sam rozkład liter. Poza tym zastanowić się można jak można ulepszyć tą metodę.
Nasuwają się następujące pytania:
- Czy usunięciu
stop-słówzwiększa unikalność haseł. - Czy posortowanie liter w rozkładzie zwiększy nieunikalność haseł.
Do przeprowadzenia eksperymentu użyłem pliku sentences.tar.bz2 ze strony tatoeba.org która zawiera plik z popularnymi sentencjami w różnych językach świata. Nas będzie interesował język angielski oraz polski.
Ponieważ plik ze strony tatoeba.org zawiera sentencje w wielu językach świata musimy przerobić i wydzielić do innych plików sentencje polskie i angielskie. Następnie dane musimy załadować do MongoDB. Fragment otrzymanego pliku:
...
5131 jpn 2006年上海では15万組の夫婦が結婚すると予想されている。
5132 jpn ニジェールでは50万人もの子供たちが未だ栄養失調に直面している。
...
53010 eng Judy is a most clever student.
53011 eng Judy is fond of dancing.
...
Do przetworzenia danych należy uruchomić skrypt PrepareData.sh, ktory:
- Pobiera plik ze strony tatoeba.org
- Rozpakowuje plik do folderu data
- Wywołuje skrypt napisany w awk który eksportuje sentencje w danym języku
- Usuwa znak "," z sentencji oraz usuwa powtarzające się linie. Kod skryptu przetwarzającego dane:
#!/bin/bash
#Prepare data and load to MongoDB
wget -O ../data/sentences.tar.bz2 http://downloads.tatoeba.org/exports/sentences.tar.bz2
tar xvf ../data/sentences.tar.bz2 -C ../data/
./convert.awk "eng" ../data/sentences.csv > ../data/english1.csv
./convert.awk "pol" ../data/sentences.csv > ../data/polish1.csv
#Remove comma from files
tr -d "," < ../data/english1.csv > ../data/english2.csv
tr -d "," < ../data/polish1.csv > ../data/polish2.csv
#Remove duplicate lines in file
awk '!x[$0]++' ../data/english2.csv > ../data/english.csv
awk '!x[$0]++' ../data/polish2.csv > ../data/polish.csv
#Remove temporary files
rm ../data/english1.csv
rm ../data/english2.csv
rm ../data/polish1.csv
rm ../data/polish2.csv
rm ../data/sentences.tar.bz2Plik ze strony tatoeba.org jeśli chodzi o język angielski zawiera błędy, które polegają na tym że językowi temu przydzielone zostały sentencje napisane w języku arabskim. Dlatego też w skrypcie są zawarte numery 3670301 itd., które zawierają te nieprawidłowe linie. Poza tym eksportuje tylko te sentencje, które zawierają od 6 do 12 słów.
Kod skryptu napisanego w awk:
#!/usr/bin/awk -f
BEGIN{
if (ARGC<3) exit(1);
arg=ARGV[1];
ARGV[1]="";
}
($2 == arg && $1 != 3670301 && $1 != 3712889 && $1 != 3712890) {
out=$3;
for(n=4;n<=NF;n++){
out=out " " $n;
}
if(NF >= 8 && NF <= 14){
printf("%s\n",out);
}
}Do załadowania danych należy użyć skryptu LoadData.sh z folderu bash_scripts. Skrypt ten używa innego skryptu napisanego w języku R, o nazwie PrepareData.R dzięki któremu ładowane są dane do MongoDB.
#!/bin/bash
#Load data to MongoDB
echo "Load Polish sentences to test.polishSentences"
Rscript ../r_scripts/PrepareData.R ../data/polish.csv 10000 test.polishSentences
echo "Load English sentences to test.englishSentences"
Rscript ../r_scripts/PrepareData.R ../data/english.csv 10000 test.englishSentences
Dane zostały zapisane odpowiednio w kolekcjach: polishSentences oraz englishSentences.
Bierzemy tu pod uwagę wyłącznie małe litery + cyfry. Aby obliczyć powyższe rozkłady należy uruchomić w konsoli mongo skrypty lettersPol.js oraz lettersEng.js.
UWAGA W sentencjach polskich zliczone są wszystkie znaki dialektyczne tzn: również znaki występujące nie tylko w języku polskim ale też niemieckim, gdyż dane posiadają słowa w tym języku np: Peter studiuje w München od roku.
Wykres występowań liter dla języka polskiego:

Najczęstsze 3 litery występujących na pierwszej pozycji:
{
"_id": "p",
"value": 21223
}
{
"_id": "n",
"value": 19242
}
{
"_id": "w",
"value": 17333
}Najczęstsze 3 litery występujących na wszystkich pozycjach:
{
"_id": "i",
"value": 90984
}
{
"_id": "e",
"value": 90671
}
{
"_id": "a",
"value": 87216
}Wykres wykres występowań liter dla języka angielskiego:

Najczęstsze 3 litery występujących na pierwszej pozycji:
{
"_id": "t",
"value": 462360
}
{
"_id": "i",
"value": 224958
}
{
"_id": "a",
"value": 201324
}Najczęstsze 3 litery występujących na wszystkich pozycjach:
{
"_id": "e",
"value": 1135093
}
{
"_id": "t",
"value": 954772
}
{
"_id": "o",
"value": 863300
}Kod pliku:
polishCollection = db.polishSentences;
englishCollection = db.englishSentences;
map = function(){
var split = this.originalText.match(/\b\w/g);
var result = split.join("");
emit(result,1);
};
reduce = function(key,values){
return Array.sum(values);
};
var resultPolish = polishCollection.mapReduce(map, reduce, {out: "resultPol"});
printjson(resultPolish);
var resultEnglish = englishCollection.mapReduce(map, reduce, {out: "resultEng"});
printjson(resultEnglish);Wynik:
{
"result" : "resultPol",
"timeMillis" : 1566,
"counts" : {
"input" : 27934,
"emit" : 27934,
"reduce" : 162,
"output" : 27771
},
"ok" : 1
}
{
"result" : "resultEng",
"timeMillis" : 16371,
"counts" : {
"input" : 301120,
"emit" : 301120,
"reduce" : 7696,
"output" : 292684
},
"ok" : 1
}Podgląd do bazy, który otrzymaliśmy:
{
"_id": "11111111112",
"value": 1
}
{
"_id": "1dwTcnw",
"value": 1
}
{
"_id": "Amepjdkdw",
"value": 2
}
{
"_id": "BdmotpzT",
"value": 2
}Powyższa baza niewiele nam mówi bo nie mamy przedewszystkim jakie zdanie mają dany rozkład, dlatego zmodyfikujemy trochę plik.
Kod pliku:
polishCollection = db.polishSentences;
englishCollection = db.englishSentences;
map = function(){
var split = this.originalText.match(/\b\w/g);
var result = split.join("");
emit(result,{list: [this.originalText],count: 1});
};
reduce = function(key,values){
var list = [];
var count = 0;
values.forEach(function(item){
list = item.list.concat(list);
count += item.count;
});
return ({list: list,count: count});
};
var resultPolish = polishCollection.mapReduce(map, reduce, {out: "result2Pol"});
printjson(resultPolish);
var resultEnglish = englishCollection.mapReduce(map, reduce, {out: "result2Eng"});
printjson(resultEnglish);Otrzymany wynik:
{
"result" : "result2Pol",
"timeMillis" : 1723,
"counts" : {
"input" : 27934,
"emit" : 27934,
"reduce" : 159,
"output" : 27771
},
"ok" : 1
}
{
"result" : "result2Eng",
"timeMillis" : 19181,
"counts" : {
"input" : 301120,
"emit" : 301120,
"reduce" : 7668,
"output" : 292684
},
"ok" : 1
}Podgląd do otrzymanej bazy:
{
"_id": "13itnawIcbr",
"value": {
"list": [
"104-3820 is the number at which I can be reached."
],
"count": 1
}
}
{
"_id": "1Fctcbm",
"value": {
"list": [
"1. Finely chop the chicken breast meat."
],
"count": 1
}
}
{
"_id": "ADtphi",
"value": {
"list": [
"A DNA test proved her innocence.",
"A DNA test proved his innocence."
],
"count": 2
}
}
{
"_id": "AItoowttiw",
"value": {
"list": [
"Am I the only one who thinks this is weird?",
"Am I the only one who thinks this is wrong?"
],
"count": 2
}
}Jak już widać niektóre rozkłady występują więcej niż 1 raz. Policzmy ile jest takich rozkładów w obu przypadkach.
Kod pliku:
resultPol = db.resultPol;
resultEng = db.resultEng;
map = function(){
emit("answer",{value: this.value, sum: this.value,notUnique: 0});
};
reduce = function(key,values){
var a = values[0];
for(var i=1; i < values.length;i++){
var b = values[i];
if(b.value > 1){
a.notUnique += b.value;
}
a.sum += b.value;
}
return a;
};
final = function(key,reducedValue){
var result = {unique: 0,notUnique: 0};
result.unique = reducedValue.sum - reducedValue.notUnique;
result.notUnique = reducedValue.notUnique;
return result;
}
print("Statistic for Polish sentences\n");
var result = resultPol.mapReduce(map, reduce, {out: {inline: 1}, finalize: final});
printjson(result);
print("Statistic for English sentences\n");
var result2 = resultEng.mapReduce(map, reduce, {out: {inline: 1}, finalize: final});
printjson(result2);Otrzymany wynik:
Statistic for Polish sentences:
{
"results" : [
{
"_id" : "answer",
"value" : {
"unique" : 27613,
"notUnique" : 321
}
}
],
"timeMillis" : 616,
"counts" : {
"input" : 27771,
"emit" : 27771,
"reduce" : 278,
"output" : 1
},
"ok" : 1
}
Statistic for English sentences:
{
"results" : [
{
"_id" : "answer",
"value" : {
"unique" : 285367,
"notUnique" : 15753
}
}
],
"timeMillis" : 6348,
"counts" : {
"input" : 292684,
"emit" : 292684,
"reduce" : 2927,
"output" : 1
},
"ok" : 1
}Teraz policzymy jaki rozkład liter najczęsciej występuje w sentencjach.
Kod pliku:
polishCollection = db.result2Pol;
englishCollection = db.result2Eng;
map = function () {
var x = { count : this.value.count , _id : this._id };
emit("maximum", { max : x } )
}
reduce = function(key,values){
var res = values[0];
for ( var i=1; i<values.length; i++ ) {
if ( values[i].max.count > res.max.count )
res.max = values[i].max;
}
return res;
}
var result = polishCollection.mapReduce(map, reduce, {out: {inline: 1}});
printjson(result);
var result2 = englishCollection.mapReduce(map, reduce, {out: {inline: 1}});
printjson(result2);Otrzymany wynik:
Maximum of polish sentences:
{
"results" : [
{
"_id" : "maximum",
"value" : {
"max" : {
"count" : 4,
"_id" : "Nmontp"
}
}
}
],
"timeMillis" : 952,
"counts" : {
"input" : 27771,
"emit" : 27771,
"reduce" : 278,
"output" : 1
},
"ok" : 1
}
Maximum of english sentences:
{
"results" : [
{
"_id" : "maximum",
"value" : {
"max" : {
"count" : 20,
"_id" : "DytTiu"
}
}
}
],
"timeMillis" : 9915,
"counts" : {
"input" : 292684,
"emit" : 292684,
"reduce" : 2927,
"output" : 1
},
"ok" : 1
}Sprawdźmy jakie zdania mają podane rozkłady: Rozkład Nmontp:
{
"_id": "Nmontp",
"value": {
"list": [
"Nie ma odpowiedzi na twoje pytanie.",
"Nie musisz odpowiadać na to pytanie.",
"Nie musisz odpowiadać na te pytania.",
"Nie mamy odpowiedzi na to pytanie."
],
"count": 4
}
}Rozkład DytTiu:
{
"_id": "DytTiu",
"value": {
"list": [
"Do you think Tom is ugly?",
"Do you think Tom is unapproachable?",
"Do you think Tom is unbiased?",
"Do you think Tom is unfair?",
"Do you think Tom is unfriendly?",
"Do you think Tom is unhappy?",
"Do you think Tom is uninteresting?",
"Do you think Tom is unkind?",
"Do you think Tom is unlucky?",
"Do you think Tom is unpleasant?",
"Do you think Tom is unpredictable?",
"Do you think Tom is unprejudiced?",
"Do you think Tom is unprincipled?",
"Do you think Tom is unreliable?",
"Do you think Tom is unscrupulous?",
"Do you think Tom is unsociable?",
"Do you think Tom is untidy?",
"Do you think Tom is untrustworthy?",
"Do you think Tom is upbeat?",
"Do you think Tom is unsophisticated?"
],
"count": 20
}
}Policzymy tu wyłącznie samogłoski angielskie, ponieważ dodatkowe polskie samogłoski rzadko występowały jako pierwsza litera w słowie.
Kod pliku:
polishCollection = db.polishSentences;
englishCollection = db.englishSentences;
map = function(){
var sam = 0;
var spol = 0;
var split = this.originalText.toLowerCase().match(/\b\w/g);
var result = split.join("");
var r = result.match(/[aeiou]/g);
var s = result.match(/[^aeiou]/g);
if(r){
sam = r.length;
}
if(s){
spol = s.length;
}
emit("answer",{samo: sam,spol: spol});
};
reduce = function(key,values){
var a = values[0];
for(var i=1; i < values.length;i++){
var b = values[i];
a.samo += b.samo;
a.spol += b.spol;
}
return a;
};
var a = polishCollection.mapReduce(map, reduce, {out: {inline: 1}});
var b = englishCollection.mapReduce(map, reduce, {out: {inline: 1}});
print "Polish sentences:\n"
printjson(a);
print "English sentences:\n"
printjson(b);Otrzymany wynik:
Polish sentences:
{
"results" : [
{
"_id" : "answer",
"value" : {
"samo" : 37873,
"spol" : 211243
}
}
],
"timeMillis" : 884,
"counts" : {
"input" : 27934,
"emit" : 27934,
"reduce" : 280,
"output" : 1
},
"ok" : 1
}
English sentences:
{
"results" : [
{
"_id" : "answer",
"value" : {
"samo" : 587866,
"spol" : 1892526
}
}
],
"timeMillis" : 8452,
"counts" : {
"input" : 301120,
"emit" : 301120,
"reduce" : 3012,
"output" : 1
},
"ok" : 1
}| Kolekcja | letters.js | split.js | splitWithSentence.js | distributions.js | maxNotUnique.js | aeiou.js |
|---|---|---|---|---|---|---|
| polishSentences | 4 s | 1.5 s | 1.7 s | 0.6 s | 1 s | 0.9 s |
| englishSentences | 41 s | 16 s | 19 s | 6.3 s | 10 s | 8.5 s |
| Kolekcja | split.js | splitWithSentence.js | distributions.js | maxNotUnique.js | aeiou.js |
|---|---|---|---|---|---|
| polishSentences | 4.3 s | 4.8 s | 0.6 s | 0.7 s | 0.8 s |
| englishSentences | 45 s | 51 s | 6.3 s | 7.8 s | 8.5 s |
Będziemy badać unikalność oraz czas wykonania skryptów w MongoDB.
Usuniemy z sentencji stop słowa i sprawdzimy unikalność rozkładów liter. Jednak musimy zmodyfikować aby po usunięciu stop-słów nadal będziemy mieli nasz wymagany przedział 6-12 słów. Przygotowanie i załadowanie danych wykonuje skrypt LoadDataStop.sh.
| Kolekcja | splitSort.js | splitWithSentence.js | distributions.js | maxNotUnique.js | aeiou.js |
|---|---|---|---|---|---|
| polishSentences | 1.5 s | 1.8 s | 0.6 s | 0.7 s | 0.9 s |
| englishSentences | 17 s | 19 s | 6 s | 7 s | 8.3 s |
| Kolekcja | splitToLower.js | splitWithSentence.js | distributions.js | maxNotUnique.js | aeiou.js |
|---|---|---|---|---|---|
| polishSentences | 1.5 s | 1.8 s | 0.6 s | 0.7 s | 0.9 s |
| englishSentences | 16 s | 19 s | 6 s | 7 s | 8.3 s |
| Kolekcja | splitToLower.js | splitWithSentence.js | distributions.js | maxNotUnique.js | aeiou.js |
|---|---|---|---|---|---|
| polishSentences | 1.7 s | 1.7 s | 0.6 s | 0.7 s | 0.9 s |
| englishSentences | 18 s | 18 s | 6 s | 7 s | 8.3 s |
Poniższe tabele pokazują liczbę unikalnych i nieunikalnych rozkładów w danym przypadku. Jeśli chodzi o orginalny przypadek to mamy 27934 sentencje polskie oraz 301120 sentencji angielskich. Gorzej jeśli chodzi o sentencję bez stop słów. Tutaj mamy 6242 sentencji polskich oraz 44565 sentencji angielskich. Poza tym obliczyłem prawdopodobieństwo że dana sentencja jest unikalna lub nieunikalna.
Dla języka polskiego:
| Orginalny przypadek | Bez stop słów | Posortowanie liter | Nierozróżnianie wielkości liter | Nierozróżnianie liter + posortowanie | |
|---|---|---|---|---|---|
| Unikalne rozkłady | 27613 | 6206 | 27372 | 27586 | 27366 |
| Nieunikalne rozkłady | 321 | 36 | 562 | 348 | 568 |
| Pr(X=nieunikalny) | 0.01 | 0.01 | 0.02 | 0.01 | 0.02 |
| Pr(X=unikalny | 0.99 | 0.99 | 0.98 | 0.99 | 0.98 |
Dla języka angielskiego:
| Orginalny przypadek | Bez stop słów | Posortowanie liter | Nierozróżnianie wielkości liter | Nierozróżnianie liter + posortowanie | |
|---|---|---|---|---|---|
| Unikalne rozkłady | 285367 | 43194 | 254439 | 283595 | 254210 |
| Nieunikalne rozkłady | 15753 | 1371 | 46681 | 17525 | 46910 |
| Pr(X=nieunikalny) | 0.05 | 0.03 | 0.16 | 0.06 | 0.16 |
| Pr(X=unikalny) | 0.95 | 0.97 | 0.84 | 0.94 | 0.84 |
Ponieważ skrypty zajmują trochę czasu postanowiłem zoptymalizować je pod względem szybkości. Opieram się tylko tutaj o jedną procedurę MapReduce zawarta w pliku split.js.
UWAGA Poniższe skrypty są zapisane w folderze faster_mapReduce.
Należy lekko zmodyfikować program. Zamiast
<nazwa kolekcji>.mapreduce()użyjemy polecenia:
<nazwa kolekcji>.runCommand()Kod skryptu:
polishCollection = db.polishSentences;
englishCollection = db.englishSentences;
map = function(){
var split = this.originalText.match(/\b\w/g);
var result = split.join("");
emit(result,1);
};
reduce = function(key,values){
return Array.sum(values);
};
var resultPolish = db.runCommand({
mapreduce: "polishSentences",
map: map,
reduce: reduce,
out: "resultFast",
sort: {originalText:1},
jsMode: true
})
printjson(resultPolish);
var resultEnglish = db.runCommand({
mapreduce: "englishSentences",
map: map,
reduce: reduce,
out: "resultFast2",
sort: {originalText:1},
jsMode: true
})
printjson(resultEnglish);Otrzymany rezultat jest następujący:
{
"result" : "resultFast",
"timeMillis" : 1364,
"counts" : {
"input" : 27934,
"emit" : 27934,
"reduce" : 158,
"output" : 27771
},
"ok" : 1
}
{
"result" : "resultFast2",
"timeMillis" : 14244,
"counts" : {
"input" : 301120,
"emit" : 301120,
"reduce" : 7317,
"output" : 292684
},
"ok" : 1
}Jeśli dane można łatwo podzielić na zbiory to możemy użyć wówczas nieużywanych rdzeni procesora. Każdy z nich będzie przetwarzał jakąś część danych i zapisywał je do danej kolekcji.
UWAGA 1 Aby użyć wielowątkowości w MongoDB należy załadować plik parallelTest.js, gdyż w nim zawarta jest procedura tworząca nowy wątek w MongoDB. Informacja o tym zawarta jest tutaj
UWAGA 2 Aby wielowątkowość działała poprawnie, funkcje map oraz reduce muszą być ściśle zaimplementowane w poleceniu runCommand() Dany proces wykonuje następujący skrypt:
polishCollection = db.polishSentences;
englishCollection = db.englishSentences;
//This file is very important to run threads in MongoDB
load("parallelTester.js");
//Split data to run in threads
var res = db.runCommand({
splitVector: "test.englishSentences",
keyPattern: {_id: 1},
maxChunkSizeBytes: 4*1024*1024
});
var keys = res.splitKeys;
//MapReduce command
var command = function(min,max){
return db.runCommand({
mapreduce: "englishSentences",
map: function(){
var split = this.originalText.match(/\b\w/g);
var result = split.join("");
emit(result,1);
},
reduce: function(key,values){ return Array.sum(values)},
out: "resultParallel" + min,
sort: {_id:1},
query: {_id: {$gte: min, $lt: max}},
})};
var numberCores = 4
var inc = (Math.floor(keys.length) / numberCores) + 1;
threads = [];
//Run MapReduce in threads
for (var i = 0; i < numberCores; ++i) {
var min = (i == 0) ? 0 : keys[i * inc]._id;
var max = (i * inc + inc >= keys.length) ? MaxKey : keys[i * inc + inc]._id ;
print("min:" + min + " max:" + max);
var t = new ScopedThread(command, min, max);
threads.push(t);
t.start()
}
//Print results on threads
for (var i in threads){
var t = threads[i];
t.join();
printjson(t.returnData());
}Otrzymany rezultat jest następujący:
min:0 max:107550
min:107550 max:197175
min:197175 max:286800
min:286800 max:[object MaxKey]
connecting to: test
connecting to: test
connecting to: test
connecting to: test
{
"result" : "resultParallel0",
"timeMillis" : 27971,
"counts" : {
"input" : 107549,
"emit" : 107549,
"reduce" : 1283,
"output" : 106227
},
"ok" : 1
}
{
"result" : "resultParallel107550",
"timeMillis" : 27690,
"counts" : {
"input" : 89625,
"emit" : 89625,
"reduce" : 1532,
"output" : 87992
},
"ok" : 1
}
{
"result" : "resultParallel197175",
"timeMillis" : 27273,
"counts" : {
"input" : 89625,
"emit" : 89625,
"reduce" : 1902,
"output" : 87459
},
"ok" : 1
}
{
"result" : "resultParallel286800",
"timeMillis" : 17881,
"counts" : {
"input" : 14321,
"emit" : 14321,
"reduce" : 120,
"output" : 14201
},
"ok" : 1
}Powtórzymy powyższy eksperyment z różnicą taką iż dane będziemy zapisywać w różnych bazach, a nie jak do tej pory w różnych kolekcjach. Kod różni się tylko wyłącznie w zmiennej command:
var command = function(min,max){
return db.runCommand({
mapreduce: "englishSentences",
map: function(){
var split = this.originalText.match(/\b\w/g);
var result = split.join("");
emit(result,1);
},
reduce: function(key,values){ return Array.sum(values)},
out: {replace: "resultParallel" + min, db: "parDB" + min},
sort: {_id:1},
query: {_id: {$gte: min, $lt: max}},
})};Rezultat jest następujący:
min:0 max:107550
min:107550 max:197175
min:197175 max:286800
min:286800 max:[object MaxKey]
connecting to: test
connecting to: test
connecting to: test
connecting to: test
{
"result" : {
"db" : "parDB0",
"collection" : "resultParallel0"
},
"timeMillis" : 9554,
"counts" : {
"input" : 107549,
"emit" : 107549,
"reduce" : 1283,
"output" : 106227
},
"ok" : 1
}
{
"result" : {
"db" : "parDB107550",
"collection" : "resultParallel107550"
},
"timeMillis" : 9121,
"counts" : {
"input" : 89625,
"emit" : 89625,
"reduce" : 1532,
"output" : 87992
},
"ok" : 1
}
{
"result" : {
"db" : "parDB197175",
"collection" : "resultParallel197175"
},
"timeMillis" : 8880,
"counts" : {
"input" : 89625,
"emit" : 89625,
"reduce" : 1902,
"output" : 87459
},
"ok" : 1
}
{
"result" : {
"db" : "parDB286800",
"collection" : "resultParallel286800"
},
"timeMillis" : 1666,
"counts" : {
"input" : 14321,
"emit" : 14321,
"reduce" : 120,
"output" : 14201
},
"ok" : 1
}Eksperyment został wykonany tylko na jednym procesie MapReduce, którym jest split.js na kolekcji englishSentences.
| Proces optymalizacji | Bez optymalizacji | JSMode | Zapis do różnych kolekcji | Zapis do różnych baz i kolekcji |
|---|---|---|---|---|
| Czas | 16 s | 14 s | 27 sekund na jeden rdzeń | 9 sekund na jeden rdzeń |
- Jeśli chodzi o rozkład pierwszych liter to samogłoski stanowią około 25% liter.
- Dla otrzymanych danych usunięcie stop słów diametralnie zmniejszyło jego ilość, aby otrzymać zdanie składające się z 6-12 słów.
- Jeśli chodzi o język angielski to prawdopodobieństwo że ktoś będzie miał takie same hasło co my wygenerowaną daną metodą jest niewielkie. Chyba że nie będziemy używać dużych liter lub co jest jeszcze gorsze posortujemy sobie te litery.
- Dla języka polskiego jest bardzo mało nieunikalnych rozkładów, ale zbiór testowy był niewielki.
- Jeśli do podanej metody dodamy jeszcze występowanie pomiędzy literami znaków specjalnych czy dodawaniu większej ilości dużych liter czy nawet liczb to nasze hasło nie powinno być tak łatwo odgadnięte.
- Jeśli chodzi o MongoDB 2.8.0.rc4 to MapReduce wykonuje się generalnie wolniej.
- Jeśli chodzi o proces optymalizacji MapReduce to opłaca się używać trybu JSMode oraz wielowątkowości do zapisu danych w różnych bazach.
- Wielowątkowość nie zawsze oznacza szybciej ponieważ należy doliczyć do tego komunikację między procesami oraz koszt podzielenia danych.


