7

Mam dwa modele Content i ContentType. W modelu zawartości mogę wykonać:"Nie równy" nazwany zakres w szynach z Mongoidem

def get_all_content_except_poking_message 
    Content.all.where(:name.ne => "no forking, just poking") 
end 

Teraz próbuję zastosować zakres w ContentType. W modelu Content ponownie:

# Associations 
belongs_to :content_type 

def get_all_content_except_certain_content_type(content_type) 
    Content.all.where(:content_type.name.ne => content_type) 
end 

, ale błąd sugeruje, że jest to niewłaściwa składnia do zastosowania zakresu w polu powiązania.

Jaki jest właściwy sposób zastosowania zakresu w polach asocjacji w modelu?

Używam również klejnotu has_scope. Czy mogę zastosować ten sam filtr w kontrolerze? Coś jak:

@contents = apply_scopes(
    if params[:type] 
    @content_type = ContentType.find_by_slug(params[:type]) 
    @content_type.contents.all 
    else 
    Content.all.where (:content_type.name.ne => "blogs") 
    end 
) 

aktualizacji

Dla wyjaśnienia, tutaj jest wyjście IRB:

irb(main):020:0> ContentType.all(:name=>"blogs").count 
=> 1 

irb(main):023:0> Content.last.content_type.name 
=> "blogs" 

irb(main):024:0> Content.all.where(:content_type => {:name => {'$ne' => "blogs"}}).count 
=> 0 

irb(main):026:0> Content.all.count 
=> 4 

Odpowiedz

5

Szybka odpowiedź brzmi, że zapytanie serwera MongoDB działa tylko na jednej kolekcji. Nie ma łączenia, które przecina zbiory. Pytasz w kolekcji zawartości, ale określając pole należące do kolekcji content_types.

Można użyć osadzania, aby umieścić dwa modele w jednej kolekcji, a następnie zapytanie może działać w odniesieniu do osadzonego dokumentu (podrzędnego) pola.

Mogę podać więcej szczegółów, jeśli chcesz, ale mam nadzieję, że to pozwoli ci przejść przez obecne zdziwienie.

Uzupełnienie na żądanie, bez konieczności osadzania:

Uwaga: Dostęp do danych z wielu kolekcjach będą wymagały wielu zapytań, co najmniej jedno zapytanie za kolekcję.

Poniższy przykład jest oparty na tym, co mogłem wyodrębnić ze swojego wpisu, z modyfikacjami, które działają tak, jak chcesz dla swoich metod. Zapytanie "nie jest równe" korzysta ze znajomości implementacji asocjacji, która jest na razie szybką odpowiedzią, ale struktura linków jest dość oczywista po inspekcji. Należy również zauważyć, że rzeczywiste zapytania Motoroli do MongoDB są wyświetlane w odpowiednim dzienniku Railsów.

Nie znam szczegółów dotyczących plataformatec/has_scope. Mongoid ma swoje własne zakresy, które powinieneś zbadać, jestem gotów pomóc, kiedy tam dotrzesz.

app/models/content_type.rb

class ContentType 
    include Mongoid::Document 
    field :name, type: String 
    has_many :contents 
end 

app/models/content.rb

class Content 
    include Mongoid::Document 
    field :name, type: String 
    belongs_to :content_type 

    def self.get_all_content_except_poking_message 
    Content.where(:name.ne => "no forking, just poking") 
    end 

    def self.get_all_content_except_certain_content_type(content_type_name) # 2 queries - one each for ContentType and Content 
    content_type = ContentType.where(:name => content_type_name).first 
    Content.where(:content_type_id.ne => content_type.id) 
    end 
end 

test/jednostka/content_test.rb

require 'test_helper'

class ContentTest < ActiveSupport::TestCase 
    def setup 
    Content.delete_all 
    ContentType.delete_all 
    end 

    test "not equal blogs" do 
    blogs = ContentType.create(:name => "blogs") 
    tweets = ContentType.create(:name => "tweets") 
    blogs.contents << Content.create(:name => "no forking, just poking") 
    tweets.contents << Content.create(:name => "Kilroy was here") 
    assert_equal 2, ContentType.count 
    assert_equal 2, Content.count 
    puts "all content_types: #{ContentType.all.to_a.inspect}" 
    puts "all contents: #{Content.all.to_a.inspect}" 
    puts "get_all_content_except_poking_message: #{Content.get_all_content_except_poking_message.to_a.inspect}" 
    puts "get_all_content_except_certain_content_type(\"blogs\"): #{Content.get_all_content_except_certain_content_type("blogs").to_a.inspect}" 
    end 
end 

testową grabie

Run options: 

# Running tests: 

[1/1] ContentTest#test_not_equal_blogsall content_types: [#<ContentType _id: 51ded9d47f11ba4ec1000001, name: "blogs">, #<ContentType _id: 51ded9d47f11ba4ec1000002, name: "tweets">] 
all contents: [#<Content _id: 51ded9d47f11ba4ec1000003, name: "no forking, just poking", content_type_id: "51ded9d47f11ba4ec1000001">, #<Content _id: 51ded9d47f11ba4ec1000004, name: "Kilroy was here", content_type_id: "51ded9d47f11ba4ec1000002">] 
get_all_content_except_poking_message: [#<Content _id: 51ded9d47f11ba4ec1000004, name: "Kilroy was here", content_type_id: "51ded9d47f11ba4ec1000002">] 
get_all_content_except_certain_content_type("blogs"): [#<Content _id: 51ded9d47f11ba4ec1000004, name: "Kilroy was here", content_type_id: "51ded9d47f11ba4ec1000002">] 
Finished tests in 0.046370s, 21.5657 tests/s, 43.1313 assertions/s. 
1 tests, 2 assertions, 0 failures, 0 errors, 0 skips 

Na tym prostym przypadku można "uproszczenia" odchodząc od ścisłego relacyjnej normalizacji, na przykład, po prostu dodać "content_type_name" pole do treści z wartościami ciągu, np. "blogi".

Ale aby naprawdę skorzystać z MongoDB, nie powinieneś się wahać.

Mam nadzieję, że to pomoże.

+0

Gary, dzięki za odpowiedź. Jeśli wbudowane powiązanie nie jest opcją, czy istnieje inny sposób na sfałszowanie powiązania? Byłbym bardzo wdzięczny, jeśli podasz przykład kodu dla mojego scenariusza. :) – Annie

+0

Annie, przykład w powyższej odpowiedzi celuje w Twój scenariusz za pomocą odnośników/linków, a nie osadzania. Sprawdź kod dla Content :: get_all_content_except_certain_content_type, który dokładnie odpowiada na twoje pytanie dzięki dwuetapowemu pobieraniu, który jest niezbędny. –

Powiązane problemy