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

Přiřaďte alespoň N prvků pole k seznamu podmínek

Vaše otázka má pro mě dvě možnosti, ale možná nějaké vysvětlení, abyste mohli začít.

Nejprve vám musím vysvětlit, že nerozumíte záměru $elemMatch a v tomto případě je to zneužito.

Myšlenka $elemMatch je vytvořit "dotazový dokument", který je ve skutečnosti aplikován na prvky pole. Záměrem je, kde máte "více podmínek" na dokumentu v rámci pole, aby se diskrétně shodoval v rámci členského dokumentu, a ne v rámci celého pole vnějšího dokumentu. tj.:

{
   "data": [
       { "a": 1, "b": 3 },
       { "a": 2, "b": 2 }
   ]
}

A následující dotaz bude fungovat, i když žádný skutečný jednotlivý prvek v tomto poli neodpovídá, ale celý dokument ano:

db.collection.find({ "data.a": 1, "data.b": 2 })

Chcete-li však zkontrolovat, zda skutečný prvek odpovídá oběma těmto podmínkám, použijte $elemMatch :

db.collection.find({ "data": { "a": 1, "b": 2 } })

V tomto vzorku tedy žádná shoda a bude se shodovat pouze tam, kde konkrétní prvek pole měl oba tyto prvky.

Nyní máme $elemMatch vysvětleno, zde je váš zjednodušený dotaz:

db.collection.find({ "tracks.artist": { "$in": arr } })

Mnohem jednodušší a funguje to tak, že se podíváte na všechny členy pole podle jednoho pole a vrátíte se tam, kde jakýkoli prvek v dokumentu obsahuje alespoň jeden z těchto možných výsledků.

Ale ne to, na co se ptáte, tak dále s vaší otázkou. Pokud si přečtete toto poslední prohlášení, měli byste si uvědomit, že $in je ve skutečnosti $or stav. Je to jen zkrácená forma pro dotaz na „nebo“ na stejný prvek v dokumentu.

S ohledem na to je jádrem toho, co žádáte, "a" operace, kde jsou obsaženy všechny "tři" hodnoty. Za předpokladu, že jste v testu posílali pouze „tři“ položky, můžete použít formu $and který je ve zkrácené podobě $all :

db.collection.find({ "tracks.artist": { "$all": arr } })

To by vám vrátilo pouze dokumenty, které měly prvek v rámci členů tohoto pole odpovídající "všem" prvkům zadaným v testovací podmínce. To může být to, co chcete, ale existuje případ, kdy samozřejmě chcete zadat seznam řekněme „čtyř nebo více“ umělců k testování a chcete z toho pouze „tři“ nebo nějaké menší číslo, v tom případě $all operátor je příliš stručný.

Existuje však logický způsob, jak to vyřešit, jen to vyžaduje trochu více zpracování s operátory, které nejsou dostupné pro základní dotazy, ale které jsou dostupné pro rámec agregace :

var arr = ["A","B","C","D"];     // List for testing

db.collection.aggregate([
    // Match conditions for documents to narrow down
    { "$match": {
        "tracks.artist": { "$in": arr },
        "tracks.2": { "$exists": true }      // you would construct in code
    }},

    // Test the array conditions
    { "$project": {
        "user": 1,
        "tracks": 1,                         // any fields you want to keep
        "matched": {
            "$gte": [
                 { "$size": {
                     "$setIntersection": [
                         { "$map": {
                             "input": "$tracks",
                             "as": "t",
                             "in": { "$$t.artist" }
                         }},
                         arr
                     ]
                 }},
                 3
             ]
        }
    }},

    // Filter out anything that did not match
    { "$match": { "matched": true } }
])

První fáze implementuje standardní dotaz $match podmínku, abyste vyfiltrovali dokumenty pouze na ty, které „pravděpodobně“ budou odpovídat podmínkám. Logickým případem je použití $in stejně jako dříve najde ty dokumenty, kde je alespoň jeden z prvků přítomných ve vašem "testovacím" poli přítomen alespoň v jednom z členských polí ve vlastním poli dokumentů.

Další klauzule je něco, co byste v ideálním případě měli vytvářet v kódu, protože se týká „délky“ pole. Myšlenka je, že chcete alespoň „tři“ shody, pak pole, které testujete v dokumentu, musí mít alespoň „tři“ prvky, aby to splnilo, takže nemá smysl získávat dokumenty se „dvěma“ nebo méně prvky pole. protože se nikdy nemohou shodovat se „tři“.

Vzhledem k tomu, že všechny dotazy MongoDB jsou v podstatě pouze reprezentací datové struktury, je to velmi snadné sestavit. tj. pro JavaScript:

var matchCount = 3;    // how many matches we want

var match1 = { "$match": { "tracks.artist": { "$in": arr } } };

match1["$match"]["tracks."+ (matchCount-1)] = { "$exits": true };

Logika je taková, že „tečkový zápis“ tvoří $exists testuje přítomnost prvku na zadaném indexu ( n-1 ) a musí tam být, aby pole mělo alespoň tuto délku.

Zbytek zúžení ideálně používá $setIntersection metoda, aby se vrátily odpovídající prvky mezi skutečným polem a testovaným polem. Protože pole v dokumentu neodpovídá struktuře pro "testovací pole", je třeba jej transformovat pomocí $map operace, která je nastavena tak, aby vracela pouze pole "artist" z každého prvku pole.

Protože je vytvořen "průnik" těchto dvou polí, je nakonec testován pro $size výsledného seznamu společných prvků, kde je test aplikován, aby se zjistilo, že "nejméně tři" z těchto prvků byly společné.

Nakonec jednoduše „odfiltrujete“ vše, co nebyla pravda, pomocí $match podmínka.

V ideálním případě používáte MongoDB 2.6 nebo vyšší, abyste měli tyto operátory k dispozici. U dřívějších verzí 2.2.xa 2.4.x je to stále možné, ale jen trochu více práce a režie zpracování:

db.collection.aggregate([
    // Match conditions for documents to narrow down
    { "$match": {
        "tracks.artist": { "$in": arr },
        "tracks.2": { "$exists": true }      // you would construct in code
    }},

    // Unwind the document array
    { "$unwind": "$tracks" },

    // Filter the content
    { "$match": { "tracks.artist": { "$in": arr } }},

    // Group for distinct values
    { "$group": {
        "_id": { 
           "_id": "$_id",
           "artist": "$tracks.artist"
        }
    }},

    // Make arrays with length
    { "$group": {
        "_id": "$_id._id",
        "artist": { "$push": "$_id.artist" },
        "length": { "$sum": 1 }
    }},

    // Filter out the sizes
    { "$match": { "length": { "$gte": 3 } }}
])



  1. Uložte data XML do kolekce mongodb

  2. Použití Mongo / BSON ObjectId s Parse Server

  3. Jak odstranit vnořený objekt na základě jeho ObjectId?

  4. Mongoose - Nelze vytvořit více než 4 pole pomocí `findOrCreate`