LINUX.ORG.RU

В каких ORM какой синтаксис на такое действие?


0

1

Предположим, что у нас есть две таблицы:

topics: id, owner_id, modify_time ...
visits: topic_id, owner_id, visit_time
Я хочу загрузить массив топиков за последнее время с пометками о прочтении юзерами. В терминах SQL это будет:
SELECT topics.*, visits.visit_time
    FROM topics
        LEFT JOIN visits_class ON visits_class.topic_id = topics_class.id AND visits_class.owner_id = %user_id%
    WHERE modify_time > %time%

Вопрос в синтаксисе подобного решения в категориях ORM. Где как реализовано? Я делаю что-то типа (для простоты все поля объектов одноимённые с полями таблиц БД):

$topics = find_all('topic_class', array(
    '*left_join' => 'visits ON visits.topic_id = topics.id AND visits.owner_id = $user_id',
    '*set' => 'visit_time => visits.visit_time',
    'modify_time>' => $time
));

Ну и потом - $topics[0]->visit_time() на общих правах, кроме модификации.

Есть ли идеи, как это сделать в общем случае красивее? Ну и да, подразумевается, что весь массив грузится одним запросом, естественно. Иначе достаточно тупо однострочную function visit_time() использовать без всяких join'ов...

★★★★★

sqlalchemy

s.query(Topic).join(Visit).filter(Visit.owner_id == user_id).filter(Topic.modify_time > time)

хм, или цимес в условном выражении в join?

hizel ★★★★★
()
Ответ на: комментарий от hizel

>s.query(Topic).join(Visit).filter()

А где указано, по какому полю объединяются Topic и Visit?

В какой запрос это реально выльется? filter() работает на формировании WHERE или уже на фильтрации результата запроса?

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

создаются соответствующие классы и там указана связь тоблицек, или можно вручную указать

filter() работает на формировании WHERE или уже на фильтрации результата запроса?


filter попадает во where
ну запрос сформируется и вернет данные если присобачить сзади .all()

http://www.sqlalchemy.org/docs/orm/tutorial.html#querying-with-joins тут можно посмотреть какой sql подобные конструкции выхлопывают

hizel ★★★★★
()
Ответ на: комментарий от KRoN73

>И где указано, что visit_time запишется в соответствующее поле Topic?
иммеется ввиду эта часть SELECT topics.*, _visits.visit_time_ ?

я просто не стал фильтровать нужные колонки, sqlalchemy сама присобачит все колонки если они при-join-ины

hizel ★★★★★
()
Ответ на: комментарий от hizel

>создаются соответствующие классы и там указана связь тоблицек

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

иммеется ввиду эта часть SELECT topics.*, _visits.visit_time_ ?


В виде SQL или в виде '*set' => 'visit_time => visits.visit_time', в ORM-выражении (собственно, я сейчас подбираю оптимальный синтаксис именно под это выражение)

sqlalchemy сама присобачит все колонки если они при-join-ины


В каком виде всё это оформляется в классах? Интересует именно всё в связке. Мой вариант не требует модификации Topic для добавления ему одноразово такого параметра.

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

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

ее можно прибивать в class-е и можно указывать в ручную для конкретной конструкции

эхъ, что-то я туплю :(

вот как то так, концэпт

#!/usr/bin/env python

class Owner(Base):
    __tablename__ = 'owners'
    id = Column('id', Integer, autoincrement=True, primary_key=True)

class Topic(Base):
    __tablename__ = 'topics'
    id = Column('id', Integer, autoincrement=True, primary_key=True)
    owner_id = ForeignKey('owners.id')
    modify_time = Column('modify_time', DateTime)
    visits = relationship('Visit', backref='parent')
    owner = relationship('Owner', backref='parents')

    def visit_time(self, time):
        return self.query(visits).filter(
                visits.c.owner_id == owner.c.id
                ).filter(self.c.modify_time > time).one().visit_time

class Visit(Base):
    __tablename__ = 'visits'
    id = Column('id', Integer, autoincrement=True, primary_key=True)
    owner_id = ForeignKey('owners.id')
    topic_id = ForeignKey('topics.id')
    visit_time = Column('visit_time', DateTime)
hizel ★★★★★
()
Ответ на: комментарий от hizel

только, бладжад, оно будет на каждую строчку sql дергать,
нет нужно просто дописать relationship с условием и будет ок, но меня что-то совершенно сбивает с толку твой sql и php код :-(

hizel ★★★★★
()

ActiveRecord

@topics = Topic.select("topics.*, visits.visit_time")
    .joins("LEFT JOIN visits ON visits.topic_id = topics.id AND visits.owner_id = #{user_id}")
    .where("modify_time > #{time}")

@topics[0].visit_time # тоже без модификации

Плоский SQL получается по сути. Зато никаких подгонок в модели делать не нужно :)

Apple-ch ★★
()
Ответ на: ActiveRecord от Apple-ch

>Плоский SQL получается по сути

Нет. Нужен универсальный механизм. В идеале и с NoSQL работающий (понятно, там в два запроса будет транслироваться).

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

Универсальный — это хорошо. То, что есть в AR, красивым не считаю, да он и служит другим целям.

К вопросу об универсальности: как в твоём синтаксисе использовать LIKE, например?

Apple-ch ★★
()
Ответ на: комментарий от Apple-ch

>как в твоём синтаксисе использовать LIKE

Не задумывался. Задача у меня очень редкая, в тех паре-тройке случаев, где нужно, используется сырой MySQL. Я пытаюсь охватывать удобными и правильными (на мой вкус, естественно) решениями не 100% случаев, а 99% :)

KRoN73 ★★★★★
() автор топика
Ответ на: комментарий от KRoN73

Просто на руби можно сделать что-нибудь вроде такого:

Company.where(:name.like => "%soft")

Где like — подмешанный в String и Symbol метод, оборачивающий атрибут, который в where затем превращается в SELECT * FROM companies WHERE name LIKE '%soft' или db.companies.find({name:/soft$/}) для SQL и NoSQL соответственно.

Я думал, у тебя есть аналог для пыха :)

Apple-ch ★★
()
Ответ на: комментарий от Apple-ch

Я думал, у тебя есть аналог для пыха :)

Да тут тоже всегда можно развернуть что-то типа

'name LIKE' => '%soft'
просто надобности не было. А вот, скажем, очень часто используемое IN у меня разворачивается:
'id IN' => $array_of_ids
массив заискейпится, заджойнится в апострофах через запятую и всё возьмётся в скобки.
'create_time BETWEEN' => array($start, $end)
тоже разворачивается.

KRoN73 ★★★★★
() автор топика
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.