Подтвердить что ты не робот

Как цепочка Rails ActiveRecord "where" содержит без нескольких запросов?

Я разработчик PHP, изучающий способность Ruby on Rails, я люблю 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

Как работает этот колдовство?

4b9b3361

Ответ 1

Метод where возвращает объект ActiveRecord::Relation, и сам по себе этот объект не выдает запрос базы данных. Это где вы используете этот объект, который имеет значение.

В консоли вы, вероятно, это сделаете:

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

И затем blammo выдает запрос базы данных и возвращает то, что кажется массивом всех по имени Jason. Yay, Активная запись!

Но тогда вы делаете что-то вроде этого:

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

И затем это вызывает другой запрос, но этот для людей, которых зовут Джейсон, которых составляет 26. Но он только выдаёт один запрос, так что, где был бы другой запрос?


Как и другие, это происходит, потому что метод where возвращает прокси-объект. Он фактически не выполняет запрос и возвращает набор данных, если он не попросил этого сделать.

Когда вы запускаете что-либо в консоли, оно выводит проверенную версию результата того, что вы запустили. Если вы положите 1 в консоль и нажмите enter, вы получите 1 назад, потому что 1.inspect - 1. Магия! То же самое касается "1". У множества других объектов нет метода inspect, и поэтому Ruby возвращается к тому, который находится на Object, который возвращает что-то ужасное, как <Object#23adbf42560>.

Каждый отдельный объект ActiveRecord::Relation имеет метод inspect, определенный на нем, так что он вызывает запрос. Когда вы пишете запрос в своей консоли, IRB вызовет inspect по возвращаемому значению этого запроса и выведет что-то почти читаемое человеком, например массив, который вы увидите.


Если вы только что опубликовали это в стандартном Ruby script, тогда запрос не будет выполняться до тех пор, пока объект не будет проверен (через inspect) или не будет повторен с помощью each или не будет иметь метод to_a призвал его.

До тех пор, пока не произойдет одна из этих трех вещей, вы можете направить на него столько операторов where, сколько захотите, а затем, когда вы вызовете inspect, to_a или each на нем, тогда это будет наконец, выполнить этот запрос.

Ответ 2

Существует несколько методов, которые называются "кикерами", которые фактически запускают запрос в базу данных. До этого они просто создают узлы AST, которые когда-то пинали, генерируют фактический SQL (или язык, скомпилированный для) и запускают запрос.

Смотрите это сообщение в блоге для более глубокого объяснения того, как это делается.

Ответ 3

Вы можете прочитать код, но одна концепция здесь - прокси-шаблон.

Вероятно, @person - это не реальный объект, а прокси-сервер для этого объекта, и когда вам нужен какой-то атрибут, активная запись, наконец, выполнит запрос. Hibernate имеет ту же концепцию.

Ответ 4

Возможно, слишком поздно, но вы можете использовать хэш:

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

Результирующий запрос:

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