MongoDB
 sql >> Baza danych >  >> NoSQL >> MongoDB

Wiele warunków granicznych w mongodb

Ogólnie rzecz biorąc to, co opisujesz, jest stosunkowo powszechnym pytaniem w społeczności MongoDB, które możemy opisać jako "najlepsze n problem z wynikami". To jest, gdy podano dane wejściowe, które prawdopodobnie są w jakiś sposób posortowane, jak uzyskać najlepsze n wyniki bez polegania na arbitralnych wartościach indeksu w danych.

MongoDB ma $first operator dostępny dla struktury agregacji który zajmuje się „największą 1” częścią problemu, ponieważ w rzeczywistości zajmuje „pierwszy” element znaleziony na granicy grupowania, taki jak „typ”. Ale uzyskanie więcej niż „jednego” wyniku jest oczywiście trochę bardziej zaangażowane. Istnieje kilka problemów z JIRA dotyczących modyfikowania innych operatorów w celu obsługi n wyniki lub „ogranicz” lub „wycinek”. W szczególności SERVER-6074 . Problem można jednak rozwiązać na kilka sposobów.

Popularne implementacje wzorca szyny Active Record do przechowywania danych MongoDB to Mongoid i Mongo Mapper , oba umożliwiają dostęp do "natywnych" funkcji kolekcji mongodb poprzez .collection akcesor. Właśnie tego potrzebujesz, aby móc korzystać z natywnych metod, takich jak .aggregate() który obsługuje więcej funkcji niż ogólna agregacja Active Record.

Oto podejście agregacyjne z mongoid, chociaż ogólny kod nie zmienia się, gdy masz dostęp do natywnego obiektu kolekcji:

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

Nazewnictwo w dokumentach w rzeczywistości nie jest używane przez kod, a tytuły w danych pokazanych dla „Pierwszy”, „Drugi” itp. są tak naprawdę tylko po to, aby zilustrować, że rzeczywiście otrzymujesz „najlepsze 2” dokumenty z listy jako wynik.

Tak więc podejście tutaj polega zasadniczo na stworzeniu „stosu” dokumentów „pogrupowanych” według klucza, takiego jak „typ”. Pierwszą rzeczą tutaj jest pobranie "pierwszego" dokumentu z tego stosu za pomocą $first operatora.

Kolejne kroki dopasowują „widziane” elementy ze stosu i filtrują je, a następnie ponownie usuwasz „następny” dokument ze stosu, używając $first operator. Ostatnie kroki są tak naprawdę po prostu przywrócenie dokumentów do oryginalnej postaci, jaka została znaleziona w danych wejściowych, czego generalnie oczekuje się od takiego zapytania.

Wynik to oczywiście tylko 2 najlepsze dokumenty dla każdego typu:

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

W tej ostatniej odpowiedzi była dłuższa dyskusja i wersja tego, a także innych rozwiązań:

Agregacja Mongodb $grupa, ogranicz długość tablicy

Zasadniczo to samo, pomimo tytułu, a ta sprawa szukała dopasowania do 10 najlepszych wpisów lub więcej. Jest tam również trochę kodu generowania potoku do radzenia sobie z większymi dopasowaniami, a także kilka alternatywnych podejść, które można rozważyć w zależności od danych.



  1. Redis
  2.   
  3. MongoDB
  4.   
  5. Memcached
  6.   
  7. HBase
  8.   
  9. CouchDB
  1. Schemat Mongoose dla współrzędnych geoJson

  2. Zapytanie o dokument i wszystkie jego dokumenty podrzędne, które pasują do warunku w mongodb (przy użyciu wiosny)

  3. MongoDB 3.6.2 2008R2 Plus nie instaluje się

  4. Mongoid nie jest w zapytaniu

  5. mongodb, sortowanie według geoNear i daty?