MongoDB nedávno představil svou novou agregační strukturu. Tato struktura poskytuje jednodušší řešení pro výpočet agregovaných hodnot namísto spoléhání se na výkonné struktury s redukovanou mapou.
Pomocí několika jednoduchých primitiv vám umožňuje vypočítat, seskupit, tvarovat a navrhnout dokumenty, které jsou obsaženy v konkrétní kolekci MongoDB. Zbytek tohoto článku popisuje refaktoring algoritmu redukce mapy pro optimální využití nové agregační platformy MongoDB. Úplný zdrojový kód lze nalézt ve veřejně dostupném úložišti Datatablend GitHub.
1. Struktura agregace MongoDB
Agregační platforma MongoDB je založena na známém konceptu Linux Pipeline, kde je výstup jednoho příkazu přenášen přes dopravník nebo přesměrován, aby byl použit jako vstup pro další příkaz . V případě MongoDB je několik operátorů spojeno do jediného dopravníku, který je zodpovědný za zpracování toku dokumentů.
Některé operátory jako $ match, $ limit a $ přeskakují přijetí dokumentu jako vstupu a výstupu stejného dokumentu, pokud je splněna určitá sada kritérií. Ostatní operátoři, jako $ project a $ unwind, přijímají jako vstupní data jeden dokument a mění jeho formát nebo tvoří několik dokumentů na základě určité projekce.
Operátor skupiny $ nakonec přijme několik dokumentů jako vstupní data a seskupí je do jednoho dokumentu kombinací odpovídajících hodnot. Výrazy lze v některých z těchto operátorů použít k výpočtu nových hodnot nebo provádění operací s řetězci.
Několik operátorů je sloučeno do jednoho kanálu, který platí pro seznam dokumentů. Samotný dopravník je spuštěn jako příkaz MongoDB, což vede k jedinému dokumentu MongoDB, který obsahuje pole všech dokumentů, které vyšly na konci dopravníku. Následující odstavec podrobně popisuje refaktorizační algoritmus molekulární podobnosti jako dopravníku operátorů. Nezapomeňte si (znovu) přečíst předchozí dva články, abyste plně porozuměli logice implementace.
2. Potrubí molekulární podobnost
Při použití dopravníku na konkrétní kolekci jsou všechny dokumenty obsažené v této kolekci předány jako vstup prvnímu operátorovi. Doporučujeme tento seznam co nejrychleji filtrovat, abyste omezili počet dokumentů, které jsou přenášeny prostřednictvím kanálu. V našem případě to znamená filtrovat celý dokument, který nikdy nesplní cílový faktor Tanimoto.
Proto v prvním kroku porovnáme všechny dokumenty, u kterých je počet otisků prstů v určitém limitu. Pokud cílíme na faktor Tanimoto 0,8 s cílovým připojením obsahujícím 40 unikátních otisků prstů, operátor $ match bude vypadat takto:
{"$match" :
{ "fingerprint_count" : {"$gte" : 32, "$lte" : 50}}.
}
Pouze spojení s počtem otisků prstů od 32 do 50 budou převedena na dalšího provozovatele potrubí. K provedení tohoto filtrování může operátor $ match použít index, který jsme definovali pro vlastnost fingerprint_count. K výpočtu Tanimotova koeficientu potřebujeme vypočítat počet společných otisků prstů mezi určitým vstupním připojením a cílovým připojením, na které cílíme.
Pro práci na úrovni otisku prstu používáme operátor $ unwind. $ unwind odstraní prvky pole jeden po druhém a vrátí proud dokumentu, ve kterém je zadané pole nahrazeno jedním z jeho prvků. V našem případě aplikujeme $ unwind na otisky prstů. V důsledku toho bude výsledkem každého složeného dokumentu n složených dokumentů, kde n je počet jedinečných otisků prstů obsažených ve složeném dokumentu.
{"$unwind" :"$fingerprints"}
Abychom vypočítali počet běžných otisků prstů, začneme filtrováním všech dokumentů, které nemají otisky, které jsou na seznamu otisků prstů cílového připojení. K tomu znovu použijeme operátor $ match, tentokrát filtrování vlastnosti otisku prstu, kde jsou podporovány pouze dokumenty obsahující otisk prstu, který je v cílovém seznamu otisků prstů.
{"$match" :
{ "fingerprints" :
{"$in" : [ 1960 , 15111 , 5186 , 5371 , 756 , 1015 , 1018 , 338 , 325 , 776 , 3900 , ..., 2473] }
}
}
Protože porovnáváme pouze otisky prstů, které jsou v cílovém seznamu otisků prstů, lze výstup použít k výpočtu celkového počtu běžných otisků prstů.
K tomu použijeme operátor skupiny $ na složené spojení, i když vytvoříme nový typ dokumentu obsahující počet odpovídajících otisků prstů (sečtením počtu výskytů), celkový počet otisků vstupních připojení a smajlíky.
{"$group" :
{ "_id" : "$compound_cid". ,
"fingerprintmatches" : {"$sum" : 1} ,
"totalcount" : { "$first" : "$fingerprint_count"} ,
"smiles" : {"$first" : "$smiles"}
}
}
Nyní máme všechny parametry pro výpočet Tanimotova koeficientu. K tomu použijeme operátor projektu $, který kromě zkopírování složené vlastnosti id a smiles přidá také nově vypočítanou vlastnost s názvem Tanimoto.
{
"$project"
:
{
"_id"
:
1
,
"tanimoto"
:
{
"$divide"
:
[
"$fingerprintmatches."
,
{
"$subtract"
:
[
{
"$add"
:
[
40
,
"$totalcount"
]
}
,
"$fingerprintmatches."
]
}
]
}
,
"smiles"
:
1
}
}
Protože nás zajímají pouze připojení, která mají cílový koeficient Tanimoto 0,8, používáme volitelný operátor $ match k odfiltrování všech, která tohoto koeficientu nedosahují.
{"$match" :
{ "tanimoto" : { "$gte" : 0.8}
}
Příkaz kompletního potrubí lze nalézt níže.
{"aggregate" : "compounds"} ,
"pipeline" : [
{"$match" :
{ "fingerprint_count" : {"$gte" : 32, "$lte" : 50} }
},
{"$unwind" : "$fingerprints"},
{"$match" :
{ "fingerprints" :
{"$in" : [ 1960 , 15111 , 5186 , 5371 , 756 , 1015 , 1018 , 338 , 325 , 776 , 3900,... , 2473] }
}
},
{"$group" :
{ "_id" : "$compound_cid" ,
"fingerprintmatches" : {"$sum" : 1} ,
"totalcount" : { "$first" : "$fingerprint_count"} ,
"smiles" : {"$first" : "$smiles"}
}
},
{"$project" :
{ "_id" : 1 ,
"tanimoto" : {"$divide" : ["$fingerprintmatches"] , { "$subtract" : [ { "$add" : [ 89 , "$totalcount"]} , "$fingerprintmatches"] }. ] } ,
"smiles" : 1
}
},
{"$match" :
{"tanimoto" : {"$gte" : 0.05} }
} ]
}
Výstup tohoto kanálu obsahuje seznam připojení, která mají Tanimoto 0.8 nebo vyšší ve vztahu ke konkrétnímu cílovému připojení. Vizuální znázornění tohoto dopravníku lze nalézt níže:
3. Závěr
Nová agregační struktura MongoDB poskytuje sadu snadno použitelných operátorů, které uživatelům umožňují stručněji vyjádřit algoritmy typu redukce karet. Koncept dopravníku pod ním nabízí intuitivní způsob zpracování dat.
Není překvapením, že toto paradigma potrubí je převzato různými přístupy NoSQL, včetně Gremlin Framework Tinkerpop v implementaci a Cypher Neo4j v implementaci.
Z hlediska výkonu představuje řešení potrubí výrazné zlepšení při implementaci redukčních map.
Operátoři jsou zpočátku podporováni platformou MongoDB, což vede k výraznému zlepšení výkonu oproti interpretovanému Javascriptu. Vzhledem k tomu, že Aggregation Framework může fungovat i v izolovaném prostředí, snadno překročí výkon mé původní implementace, zvláště když je počet vstupních připojení vysoký a cíl Tanimoto je nízký. Vynikající výkon příkazu MongoDB!