Rails ActiveRecord在没有多个查询的情况下是如何链接“where”子句的?

时间:2022-05-14 12:03:38

I'm a PHP developer learning the awesomness of Ruby on Rails, I'm loving ActiveRecord and i noticed something really interesting, Which is how ActiveRecord methods detect the end of method chain to execute the query.

我是一个学习Ruby on Rails惊人之处的PHP开发人员,我喜欢ActiveRecord,我注意到一件非常有趣的事情,那就是ActiveRecord方法如何检测方法链的末端来执行查询。

@person = Person.where(name: 'Jason').where(age: 26)

# In my humble imagination I'd think that each where() executes a database query
# But in reality, it doesn't until the last method in the chain

How does this sorcery work?

这个魔法是怎么起作用的?

4 个解决方案

#1


129  

The where method returns an ActiveRecord::Relation object, and by itself this object does not issue a database query. It's where you use this object that matters.

where方法返回一个ActiveRecord::关系对象,该对象本身不会发出数据库查询。重要的是你在哪里使用这个对象。

In the console, you're probably doing this:

在控制台,您可能正在这样做:

@person = Person.where(name: "Jason")

And then blammo it issues a database query and returns what appears to be an array of everyone named Jason. Yay, Active Record!

然后blammo发出一个数据库查询,并返回每个名为Jason的人的数组。耶,活动记录!

But then you do something like this:

但接下来你要做的是:

@person = Person.where(name: "Jason").where(age: 26)

And then that issues another query, but this one's for people who are called Jason who are 26. But it's only issuing one query, so where'd the other query go?

然后它会发出另一个查询,但是这个是给那些叫Jason的26岁的人用的。但是它只发出一个查询,那么另一个查询会到哪里去呢?


As others have suggested, this is happening because the where method returns a proxy object. It doesn't actually perform a query and return a dataset unless it's asked to do that.

正如其他人所指出的,这是因为where方法返回一个代理对象。它实际上不会执行查询并返回数据集,除非它被要求这样做。

When you run anything in the console, it's going to output the inspected version of the outcome of whatever it is you ran. If you put 1 in the console and hit enter, you'll get 1 back because 1.inspect is 1. Magic! Same goes for "1". A variety of other objets don't have an inspect method defined and so Ruby falls back to the one on Object which returns something ghastly like <Object#23adbf42560>.

当您在控制台中运行任何东西时,它将输出您所运行的结果的检查版本。如果你在控制台输入1,点击回车,你会得到1,因为1。检查是1。魔法!同样适用于“1”。许多其他objets都没有定义检查方法,因此Ruby回到了对象上的方法,该方法返回类似于 的东西。 #23adbf42560>

Every single ActiveRecord::Relation object has the inspect method defined on it so that it causes a query. When you write the query in your console, IRB will call inspect on the return value from that query and output something almost human readable, like the Array that you'd see.

每个ActiveRecord::关系对象都有一个定义在它上面的检查方法,这样它就会导致一个查询。当您在控制台中编写查询时,IRB将调用该查询的返回值inspect,并输出几乎人类可读的内容,如您将看到的数组。


If you were just issuing this in a standard Ruby script, then no query would be executed until the object was inspected (via inspect) or was iterated through using each, or had the to_a method called on it.

如果您只是在一个标准的Ruby脚本中发出这个命令,那么在检查对象(通过inspect)或使用它们进行迭代之前,都不会执行查询,或者调用to_a方法。

Up until one of those three things happen, you can chain as many where statements on it as you will like and then when you do call inspect, to_a or each on it, then it will finally execute that query.

直到这三件事中的一件发生,你可以把你想要的语句链锁在上面,然后当你调用检查,to_a或它的每一个时,它最终会执行那个查询。

#2


7  

There are a number of methods that are known as "kickers" that actually fire off the query to the database. Prior to that, they just create AST nodes, which once kicked, will generate the actual SQL (or language being compiled to) and run the query.

有许多方法被称为“kickers”,它实际上是将查询发送到数据库。在此之前,他们只创建AST节点,一旦启动,将生成实际的SQL(或正在编译的语言)并运行查询。

See this blog post for a deeper explanation of how this is done.

请参阅这篇博客文章,以获得更深入的解释。

#3


4  

You can read the code, but one concept here is the proxy pattern.

您可以读取代码,但是这里的一个概念是代理模式。

Probably @person is not the real object but a proxy to this object and when you need some attribute there the active record finally execute the query. Hibernate has the same concept.

可能@person不是真正的对象,而是这个对象的代理,当您需要某个属性时,活动记录最终执行查询。Hibernate有相同的概念。

#4


-2  

Maybe bit too late but you can use a hash:

可能有点太晚了,但你可以使用散列:

@person = Person.where({name: "Jason", age: 26})

Resulting query:

查询结果:

SELECT "person".* FROM "person"  WHERE "person"."name" = 'Jason' AND "person"."age" = 26

#1


129  

The where method returns an ActiveRecord::Relation object, and by itself this object does not issue a database query. It's where you use this object that matters.

where方法返回一个ActiveRecord::关系对象,该对象本身不会发出数据库查询。重要的是你在哪里使用这个对象。

In the console, you're probably doing this:

在控制台,您可能正在这样做:

@person = Person.where(name: "Jason")

And then blammo it issues a database query and returns what appears to be an array of everyone named Jason. Yay, Active Record!

然后blammo发出一个数据库查询,并返回每个名为Jason的人的数组。耶,活动记录!

But then you do something like this:

但接下来你要做的是:

@person = Person.where(name: "Jason").where(age: 26)

And then that issues another query, but this one's for people who are called Jason who are 26. But it's only issuing one query, so where'd the other query go?

然后它会发出另一个查询,但是这个是给那些叫Jason的26岁的人用的。但是它只发出一个查询,那么另一个查询会到哪里去呢?


As others have suggested, this is happening because the where method returns a proxy object. It doesn't actually perform a query and return a dataset unless it's asked to do that.

正如其他人所指出的,这是因为where方法返回一个代理对象。它实际上不会执行查询并返回数据集,除非它被要求这样做。

When you run anything in the console, it's going to output the inspected version of the outcome of whatever it is you ran. If you put 1 in the console and hit enter, you'll get 1 back because 1.inspect is 1. Magic! Same goes for "1". A variety of other objets don't have an inspect method defined and so Ruby falls back to the one on Object which returns something ghastly like <Object#23adbf42560>.

当您在控制台中运行任何东西时,它将输出您所运行的结果的检查版本。如果你在控制台输入1,点击回车,你会得到1,因为1。检查是1。魔法!同样适用于“1”。许多其他objets都没有定义检查方法,因此Ruby回到了对象上的方法,该方法返回类似于 的东西。 #23adbf42560>

Every single ActiveRecord::Relation object has the inspect method defined on it so that it causes a query. When you write the query in your console, IRB will call inspect on the return value from that query and output something almost human readable, like the Array that you'd see.

每个ActiveRecord::关系对象都有一个定义在它上面的检查方法,这样它就会导致一个查询。当您在控制台中编写查询时,IRB将调用该查询的返回值inspect,并输出几乎人类可读的内容,如您将看到的数组。


If you were just issuing this in a standard Ruby script, then no query would be executed until the object was inspected (via inspect) or was iterated through using each, or had the to_a method called on it.

如果您只是在一个标准的Ruby脚本中发出这个命令,那么在检查对象(通过inspect)或使用它们进行迭代之前,都不会执行查询,或者调用to_a方法。

Up until one of those three things happen, you can chain as many where statements on it as you will like and then when you do call inspect, to_a or each on it, then it will finally execute that query.

直到这三件事中的一件发生,你可以把你想要的语句链锁在上面,然后当你调用检查,to_a或它的每一个时,它最终会执行那个查询。

#2


7  

There are a number of methods that are known as "kickers" that actually fire off the query to the database. Prior to that, they just create AST nodes, which once kicked, will generate the actual SQL (or language being compiled to) and run the query.

有许多方法被称为“kickers”,它实际上是将查询发送到数据库。在此之前,他们只创建AST节点,一旦启动,将生成实际的SQL(或正在编译的语言)并运行查询。

See this blog post for a deeper explanation of how this is done.

请参阅这篇博客文章,以获得更深入的解释。

#3


4  

You can read the code, but one concept here is the proxy pattern.

您可以读取代码,但是这里的一个概念是代理模式。

Probably @person is not the real object but a proxy to this object and when you need some attribute there the active record finally execute the query. Hibernate has the same concept.

可能@person不是真正的对象,而是这个对象的代理,当您需要某个属性时,活动记录最终执行查询。Hibernate有相同的概念。

#4


-2  

Maybe bit too late but you can use a hash:

可能有点太晚了,但你可以使用散列:

@person = Person.where({name: "Jason", age: 26})

Resulting query:

查询结果:

SELECT "person".* FROM "person"  WHERE "person"."name" = 'Jason' AND "person"."age" = 26