In this video you will learn how to take advantage of Fluent to perform powerful queries on your databases.
This is a companion discussion topic for the original entry at https://www.raywenderlich.com/4493-server-side-swift-with-vapor/lessons/14
In this video you will learn how to take advantage of Fluent to perform powerful queries on your databases.
I tried diving a little into the query
docs and how it is constructed. Can you explain a little more on how you did req.query[String.self, at: "term"]
? The definition of it through Xcode says it returns a QueryContainer(query: http.uri.query ?? "", container: self)
Perhaps my Swift isn’t as good as I thought it was!
Learning lots, thank you
@simonqq the QueryContainer
is the computed property on Request
for a query. So anything in the URL after the ?foo=bar
, which defaults to an empty string if none is supplied. The query container has a subscript implementation here which follows the chain of functions in that file to pull out the query at with the key provided, using the type provided
func search(req : Request) throws -> Future<[Acronym]> {
guard let searchTerm = req.query[String.self, at : "term"] else {
throw Abort(.badRequest, reason: "Missing search term in request")
}
return Acronym.query(on: req).filter(\.short == searchTerm).all()
}
error :
return Acronym.query(on: req).filter(.short == searchTerm).all()
Type of expression is ambiguous without more context
can you help me?
@lashkari did you import Fluent
at the top?
no i’m don’t import Fluent
thanks
I got an error trying to test the search:
[ ERROR ] FluentError.invalidID: Could not convert parameter search?term=OMG to type Int
(Model.swift:258)
and
[ ERROR ] FluentError.invalidID: Could not convert parameter search?term=Oh%20My%20God to type Int
(Model.swift:258)
Here’s the snaps of Rested for those:
https://drive.google.com/open?id=19kkKHGSloOJmibPp-ppOLbuE-bfk7Xjx
https://drive.google.com/open?id=1R_pAR3lxpDGwRUuQD09STdueDnrpShLj
@amyerson there’s a bug with URL queries at the moment, issue is here Query container should not have .wait() on it's decoder · Issue #1567 · vapor/vapor · GitHub
This will need to be fixed before it works. Having said that, the bug you are getting is weird! Could you post your code and your Package.resolved file?
Hello @amyerson, just a quick comment:
return Acronym.query(on: req).filter(.short == searchTerm).all()
Please ignore this if your code is using the “long” for the search.
The search is based on “short” not the “long” and seems the URL you have post is to search the long one. Perhaps you can try with the short instead and see if it works?.
Cheers
Hello @amyerson and @0xtim I am getting the same error now
The error points to Model.swift line 258
guard let id = idType.decode(from: parameter) as? ID else {
throw FluentError(
identifier: "invalidID",
reason: "Could not convert parameter \(parameter) to type `\(ID.self)`",
source: .capture()
)
}
The code I am using is:
func searchHandler(_ req: Request) throws -> Future<[Acronym]> {
guard let searchTerm = req.query[String.self, at: "term"] else {
throw Abort(.badRequest, reason: "Missing search term in request")
}
return try Acronym.query(on: req).group(.or) { or in
try or.filter(\.short == searchTerm)
try or.filter(\.long == searchTerm)
}.all()
}
Thank you very much in advanced
@fjrivash few things to try!
vapor update
to get the latest changes and see if that helpssearchHandler(_:)
?Hello @0xtim,
1.- Done
2.- I have been testing with these 2:
http://localhost:8080/api/acronyms/search?term=Oh My God
http://localhost:8080/api/acronyms/search?term=OMG
3.- acronymRoute.get(“search”, use: searchHandler)
It works now with Rested both URLs. Response
[
{
“id”: 1,
“short”: “OMG”,
“long”: “Oh My God”,
“creatorID”: “CC067DB0-8BD4-42DD-9B9E-0322B2C529A3”
}
]
Thank you very much Tim I am loving this course. I really appreciate the time and effort you are putting on bringing Vapor to us and thank you for replying our questions.
Honestly I did not think I could do it. I am on a journey of learning Vapor to write my own backend for my iOS app, so I took RW’s course on iOS App Development and now this one on Vapor.
I get an error when running http://localhost:8080/api/acronyms/search?term=OMG
“NIO-ELT-#1 (3): Precondition failed: wait() must not be called when on the EventLoop”
The error is in:
~/Vapor/TILApp/.build/checkouts/swift-nio.git-3108475404973543938/Sources/NIO/EventLoopFuture.swift, line 753
extension EventLoopFuture {
/// instructions etc.
public func wait() throws -> T {
if !(self.eventLoop is EmbeddedEventLoop) {
precondition(!eventLoop.inEventLoop, "wait() must not be called when on the EventLoop")
}
// etc.
}
the offending line of code is in:
~/Vapor/TILApp/.build/checkouts/vapor.git-5492988889259800272/Sources/Vapor/HTTP/QueryContainer.swift, line 57
/// Convenience for accessing a single value from the content
public func get<D>(_ type: D.Type = D.self, at keyPath: [BasicKeyRepresentable]) throws -> D
where D: Decodable
{
return try requireDecoder().get(at: keyPath.makeBasicKeys(), from: HTTPBody(string: query), on: container).wait()
}
Here’s the stack above AcronymsController…
Any idea what’s happening?
@geosymani wait()
can’t be called on the main loop (a request thread) - that was a bug that should have been fixed. Run vapor update
and you should get the latest code that fixes it
Hi Tim,
Suppose I wan’t to retrieve multiple Acronyms, but no all. I wan’t to provide a list of acronyms (short field) and expect to have only those returned where
acronym.short in [list_of_acronyms]
For example, I was thinking about something like
http://localhost:8080/api/acronyms/search?term=OMG&term=WTF
which should return all acronyms with short in [OMG, WTF]
Can’t figure out how to do it.
Thanks,
@epinaud it depends on the decoding of the search terms, but you should be able to decode it to [String] I think. Once you’ve done that you can just loop through the array and add the filters to your .or
group
@0xtim Can you explain how to do multi-level queries? For example, an Order
has multiple OrderPoint
elements, and an OrderPoint
has a single Point
element. When I do my
Order.query(on: req).filter(\Order.month == month).all().flatMap(to: View.self) {
I then need to find all the points related to it. In essence I’d do this directly in the SQL database
SELECT p.abbrev, o.amount
FROM order_points o
INNER JOIN points p ON o.points_id = p.id
WHERE o.order_id = 1;
You can see a visual of the three tables at this StackOverflow question
@gargoyle has this been answered for you on StackOverflow?
No, I haven’t asked this one there.
It looks like you OrderPoint
is a pivot between Order
s and Point
s? If so, can you just use Fluent’s sibling stuff to work?
Otherwise take a look in Fluent at join
s - those should help you achieve what you need.