MongoDB
 sql >> база данни >  >> NoSQL >> MongoDB

Условие за множество ограничения в mongodb

Като цяло това, което описвате, е относително често срещан въпрос в общността на MongoDB, който бихме могли да опишем като „топ n проблем с резултатите". Това е, когато се даде някакъв вход, който вероятно е сортиран по някакъв начин, как да получите най-горния n резултати, без да се разчита на произволни стойности на индекса в данните.

MongoDB има $first оператор, който е достъпен за рамката за агрегиране който се занимава с "горната 1" част от проблема, тъй като това всъщност отнема "първия" елемент, намерен на границата на групиране, като например вашия "тип". Но получаването на повече от "един" резултат разбира се е малко по-ангажирано. Има някои проблеми с JIRA относно модифицирането на други оператори, за да се справят с n резултати или „ограничаване“ или „разрязване“. По-специално SERVER-6074 . Но проблемът може да се реши по няколко начина.

Популярни реализации на модела на релсовия активен запис за съхранение на MongoDB са Mongoid и Mongo Mapper , и двете позволяват достъп до „родните“ функции на колекцията на mongodb чрез .collection аксесоар. Това е, което основно ви трябва, за да можете да използвате естествени методи като .aggregate() който поддържа повече функционалност от общото агрегиране на Active Record.

Ето един подход за агрегиране с mongoid, въпреки че общият код не се променя, след като имате достъп до собствения обект на колекция:

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

Наименуването в документите всъщност не се използва от кода и заглавията в данните, показани за „Първи“, „Втори“ и т.н., наистина са там само за да илюстрират, че наистина получавате „топ 2“ документа от списъка като резултат.

Така че подходът тук по същество е да се създаде "стек" от документи, "групирани" по вашия ключ, като например "тип". Първото нещо тук е да вземете „първия“ документ от този стек с помощта на $first оператор.

Следващите стъпки съответстват на „вижданите“ елементи от стека и ги филтрират, след което изваждате „следващия“ документ отново от стека, като използвате $first оператор. Последните стъпки там всъщност са самоx за връщане на документите в оригиналната форма, както се намира във входа, което обикновено се очаква от такава заявка.

Така че резултатът, разбира се, е само първите 2 документа за всеки тип:

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

Имаше по-дълга дискусия и версия на това, както и на други решения в този скорошен отговор:

$група за агрегиране на Mongodb, ограничаване на дължината на масив

По същество същото нещо въпреки заглавието и този случай се стремеше да съвпадне с до 10 най-добри записи или повече. Там също има някакъв код за генериране на конвейер за справяне с по-големи съвпадения, както и някои алтернативни подходи, които могат да бъдат разгледани в зависимост от вашите данни.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. node.js база данни

  2. Използвайте $strLenCP с Spring Data MongoDB

  3. MongoDB atomic findOrCreate:findOne, вмъкнете, ако не съществува, но не актуализирайте

  4. MongoDB филтрира множество поддокументи

  5. MongoDB - странно поведение на низходящ индекс