scylladb / gocqlx

All-In-One: CQL query builder, ORM and migration tool
Apache License 2.0
929 stars 122 forks source link

How to pass values to a query #253

Closed gronka closed 1 year ago

gronka commented 1 year ago

I'm not seeing anything clear in the docs. Full examples would help. I've played with this many ways, and also with qb.EqNamed().

I'd also like to know how to select with a different type, such as UUID. At first I thought my error had something to do with converting the UUID to string.

cluster := gocql.NewCluster("127.0.0.1")
var Csm *gocqlx.Session
Csm, _ = gocqlx.WrapSession(cluster.CreateSession())

...

stmt, _ := qb.Select("aim.surfers").Where(qb.Eq("email")).Limit(1).ToCql()
fmt.Println(stmt)
names := []string{"2@2.2"}
//names := []string{"email", "2@2.2"}
fmt.Println(names)
q := uy.Csm.Query(stmt, names).BindStruct(&out)

Error:

ERR bind error: could not find name "2@2.2" in &models.SurfersStruct{...

out is referencing the SurfersStruct created by schemagen. I am able to select the user when I handwrite the CQL:

type SurfersStruct struct {
    AccountStatus   string
    AtName          string
    Bio             string
    City            string
    Email           string
    FirstName       string
    GoogleId        string
    IsAdmin         bool
    IsEmailVerified bool
    IsPhoneVerified bool
    Language        string
    LastName        string
    Lat             float32
    Long            float32
    OnlineStatus    string
    Password        string
    Phone           string
    PhoneIsoCode    string
    PhonePrefixCode string
    Popularity      float32
    ProjectId       [16]byte
    PublicStatus    string
    ScreenName      string
    SurferId        [16]byte
    TimeCreated     int64
    TimeUpdated     int64
}
mykaul commented 1 year ago

Are you asking specifically about Go? Have you looked at the examples @ https://github.com/scylladb/gocql (root dir, there are several ones) ?

mykaul commented 1 year ago

Are you asking specifically about Go? Have you looked at the examples @ https://github.com/scylladb/gocql (root dir, there are several ones) ?

Or the examples in this repo?

gronka commented 1 year ago

Are you asking specifically about Go? Have you looked at the examples @ https://github.com/scylladb/gocql (root dir, there are several ones) ?

gocqlx. I've used gocql a lot; I'd like to handwrite fewer queries. I see lots of examples in the tests, but it's taking me a long time to piece them together into a select * from users where email='my@email.com' statement - and after that where user_id={uuid_var}. I see strings being passed around, and byte arrays being used instead of gocql.UUID, so I expect that to take a bit of time to handle as well.

gronka commented 1 year ago

Great! I was able to figure my mistakes from this issue: https://github.com/scylladb/gocqlx/issues/109

My solution:

stmt, names := qb.Select("aim.surfers").Where(qb.Eq("surfer_uuid")).Limit(1).ToCql()
res := uy.Csm.Query(stmt, names).Bind(in.SurferUuid)
if err := res.GetRelease(&out); err != nil {                                  
  uy.Error(err)                                                               
}  

I appreciate the library greatly. My thoughts as a newcomer: the docline for GetRelease did not convey what it does to me; maybe it's using driver-side terminology. GetRelease calls Get and releases the query, a released query cannot be reused.

Also Queryx.Names is a similar example: Queryx is a wrapper around gocql.Query which adds struct binding capabilities.

mykaul commented 1 year ago

@avelanarius - anything we can do to improve the documentation around GetRelease() ?

gronka commented 1 year ago

Just to share - my original interpretations that tripped me up:

  1. I thought the names variable was used to bind values to a query. Because the querybuilder returns it, it seemed like it was used to build the query - you can see I was trying to cram prepared query parameters into names lol
  2. I thought Bind() was used to bind values to the output struct (as opposed to binding to the query). The doc for Bind() seems fine, but I never thought to look it up. I assumed it was a wrapper for gocql.Iter.MapScan/StructScan
  3. I assumed GetRelease() was a simple cleanup function. What tripped me up here is the doc for Get() says scans first row into a destination, and it simply didn't click in my brain that destination meant struct to unpack the query result. The language was too abstracted for me. It does mention Iter.StructScan, but it just didn't click for me; it's been a long time since I've had to deal with Iter directly

I'm still not sure what names does - at this point I guess BindMap() uses it, but I'm not sure if anything else does. There were no compiler errors since many functions accept ...string or ...interface{} (probably unavoidable)

imo, an examples.go file with some comments might be best/simplest. Compile time feedback could be given if it's possible to create a type Names ...