How to filter other parent of child with Vapor? - vapor

I have this request:
router.get("/fetchOngoingReleases") { (request) -> Future<[ReleaseOut]> in
return Release.query(on: request).filter(\.inprogress == true).all().map { releases in
var result: [ReleaseOut] = []
for r in releases {
var pageEvents: [Event] = []
let num = r.releaseUsers.query(on: request).filter(\.user.fbId ~~ "something").count()
var needAuthentication: Bool
if num == 0 {
needAuthentication = true
} else {
needAuthentication = false
}
let rOut = ReleaseOut(fbId: r.fbId, name: r.name, purpose: r.purpose, needAuthentication: needAuthentication)
result.append(rOut)
}
return result
}
}
}
It says I can not access (???) releaseUser.user.fbId in the query?
Here the data model:
and in code
final class Release: Content {
var id: Int?
var fbId: String
var inprogress: Bool?
var name: String
var purpose: String
/// Creates a new `Release`.
init(id: Int? = nil, fbId: String, name: String, purpose: String = "normal selling") {
self.id = id
self.fbId = fbId
self.name = name
self.purpose = purpose
}
}
extension Release {
var releaseUsers: Children<Release, ReleaseUser> {
return children(\.releaseId)
}
}
final class ReleaseUser: Content {
var id: Int?
var releaseId: Release.ID
var userId: User.ID
init(id: Int? = nil, releaseId: Release.ID, userId: User.ID) {
self.id = id
self.releaseId = releaseId
self.userId = userId
}
}
extension ReleaseUser {
var user: Parent<ReleaseUser, User> {
return parent(\.userId)
}
}
final class User: Content {
var id: Int?
var fbId: String
var name: String
init(id: Int? = nil, fbId: String, name: String) {
self.id = id
self.fbId = fbId
self.name = name
}
}

Ok so there are several things going on here, but the main concept is that you can't just jump across different tables like that - you need to use a JOIN to join the ReleaseUser table to the User table so you can then query on the fbId

Try changing your query to:
Release.query(on: request).filter(\.inprogress == true).join(\ReleaseUser.releaseId, to:\Release.Id).join(\ReleaseUser.userId, to:\User.Id).alsoDecode(User.self).all()
The alsoDecode will give you a tuple with the first position containing your original Release instance and the second containing the corresponding User instance. So, fbId should be available as:
r.1.fbId
In your case.

Related

Issue with unwrapping optionals in my model and DTO

This might be something really easy but I don't understand how to do it:
so I have this DTO struct I use to get API data into it and map it to Model struct
my DTO:
struct PetDTO: Codable {
var id: Int
var category: CategoryDTO?
var name: String?
var photoUrls: [String]?
var tags: [TagDTO]?
var status: StatusDTO?
}
public class CategoryDTO: NSObject, Codable {
var id: Int
var name: String?
private enum CodingKeys: String, CodingKey {
case id = "id"
case name = "name"
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
}
}
public class TagDTO: NSObject, Codable {
var id: Int
var name: String?
private enum CodingKeys: String, CodingKey {
case id = "id"
case name = "name"
}
required public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
id = try container.decode(Int.self, forKey: .id)
name = try container.decode(String.self, forKey: .name)
}
}
enum StatusDTO: String, Codable {
case available
case sold
case pending
}
And my model:
struct PetDataModel {
var id: Int
var category: Category
var name: String?
var photoUrls: [String]?
var tags: [Tags]?
var status: Status?
init(petDto: PetDTO) {
self.id = petDto.id
self.category = Category(categoryDto: petDto.category)
self.name = petDto.name
self.photoUrls = petDto.photoUrls
for tag in petDto.tags ?? [] {
self.tags = [Tags(tagDTO: tag)] // petDto?.map { Tags(tagDTO: $0) }
}
self.status = Status(rawValue: petDto.status?.rawValue)
}
}
struct Category {
var id: Int
var name: String?
init(categoryDto: CategoryDTO) {
self.id = categoryDto.id
self.name = categoryDto.name
}
}
struct Tags {
var id: Int
var name: String?
init(tagDTO: TagDTO) {
self.id = tagDTO.id
self.name = tagDTO.name
}
}
enum Status: String, Codable {
case available
case sold
case pending
}
As you can see, the mapping happens in Init of PetDataModel. I have errors on this lines
Please tell me how to fix this without making CategoryDto from PetDTO non optional, I need it to stay optional.
You can make category form your PetDataModel optional too.
struct PetDataModel {
var id: Int
var category: Category?
var name: String?
var photoUrls: [String]?
var tags: [Tags]?
var status: Status?
init(petDto: PetDTO) {
self.id = petDto.id
self.category = Category(categoryDto: petDto.category)
self.name = petDto.name
self.photoUrls = petDto.photoUrls
for tag in petDto.tags ?? [] {
self.tags = [Tags(tagDTO: tag)] // petDto?.map { Tags(tagDTO: $0) }
}
self.status = Status(rawValue: petDto.status?.rawValue)
}
}
and make your initializer optional:
struct Category {
var id: Int
var name: String?
init?(categoryDto: CategoryDTO?) {
guard let categoryDto = categoryDto else{
return nil
}
self.id = categoryDto.id
self.name = categoryDto.name
}
}
if you donĀ“t want an optional initializer you can check and assign like this:
self.category = petDto.category != nil ? Category(categoryDto: petDto.category!) : nil

Filter two struct arrays

What is the best way to filter two Struct Arrays to match by ID and added the information in a specific property.
Example
Struct User {
let id: Int
let name: String
var arts: [Article]?
}
Struct Article {
let userId: Int
let id: Int
let title: String
let body: String
}
I have an array with all Users and other array with all Post for all Users. I need to add into User Array all post by user (User.id == Article.userId)
I'm trying to do with this.
var art = [Article]()
var users = [User]()
self?.art.forEach({ art in
guard let userId = self?.users.firstIndex(where: { $0.id == art.userId }) else {
print("Failed to find a Art by UserID")
return
}
self?.users[userId].arts?.append(art)
})
The idea is added into User Struct all Articles corresponding by user
I think your code was in the right direction. Try this approach (works for me):
var arts = [Article(userId: 1, id: 1, title: "title1"),
Article(userId: 6, id: 1, title: "title6")]
var users = [User(id: 1, name: "user1"),
User(id: 2, name: "user2")]
print("---> before users: \n \(users)")
arts.forEach{ art in
if let userNdx = users.firstIndex(where: { $0.id == art.userId }) {
if let _ = users[userNdx].arts {} else {
users[userNdx].arts = []
}
users[userNdx].arts!.append(art)
}
}
print("\n---> after users: \n \(users)")
class Article {
let userId: Int
let id: Int
let title: String
let body: String
}
class User {
let id: Int
let name: String
var arts: [Post]?
}
I think the best possible way is to convert it to a dictionary. I think the below code is well explonary.
var dict = [Int: [Article]]()
var arts = [Article]()
for art in arts {
dict[art.userId, default: []].append(art)
}
var users = [User]()
for case let user in users {
let articles = dict[user.id]
user.atrs = articles
}

Vapor 3 and Fluent - nested query

I'm trying to do a nested query in Vapor 3 and Fluent. The point is I need to get all the users, from each team where the teams have a specific eventID. Teams are children of Event. Users are children of Teams. Thanks in advance for your help.
There are only 15 teams per event, but 12 users per team
Here is the Event model:
final class Event: Codable {
var id: Int?
var name: String
}
extension Event {
var teams: Children<Event, Team> {
return children(\.eventID)
}
}
Here is the Team model
final class Team: Codable {
var id: Int?
var name: String
var icon: String
var eventID: Event.ID
}
extension Team {
var user: Parent<Team, Event> {
return parent(\.eventID)
}
}
extension Team {
var users: Children<Team, User> {
return children(\.teamID)
}
}
Here is the User model.
final class User: Codable {
var id: UUID?
var name: String
var email: String
var eventID: Event.ID
var teamID: Team.ID
}
extension User {
var user: Parent<User, Team> {
return parent(\.teamID)
}
}
I need to send an event ID and I want it to return all the users in all the teams
func getUsersForEvent(_ req: Request) throws -> Future<[User]> {
return try req.parameters.next(Event.self).flatMap(to: [User].self) { event in
return try event.teams.query(on: req).all().flatMap(to: [User].self) { team in
return try team.users.query(on: req).all()
}
}
}
You'd query that easily with raw SQL query or using SwifQL lib
Here is an example with SwifQL
struct TeamWithUsers: Content {
let id: UUID
let name, icon: String
let users: [User]
}
func getCategoriesWithProducts(_ req: Request) throws -> Future<[TeamWithUsers]> {
return try req.parameters.next(Event.self).flatMap { event in
let usersSubquery = SwifQL
.select(Fn.coalesce(Fn.array_agg(Fn.to_jsonb(User.table)), PgArray() => .jsonbArray))
.from(User.table)
.where(\User.teamID == \Team.id)
let query = try SwifQL
.select(\Team.id, \Team.name, \Team.icon, |usersSubquery | => "users")
.from(Team.table)
.where(\Team.eventID == event.requireID())
// here you could print the raw query for debugging
// print(query.prepare(.psql).plain)
return query.execute(on: req, as: .psql).all(decoding: TeamWithUsers.self)
}
}
Here's what I came up with, with help from the Ray Wenderlich book. In my task I don't need to return all the users and only need to see teams for 1 event at a time, so I pass in the eventID as a parameter.
Any guidance on how to sort the result by teamScore?
func getTeamsWithUsersForEvent(_ req: Request) throws -> Future<[TeamWithUsers]> {
let currentID = try req.parameters.next(Int.self)
print("currentID \(currentID)")
return Team.query(on: req).filter(\Team.eventID == currentID).all().flatMap(to: [TeamWithUsers].self) { team in
try team.map { team in
try team.users.query(on: req).all().map { users in
TeamWithUsers(
id: team.id,
name: team.name,
icon: team.icon,
eventID: team.eventID,
//rawScore: team.rawScore,
//users: users,
count: users.count,
teamScore: team.rawScore / users.count
)
}
}.flatten(on: req)
}
}
struct TeamWithUsers: Content {
let id: Int?
let name: String
let icon: String
let eventID: Event.ID
//let rawScore: Int
//let users: [User]
let count: Int
let teamScore: Int
}

Remove objects that have duplicate keys

My code...
class Inbox {
var user = "name"
var pmsg = "label"
var match = ""
var resim = "photo"
var userID = ""
var distance = ""
var updated = ""
var isAttendingNow = ""
var isAttendingNowText = ""
init(user : String, pmsg: String, match: String, resim: String, userID : String, distance: String, updated: String, isAttendingNow: String, isAttendingNowText: String) {
self.user = user
self.pmsg = pmsg
self.match = match
self.resim = resim
self.userID = userID
self.distance = distance
self.updated = updated
self.isAttendingNow = isAttendingNow
self.isAttendingNowText = isAttendingNowText
}
}
var arrayOfRels: [Inbox] = [Inbox]()
My goal is to remove duplicate items for userID key.
How can I achieve that?
You could use a set to figure out which useIDs are unique:
func filteredRels(source [Inbox]) -> [Inbox] {
var keys: Set<String> = []
return source.filter {
if keys.contains($0.userID) {
return false
} else {
keys.insert($0.userID)
return true
}
}
}
(Banged out in the editor, so it might need some minor cleanup.)
Use Hashable
class RRR : Hashable {
var hashValue: Int = 0
static func == (lhs: RRR, rhs: RRR) -> Bool {
// in your case set only userID
return lhs.name == rhs.name && lhs.age == rhs.age
}
var name:String
var age:Int
init(name:String,age:Int) {
self.name = name
self.age = age
}
}
//
let arr = [RRR(name: "qqq", age: 12) ,RRR(name: "qqq", age: 12) , RRR(name: "hhhh", age: 12) , RRR(name: "ppppp", age: 12) ]
let set = Array(Set(arr))
print(set) // [ RRR(name: "qqq", age: 12) , RRR(name: "hhhh", age: 12) , RRR(name: "ppppp", age: 12)]
Checkout this:
extension Sequence where Iterator.Element: Hashable {
func uniqueOrdered() -> [Iterator.Element] {
return reduce([Iterator.Element]()) { $0.contains($1) ? $0 : $0 + [$1] }
}
}
class Inbox: Hashable {
...
...
static func == (lhs: User, rhs: User) -> Bool {
return lhs.userID == rhs.userID
}
}
arrayOfRels.uniqueOrdered()
You could do this in a couple of lines using a set:
var unique = Set<String>()
arrayOfRels = arrayOfRels.filter{unique.insert($0.userID).inserted}

In Swift, how can I quickly set my instance variables?

public class User {
var id: Int
var fb_id: String?
var first_name: String?
var last_name: String?
var age: Int?
var distance: Int?
var login_at_pretty: String?
var login_at: Int?
var profile: Profile?
init(id: Int, fb_id: String?, first_name: String?, last_name: String?, age: Int?, distance: Int?, login_at_pretty: String?, login_at: Int?, profile: Profile?){
self.id = id
if let fb_id = fb_id {
self.fb_id = fb_id
}
if let first_name = first_name {
self.first_name = first_name
}
if let last_name = last_name {
self.last_name = last_name
}
if let age = age {
self.age = age
}
if let distance = distance {
self.distance = distance
}
if let login_at_pretty = login_at_pretty {
self.login_at_pretty = login_at_pretty
}
if let login_at = login_at {
self.login_at = login_at
}
if let profile = profile {
self.profile = profile
}
}
}
Is this the quickest way to do it? I feel like I'm over typing.
For your class, you're doing a lot of if-let statements that don't provide anything useful. ie:
if let fb_id = fb_id {
self.fb_id = fb_id
}
fb_id and self.fb_id are optionals, so the binding isn't necessary. Just do:
self.fb_id = fb_id
On that note however, if you're not planning on subclassing, using a struct would provide a memberwise initializer automagically:
public struct User {
var id: Int
var fb_id: String?
var first_name: String?
var last_name: String?
var age: Int?
var distance: Int?
var login_at_pretty: String?
var login_at: Int?
var profile: Profile?
}
Check out Memberwise Initializers for Structure Types in the swift docs

Resources