sql >> Databasteknik >  >> NoSQL >> MongoDB

Förbättra MongoDB-aggregationsstruktur

MongoDB introducerade nyligen sin nya aggregeringsstruktur. Denna struktur ger en enklare lösning för att beräkna aggregerade värden snarare än att förlita sig på kraftfulla strukturer med en reducerad karta.

Med bara några enkla primitiver låter den dig beräkna, gruppera, forma och designa dokument som finns i en viss MongoDB-samling. Resten av den här artikeln beskriver refaktoreringen av kartreduktionsalgoritmen för optimal användning av den nya MongoDB-aggregationsplattformen. Den fullständiga källkoden kan hittas i det allmänt tillgängliga Datablend GitHub-förrådet.

1. MongoDB-aggregationsstruktur

MongoDB-aggregationsplattformen är baserad på det välkända Linux Pipeline-konceptet där utdata från ett kommando sänds via en transportör eller omdirigeras för att användas som indata för nästa kommando . När det gäller MongoDB är flera operatörer kombinerade till en enda transportör som ansvarar för att hantera dokumentflödet.

Vissa operatörer som $ match, $ limit och $ hoppar över att acceptera dokumentet som inmatning och mata ut samma dokument om en viss uppsättning kriterier är uppfyllda. Andra operatörer, som $ project och $ unwind, accepterar ett enstaka dokument som indata och ändrar dess format eller bildar flera dokument baserat på en viss projektion.

Gruppoperatören $ accepterar slutligen flera dokument som indata och grupperar dem i ett dokument genom att kombinera motsvarande värden. Uttryck kan användas i vissa av dessa operatorer för att beräkna nya värden eller utföra strängoperationer.

Flera operatörer kombineras till en enda pipeline, vilket gäller listan med dokument. Själva transportören exekveras som MongoDB-kommandot, vilket resulterar i ett enda MongoDB-dokument, som innehåller en uppsättning av alla dokument som kom ut i slutet av transportören. Nästa stycke beskriver i detalj refaktoreringsalgoritmen för molekylär likhet som en transportör för operatörer. Var noga med att (om)läsa de två föregående artiklarna för att helt förstå implementeringslogiken.

2. Piping av molekylär likhet

När en transportör appliceras på en viss samling, skickas alla dokument i den samlingen som indata till den första operatören. Det rekommenderas att du filtrerar den här listan så snabbt som möjligt för att begränsa antalet dokument som överförs via pipelinen. I vårt fall innebär detta att filtrera hela dokumentet som aldrig kommer att uppfylla Tanimoto-målet.

Därför jämför vi som ett första steg alla dokument där antalet fingeravtryck ligger inom en viss tröskel. Om vi ​​riktar in oss på en Tanimoto-faktor på 0,8 med en målanslutning som innehåller 40 unika fingeravtryck, ser matchningsoperatorn $ ut så här:

{"$match" :
{ "fingerprint_count" : {"$gte" : 32, "$lte" : 50}}.
}

Endast anslutningar med ett antal fingeravtryck från 32 till 50 kommer att överföras till nästa pipelineoperatör. För att utföra denna filtrering kan $ match-operatorn använda indexet som vi definierade för egenskapen fingerprint_count. För att beräkna Tanimoto-koefficienten måste vi beräkna antalet vanliga fingeravtryck mellan en viss ingångsanslutning och målanslutningen vi riktar in oss på.

För att arbeta på fingeravtrycksnivå använder vi $ unwind-operatorn. $ unwind tar bort arrayelementen ett efter ett och returnerar dokumentströmmen där den angivna arrayen ersätts med ett av dess element. I vårt fall tillämpar vi $ unwind på fingeravtryck. Följaktligen kommer varje sammansatt dokument att resultera i n sammansatta dokument, där n är antalet unika fingeravtryck som finns i ett sammansatt dokument.

{"$unwind" :"$fingerprints"}

För att beräkna antalet vanliga fingeravtryck börjar vi med att filtrera alla dokument som inte har de fingeravtryck som finns på fingeravtryckslistan för målanslutningen. För att göra detta använder vi $ match-operatorn igen, denna gång filtrerar vi fingeravtrycksegenskapen, där endast dokument som innehåller ett fingeravtryck som finns i målfingeravtryckslistan stöds.

{"$match" :
{ "fingerprints" :
{"$in" : [ 1960 , 15111 , 5186 , 5371 , 756 , 1015 , 1018 , 338 , 325 , 776 , 3900 , ..., 2473] }
}
}

Eftersom vi bara matchar de fingeravtryck som finns i målfingeravtryckslistan, kan utdata användas för att beräkna det totala antalet vanliga fingeravtryck.

För att göra detta tillämpar vi gruppoperatorn $ på den sammansatta anslutningen, även om vi skapar en ny typ av dokument som innehåller antalet matchande fingeravtryck (genom att lägga ihop antalet förekomster), det totala antalet inmatade anslutningsfingeravtryck och smileys.

{"$group" :
{ "_id" : "$compound_cid". ,
"fingerprintmatches" : {"$sum" : 1} ,
"totalcount" : { "$first" : "$fingerprint_count"} ,
"smiles" : {"$first" : "$smiles"}
}
}

Nu har vi alla parametrar för att beräkna Tanimoto-koefficienten. För att göra detta kommer vi att använda $-projektoperatorn som, förutom att kopiera id och smiles composite-egenskapen, även lägger till en nyligen beräknad egenskap som heter Tanimoto.

{
"$project"
:

{
"_id"
:
1
,

"tanimoto"
:
{
"$divide"
:
[
"$fingerprintmatches."
,
{
"$subtract"
:
[
{
"$add"
:
[
40
,
"$totalcount"
]
}
,
"$fingerprintmatches."
]
}
]
}
,

"smiles"
:
1

}

}

Eftersom vi bara är intresserade av anslutningar som har en Tanimoto-målkoefficient på 0,8, använder vi den valfria matchningsoperatorn $ för att filtrera bort alla de som inte når denna koefficient.

{"$match" :
{ "tanimoto" : { "$gte" : 0.8}
}

Kommandot för hela pipelinen finns nedan.

{"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} }
} ]
}

Utdata från denna pipeline innehåller en lista över anslutningar som har Tanimoto 0.8 eller högre i förhållande till en viss målanslutning. En visuell representation av denna transportör finns nedan:

3. Slutsats

Den nya MongoDB-aggregationsstrukturen tillhandahåller en uppsättning lättanvända operatörer som tillåter användare att uttrycka algoritmer av kortreduktionstyp kortare. Konceptet med en transportör under den erbjuder ett intuitivt sätt att bearbeta data.

Inte överraskande, detta pipeline-paradigm antas av olika NoSQL-metoder, inklusive Gremlin Framework Tinkerpop i implementeringen och Cypher Neo4j i implementeringen.

När det gäller prestanda är rörledningslösningen en betydande förbättring av implementeringen av reduktionskartorna.

Operatörer stöds initialt av MongoDB-plattformen, vilket leder till betydande prestandaförbättringar jämfört med det tolkade Javascriptet. Eftersom Aggregation Framework också kan fungera i en isolerad miljö, överträffar det lätt prestandan för min ursprungliga implementering, speciellt när antalet ingångsanslutningar är högt och Tanimoto-målet är lågt. Utmärkt prestanda från MongoDB-kommandot!

Aggregation | MongoDB | Handledning


  1. bästa praxis för django + PyMongo pooling?

  2. Validerar lösenord / bekräfta lösenord med Mongoose-schema

  3. Lagra objektegenskaper i redis

  4. Exportera en MongoDB-samling till en CSV-fil