Неоднозначное имя столбца при объединении таблиц с FluentSQLite - PullRequest
4 голосов
/ 21 февраля 2020

Я пытаюсь написать маршрут, который возвращает все сообщения, написанные пользователями, за которыми следует конкретный пользователь. Это модель данных, которую я использую:

struct Follow: Content, SQLiteModel, Migration {
    var id: Int?
    var follower: String
    var following: String
}

struct Post: Content, SQLiteModel, Migration {
    var id: Int?
    var username: String
    var message: String
}

struct User: Content, SQLiteStringModel, Migration {
    var id: String?
    var password: String
}

И это маршрут:

router.get(String.parameter, "timeline") { req -> Future<[Post]> in
    let username = try req.parameters.next(String.self)

    return Follow.query(on: req).filter(\Follow.follower == username).join(\Follow.following, to: \Post.username).alsoDecode(Post.self).all().map { tuples in
        return tuples.map { tuple in
            return tuple.1
        }
    }
}

Код компилируется, но во время выполнения я получаю этот JSON словарь ошибок :

{"error":true,"reason":"ambiguous column name: main.Follow.id"}

И у меня также есть идея, что проблема в том, что, поскольку я делаю объединение, есть поле идентификатора-дубликата (Follow.id, Post.id), но как это решить? проблема? в sql я бы просто указал что-то вроде 'as followId', чтобы переименовать поле, но как это сделать в FluentSQLite?

Обновление

Это как я изменил маршрут «временной шкалы» в конце:

return User.find(username, on: req).flatMap { user in
    guard let user = user else {
        throw Abort(.notFound)
    }

    return try user.followers.query(on: req)
        .join(\Post.username, to:\FollowUp.following)
        .alsoDecode(Post.self).all()
        .flatMap { tuples in
        return tuples.map { tuple in
            return tuple.1
        }
    }
}

Я получаю эту ошибку: «Невозможно преобразовать значение типа« [Post] »для закрытия типа результата« EventLoopFuture <[Post]> '»

1 Ответ

2 голосов
/ 22 февраля 2020

Follow - это, по сути, таблица Pivot, формально реализующая ее по мере необходимости, чтобы избавиться от вашей проблемы. Тем не менее, вы можете столкнуться с трудностями, см .:

Родственные отношения между одинаковыми моделями в Vapor

Так что, в вашем случае, сделайте ваши follower и following введите User.ID и добавьте в свою модель Follow следующее:

struct Follow: Content, Pivot, Migration
{
    static var idKey: WritableKeyPath<FollowUp, Int?> = .id
    typealias Database = SQLiteDatabase
    typealias ID = Int
    var id:Int?
    var follower:User.ID
    var following:User.ID

    typealias Left = User
    typealias Right = User

    static var leftIDKey: WritableKeyPath<Follow, Int> {
        return \.follower
    }

    static var rightIDKey: WritableKeyPath<Follow, Int> {
        return \.following
    }
}

А затем создайте это расширение:

extension User:
{
    var followers: Siblings<User, User, Follow> {
    return siblings(Follow.leftIDKey, Follow.rightIDKey)
    }
}

Итак, наконец, ваш запрос становится:

return User.find(username, on: req).flatMap { user in
    guard let user = user else {
        throw Abort(.notFound)
    }

    return try user.followers.query(on: req)
        .join(\Post.username, to:\FollowUp.following)
        .alsoDecode(Post.self).all()
        .map { tuples in
        return tuples.map { tuple in
            return tuple.1
        }
    }
}

Вам нужно настроить Post, чтобы удерживать User.ID вместо username, чтобы объединение работало.

...