选择关联时具有否定条件的对象和关联的组

时间:2015-11-29 13:04:48

标签: sql ruby-on-rails postgresql activerecord

我想我在标题中并不是很清楚,但我发现很难描述完全之后的事情。所以最好从一个例子开始:

我有以下型号:

class Song < ActiveRecord::Base
  has_many :learnt_items, as: :learnable
  has_many :trained_items, as: :learnable
end

class LearntItem < ActiveRecord::Base
  belongs_to :learnable, polymorphic: true
end

class TrainedItem < LearntItem
end

例如,如果我选择所有受过训练的项目的歌曲:

Song.joins(:trained_items)

我会看到(大致)返回的记录如下:

learnable_type | learnable_id | type        | label
Song           | 1            | TrainedItem | happy
Song           | 1            | TrainedItem | sad
Song           | 1            | TrainedItem | lively
Song           | 2            | TrainedItem | lively

如果我想选择所有已经训练过 特定标签的歌曲,我会这样做:

Song.joins(:trained_items).where(learnt_items: { label: [:happy, :sad] })

现在,我需要让所有的歌曲都有针对给定标签的训练项目。人们会认为以下就足够了:

Song.joins(:trained_items).where.not(learnt_items: { label: [:happy, :sad] })

但这仍然会产生以下记录:

learnable_type | learnable_id | type        | label
Song           | 1            | TrainedItem | lively
Song           | 2            | TrainedItem | lively

这确实不是我的意图。您可以看到查询过滤掉了带有给定标签的记录,但label = 'lively'的记录仍然存在,因此返回的歌曲为id = 1。我只需要从此查询中返回id = 2的歌曲。

如何使用ActiveRecord构建查询以便我的方案得以实现?

1 个答案:

答案 0 :(得分:1)

使用子查询查找您不想要的id并使用where.not条件中的Song.joins(:trained_items) .where.not(learnable_id: Song.select(:learnable_id).where(learnt_items: { label: [:happy, :sad] })

class Message(object):
    pass

class InitMessage(Message):
    def __init__(self, queue_in, queue_out):
        self.queue_in = queue_in
        self.queue_out = queue_out

class Client(Thread):

    def __init__(self, server_queue, name):
        Thread.__init__(self)
        self.control = server_queue
        self.my_in_queue = Queue()
        self.my_out_queue = Queue()
        self.name = name

    def run(self):
        self.control.put(InitMessage(self.my_in_queue, self.my_out_queue))
        while True:
            if not self.my_in_queue.empty():
                message = self.my_in_queue.get()
                print("Client {name} got message from server of type {type}".format(
                    name=self.name, type=type(message).__name__
                ))
                self.my_in_queue.task_done()
                self.my_out_queue.put(Message())

class Server(Thread):

    def __init__(self):
        Thread.__init__(self)
        self.control = Queue()
        self.clients = {}
        self.id = 0

    def run(self):
        while True:
            while not self.control.empty():
                message = self.control.get()
                if isinstance(message, InitMessage):
                    self.clients[self.id] = {'to_client': message.queue_in,
                                             'from_client': message.queue_out}
                    message.queue_in.put(Message())
                    self.id += 1
                self.control.task_done()

            for client in self.clients:
                if not self.clients[client]['from_client'].empty():
                    message = self.clients[client]['from_client'].get()
                    print("got message from client {id}".format(id=client))
                    self.clients[client]['from_client'].task_done()
                    self.clients[client]['to_client'].put(Message())

            sleep(1)

server = Server()
client1 = Client(server.control, "a")
client2 = Client(server.control, "b")
server.start()
client1.start()
client2.start()
server.join()