Что не так с lazy = "dynamic"?Какие есть альтернативы? - PullRequest
0 голосов
/ 11 октября 2018

Этот вопрос был полностью переписан 10/17/18

Для того, чтобы иметь «Редактирование системы управления версиями» (аналогично StackOverflow по функциональности) Я настроил следующие классы:

tags = db.Table(
    "tags",
    db.Column("tag_id", db.Integer, db.ForeignKey("tag.id")),
    db.Column("post_version_id", db.Integer,
        db.ForeignKey("post_version.id"))
    )

class Tag(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    tag = db.Column(db.String(128), index=True, unique=True)

class Post(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    head_id = db.Column(db.Integer, db.ForeignKey("post_version.id"))

class PostVersion(db.Model):
    id = db.Column(db.Integer, primary_key=True)
    previous_id = db.Column(db.Integer, db.ForeignKey("post_version.id"), default=None)
    pointer_id = db.Column(db.Integer, db.ForeignKey("annotation.id"))
    current = db.Column(db.Boolean, index=True)
    tags = db.relationship("Tag", secondary=tags)

Исключены не относящиеся к делу столбцы, такие как содержимое сообщения и т. Д. Фактически, настоящая модель данных - это аннотации; Я упростил эти модели для универсальности

Фактические данные состоят из 136 Post, размеченных и изменяемых версиями посредством правок;то есть я произвел 136 Post х.У меня 15 Tag х.Начальные 136 Post были помечены последовательно с 2 Tag.Впоследствии я помечал Post различными тегами (используя мою систему редактирования; таким образом, для отредактированных Post было несколько PostVersion).

Как вы, вероятно, заметите,есть круговая ссылка между Post и PostVersion;Я использую это для настройки следующих двух отношений для экспериментов:

Отношение 1 posts

posts = db.relationship("Post",
    secondary="join(tags, PostVersion,"
        "tags.c.post_version_id==PostVersion.id)",
    primaryjoin="Tag.id==tags.c.tag_id",
    secondaryjoin="Post.head_id==PostVersion.id",
    lazy="dynamic")

, основанное на операторе SQL

SELECT
    post.id
FROM
    tag
JOIN
    tags ON tag.id=tags.tag_id
JOIN
    post_version ON tags.post_version_id=post_version.id
JOIN
    post ON post.head_id=post_version.id
WHERE
    tag.id=<tag_id>

и

Отношение 2 posts2

posts2 = db.relationship("Post",
    secondary="join(tags, PostVersion,"
    "and_(tags.c.post_version_id==PostVersion.id,"
    "AnnotationVersion.current==True))",
    primaryjoin="Tag.id==tags.c.tag_id",
    secondaryjoin="PostVersion.pointer_id==Post.id",
    lazy="dynamic")

на основе оператора SQL

SELECT
    annotation.id
FROM
    tag
JOIN
    tags ON tag.id=tags.tag_id
JOIN
    annotation_version ON tags.annotation_version_id=annotation_version.id AND 
    annotation_version.current=1
JOIN
    annotation ON annotation_version.pointer_id = annotation.id
WHERE
    tag_id=8;

Это дает следующие данные:

Tag         Actual      len(t.posts.all())  len(t.posts.paginate(1,5,False).items)
t1          0           0                   0
t2          1           136                 5
t3          1           136                 5
t8          136         136                 1
t14         136         136                 1
t15         24          136                 1

Tag         Actual      t.posts.count()     t.posts2.count()
t1          0           0                   0
t2          1           136                 163
t3          1           136                 163
t8          136         22168               26569
t14         136         22168               26569
t15         24          3264                3912

Iисключили избыточные теги (т. е. все остальные Tag с 0 Post) и идентичные данные (т. е. результаты posts2, которые были такими же, как для posts).

Как видите, есть серьезные проблемы с результатами!Особенно, когда для обоих отношений , если lazy="dynamic" выключен, правильные Post всегда возвращаются .

Используя echo=True при создании движка, @ IljaEverilä обнаружилчто lazy="dynamic" меняет SQL.Я цитирую комментарии в этом вопросе:

В двух словах: с lazy="dynamic" вы получаете FROM post, tags, post_version WHERE ..., но без вас FROM post, tags JOIN post_version ON tags.post_version_id = post_version.id WHERE .... Как вы можете видеть, ваш составной вторичный объект в значительной степени игнорируетсяс динамической настройкой.Теперь вопрос «почему?»


Мой вопрос:

1.Это ошибка?

2.Что я могу сделать, чтобы исправить это затруднительное положение?


Обновление:

Кажется, lazy="dynamic" явно не рекомендуется здесь , но альтернативы не предлагается.Какая альтернатива по-прежнему позволяет разбивать на страницы и рассчитывать на большие коллекции?По умолчанию это не допускается (или, по крайней мере, в том виде, в котором я к нему обращаюсь), и документация, похоже, не проясняет проблему!В разделе под названием Какой тип загрузки использовать? Стратегия загрузки, которую он рекомендует использовать для больших коллекций, составляет lazy="subquery", но это не допускает paginate() и count().

.

1 Ответ

0 голосов
/ 17 октября 2018

Это действительно проблема в том, как SQLAlchemy обрабатывает формирование запроса для динамических отношений загрузки.Хотя запрос должен был быть

SELECT post.id AS post_id, post.head_id AS post_head_id 
FROM post, tags JOIN post_version ON tags.post_version_id = post_version.id 
WHERE ? = tags.tag_id AND post.head_id = post_version.id

, он заканчивался как

SELECT post.id AS post_id, post.head_id AS post_head_id 
FROM post, tags, post_version
WHERE ? = tags.tag_id AND post.head_id = post_version.id

Так что, хотя есть внутреннее объединение между post и post_version (в пред В стиле SQL-92 ), внутреннее соединение между tags и post_version отсутствует, и поэтому существует CROSS JOIN между tags и остальными.В результате запрос загружает все текущие версии сообщений, независимо от тега (ов) , поскольку каждое сообщение объединяется с каждой строкой из tags.Это также объясняет умножение t.posts.count().

. Решение состоит в том, чтобы дождаться fix и в то же время использовать другую стратегию загрузки отношений.

...