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


Я разработчик 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

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

4 58

4 ответа:

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

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

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

а то blammo он выдает запрос к базе данных и возвращает то, что кажется массив всех по имени Джейсон. Ура, Активная Запись!

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

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

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


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

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

все до единого

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

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

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

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

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

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

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

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