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

Vícenásobná limitní podmínka v mongodb

Obecně to, co popisujete, je poměrně častá otázka v komunitě MongoDB, kterou bychom mohli popsat jako „top n problém s výsledky." Toto je, když je zadán nějaký vstup, který je pravděpodobně nějakým způsobem seřazen, jak získat horní n výsledky bez spoléhání se na libovolné hodnoty indexu v datech.

MongoDB má $first operátor, který je dostupný pro rámec agregace který se zabývá "top 1" částí problému, protože to ve skutečnosti bere "první" položku nacházející se na hranici seskupení, jako je váš "typ". Ale získat více než „jeden“ výsledek samozřejmě znamená trochu víc. Existují určité problémy JIRA ohledně úpravy jiných operátorů, aby se zabývali n výsledky nebo „omezit“ nebo „krájet“. Zejména SERVER-6074 . Ale problém lze vyřešit několika způsoby.

Populární implementace vzoru aktivního záznamu kolejnic pro úložiště MongoDB jsou Mongoid a Mongo Mapper , oba umožňují přístup k "nativním" funkcím kolekce mongodb prostřednictvím .collection přístupový prvek. To je to, co v podstatě potřebujete, abyste mohli používat nativní metody, jako je .aggregate() který podporuje více funkcí než obecná agregace aktivních záznamů.

Zde je agregační přístup s mongoidem, i když obecný kód se nezmění, jakmile budete mít přístup k nativnímu objektu kolekce:

require "mongoid"
require "pp";

Mongoid.configure.connect_to("test");

class Item
  include Mongoid::Document
  store_in collection: "item"

  field :type, type: String
  field :pos, type: String
end

Item.collection.drop

Item.collection.insert( :type => "A", :pos => "First" )
Item.collection.insert( :type => "A", :pos => "Second"  )
Item.collection.insert( :type => "A", :pos => "Third" )
Item.collection.insert( :type => "A", :pos => "Forth" )
Item.collection.insert( :type => "B", :pos => "First" )
Item.collection.insert( :type => "B", :pos => "Second" )
Item.collection.insert( :type => "B", :pos => "Third" )
Item.collection.insert( :type => "B", :pos => "Forth" )

res = Item.collection.aggregate([
  { "$group" => {
      "_id" => "$type",
      "docs" => {
        "$push" => {
          "pos" => "$pos", "type" => "$type"
        }
      },
      "one" => {
        "$first" => {
          "pos" => "$pos", "type" => "$type"
        }
      }
  }},
  { "$unwind" =>  "$docs" },
  { "$project" => {
    "docs" => {
      "pos" => "$docs.pos",
      "type" => "$docs.type",
      "seen" => {
        "$eq" => [ "$one", "$docs" ]
      },
    },
    "one" => 1
  }},
  { "$match" => {
    "docs.seen" => false
  }},
  { "$group" => {
    "_id" => "$_id",
    "one" => { "$first" => "$one" },
    "two" => {
      "$first" => {
        "pos" => "$docs.pos",
        "type" => "$docs.type"
      }
    },
    "splitter" => {
      "$first" => {
        "$literal" => ["one","two"]
      }
    }
  }},
  { "$unwind" => "$splitter" },
  { "$project" => {
    "_id" => 0,
    "type" => {
      "$cond" => [
        { "$eq" => [ "$splitter", "one" ] },
        "$one.type",
        "$two.type"
      ]
    },
    "pos" => {
      "$cond" => [
        { "$eq" => [ "$splitter", "one" ] },
        "$one.pos",
        "$two.pos"
      ]
    }
  }}
])

pp res

Pojmenování v dokumentech ve skutečnosti kód nepoužívá a názvy v datech zobrazených pro „První“, „Druhý“ atd. jsou zde skutečně jen pro ilustraci toho, že skutečně získáváte „nejlepší 2“ dokumenty ze seznamu jako výsledek.

Přístup je tedy v podstatě takový, že vytvoříte „hromadu“ dokumentů „seskupených“ podle vašeho klíče, jako je „typ“. Úplně první věcí je vzít „první“ dokument z tohoto zásobníku pomocí $first operátor.

Následující kroky odpovídají „viděným“ prvkům ze zásobníku a filtrují je, poté „další“ dokument ze zásobníku znovu odeberete pomocí $first operátor. Poslední kroky tam jsou ve skutečnosti justx, aby se dokumenty vrátily do původní podoby, jak se nacházejí ve vstupu, což je obecně to, co se od takového dotazu očekává.

Takže výsledkem jsou samozřejmě jen 2 nejlepší dokumenty pro každý typ:

{ "type"=>"A", "pos"=>"First" }
{ "type"=>"A", "pos"=>"Second" }
{ "type"=>"B", "pos"=>"First" }
{ "type"=>"B", "pos"=>"Second" }

V této nedávné odpovědi byla delší diskuse a verze tohoto i dalších řešení:

Agregace Mongodb $group, omezit délku pole

V podstatě to samé navzdory názvu a tento případ se snažil porovnat až 10 nejlepších záznamů nebo více. Existuje také nějaký kód pro generování kanálu pro řešení větších shod a také některé alternativní přístupy, které lze zvážit v závislosti na vašich datech.



  1. pomocí proměnné v aktualizaci mongodb

  2. zobrazující součet všech načtených souborů v ovladači mongodb-nodejs

  3. Jak převést řetězec na ObjectId v nativním ovladači nodejs mongodb?

  4. flushdb nevymaže všechny klíče v redis?