sql >> Databáze >  >> NoSQL >> MongoDB

Nalezení dvou dokumentů v MongoDB, které sdílejí klíčovou hodnotu

I když stojím za komentáři, že si nemyslím, že způsob, jakým formulujete svou otázku, ve skutečnosti souvisí s konkrétním problémem, který máte, půjdu nějak vysvětlit idiomatický způsob SQL v řešení typu MongoDB. Stojím si za tím, že vaše skutečné řešení by bylo jiné, ale nepředložil jste nám tento problém, ale pouze SQL.

Zvažte tedy následující dokumenty jako vzorovou sadu, z důvodu přehlednosti odeberte pole _id v tomto seznamu:

{ "name" : "a", "type" : "b" }
{ "name" : "a", "type" : "c" }
{ "name" : "b", "type" : "c" }
{ "name" : "b", "type" : "a" }
{ "name" : "a", "type" : "b" }
{ "name" : "b", "type" : "c" }
{ "name" : "f", "type" : "e" }
{ "name" : "z", "type" : "z" }
{ "name" : "z", "type" : "z" }

Pokud bychom spustili SQL prezentované přes stejná data, dostali bychom tento výsledek:

a|b
a|c
a|c
b|c
b|a
b|a
a|b
b|c

Vidíme, že se 2 dokumenty neshodují, a pak vyřešíme logiku operace SQL. Jiný způsob, jak to říci, je tedy „Jaké dokumenty s klíčem „jméno“ dělat mít více než jeden možná hodnota v klíči "type".

Vzhledem k tomu, že použijeme mongo přístup, můžeme se dotazovat na položky, které nedělají odpovídat danému stavu. Tedy efektivně zpětně výsledku:

db.sample.aggregate([

    // Store unique documents grouped by the "name"
    {$group: { 
        _id: "$name",
        comp: {
            $addToSet: { 
                name:"$name",
                type: "$type" 
            }
        } 
    }},

    // Unwind the "set" results
    {$unwind: "$comp"},

    // Push the results back to get the unique count
    // *note* you could not have done this with alongside $addtoSet
    {$group: {
        _id: "$_id",
        comp: {
            $push: { 
                name: "$comp.name",
                type: "$comp.type" 
            }
        },
        count: {$sum: 1} 
    }},

    // Match only what was counted once
    {$match: {count: 1}},

    // Unwind the array
    {$unwind: "$comp"},

    // Clean up to "name" and "type" only
    {$project: { _id: 0, name: "$comp.name", type: "$comp.type"}}

])

Tato operace přinese výsledky:

{ "name" : "f", "type" : "e" }
{ "name" : "z", "type" : "z" }

Nyní, abychom získali stejný výsledek jako SQL dotaz, vezmeme tyto výsledky a nasměrujeme je do jiného dotazu:

db.sample.find({$nor: [{ name: "f", type: "e"},{ name: "z", type: "z"}] })

Což přijde jako konečný výsledek shody:

{ "name" : "a", "type" : "b" }
{ "name" : "a", "type" : "c" }
{ "name" : "b", "type" : "c" }
{ "name" : "b", "type" : "a" }
{ "name" : "a", "type" : "b" }
{ "name" : "b", "type" : "c" }

Takže to bude fungovat, ale jedna věc, která to může dělat nepraktickým, je porovnávání počtu dokumentů je velmi velký, narazili jsme na pracovní limit při komprimaci těchto výsledků na pole.

Trochu také trpí používáním negativu v konečné operaci hledání, která by si vynutila skenování sbírky. Ale ve vší spravedlnosti by se totéž dalo říci o dotazu SQL, který používá stejné negativní předpoklad.

Upravit

Samozřejmě, co jsem nezmínil, je to, že pokud je výsledná sada obrácená a vy odpovídáte více výsledkem jsou vyloučené položky z agregace, pak stačí obrátit logiku, abyste získali klíče, které chcete. Jednoduše změňte $match následovně:

{$match: {$gt: 1}}

A to bude výsledek, možná ne skutečné dokumenty, ale je to výsledek. Nepotřebujete tedy další dotaz, který by odpovídal negativním případům.

A nakonec to byla moje chyba, protože jsem se tak soustředil na idiomatický překlad, že jsem nečetl poslední řádek ve vaší otázce, kde dělat řekněte, že jste hledali jednoho dokument.

Samozřejmě, aktuálně pokud je výsledná velikost větší než 16 MB, pak jste uvízli. Minimálně do 2.6 vydání, kde výsledky agregačních operací jsou $size operátor, který se používá k nalezení velikosti pole v dokumentu. Takže by to pomohlo odstranit druhý $unwind a $group které se používají k získání délky sady. Tím se dotaz změní na rychlejší formu:

db.sample.aggregate([
    {$group: { 
        _id: "$name",
        comp: {
            $addToSet: { 
                name:"$name",
                type: "$type"
            }
        } 
    }},
    {$project: { 
        comp: 1,
        count: {$size: "$comp"} 
    }},
    {$match: {count: {$gt: 1}}},
    {$unwind: "$comp"},
    {$project: { _id: 0, name: "$comp.name", type: "$comp.type"}}
])

A MongoDB 2.6.0-rc0 je aktuálně k dispozici, pokud to děláte pouze pro osobní použití nebo vývoj/testování.

Morálka příběhu. Ano, můžete udělej to, Ale opravdu opravdu chtít nebo potřebovat udělat to tak? Pak pravděpodobně ne, a pokud jste položili jinou otázku týkající se konkrétního obchodního případu, můžete dostat jinou odpověď. Ale opět to může být přesně to pravé pro to, co chcete.

Poznámka

Za zmínku stojí, že když se podíváte na výsledky z SQL, dojde k chybné duplikaci několik položek kvůli dalším dostupným možnostem typu, pokud jste nepoužili DISTINCT pro tyto hodnoty nebo v podstatě jiné seskupení. Ale to je výsledek, který byl vytvořen tímto procesem pomocí MongoDB.

Pro Alexandra

Toto je výstup agregátu v shellu ze současných verzí 2.4.x:

{
    "result" : [
            {
                    "name" : "f",
                    "type" : "e"
            },
            {
                    "name" : "z",
                    "type" : "z"
            }
    ],
    "ok" : 1
}

Udělejte to, aby se var předal jako argument podmínce $nor ve druhém nálezu, takto:

var cond = db.sample.aggregate([ .....

db.sample.find({$nor: cond.result })

A měli byste získat stejné výsledky. V opačném případě se obraťte na svého řidiče.



  1. Jak zabezpečit své open source databáze pomocí ClusterControl

  2. ReactiveMongo:Jak převést BSON vrácený FindAndModify na JSON

  3. Seřadit podle data řetězce (vzestupně) na Mongo

  4. Jak zabránit _t a _v při vkládání do MongoDB?