gographqldgraphdgraph-dgo

How to dynamically build query filters


I'm and using Go to setup my own API. I'm kind of stuck right now because of how I wrote the code to dynamically create/apply the query filter. It works but I'm wondering if there is a better way to do the scenario below.

For example, I have a search page with check boxes (1 for email and 1 for name) to narrow the search.

// If I checked the email, the query would be like this
query findOne() {
  user(func: type(user)) @filter(eq(email, "john.doe@email.com")) {
    name
    email
    age
    home_address
  }
}

// If name checkedbox is also checked, it would be like this
query findOne() {
  user(func: type(user)) @filter(eq(email, "john") OR eq(name, "john")) {
    name
    email
    age
    home_address
  }
}

This is what I got so far and I think there is a better way to do this:

func (s *Service) GetUser(email, name string) (*Users, error) {
    c := db.NewClient()
    defer db.Close()
    var u Users
    var filter string

    if email != "" && mobileNumber != "" {
      filter = fmt.Sprintf(`eq(email, "%s") OR eq(mobileNumber, "%s")`, email, mobileNumber)
    } else if email != "" && mobileNumber == "" {
      filter = fmt.Sprintf(`eq(email, "%s")`, email)
    } else if email == "" && mobileNumber != "" {
      filter = fmt.Sprintf(`eq(mobileNumber, "%s")`, mobileNumber)
    }

    q := fmt.Sprintf(`query findOne() {
        users(func: type("user")) @filter(%s) {
            name
            email
            home_address
            contact_number
        }
    }`, filter)
    ctx := context.Background()
    res, err := c.NewTxn().Query(ctx, q)
    if err != nil {
        return nil, err
    }
    if err = json.Unmarshal(res.Json, &u); err != nil {
        return nil, err
    }
    return &u, nil
}

Is there a better way to do this instead of creating long condition?


Solution

  • Here is the reflection version of it. Basically it enumerates fields, gets the value and field names to build an array of string based on them. Please not that i'm not well experienced it might also require some improvements.

    import (
        "fmt"
        "reflect"
        "strings"
    )
    
    type User struct {
        Id       int    
        FullName string 
        Phone    string 
        Mail     string 
    }
    
    func main() {
        u := &User{Id: 10, FullName: "John", Mail: "john@mail"}
        u2 := struct {
            id   int
            name string
        }{10, "john"};
       // inline struct
        q := getQuery(&u2, "OR")
        fmt.Println(q)
    
       // typed struct
        q = getQuery(u, "AND")
        fmt.Println(q)
    }
    
    func getQuery(target interface{}, join string) string {
        var filters []string
        val := reflect.ValueOf(target).Elem()
    
        for i := 0; i < val.NumField(); i++ {
            value := val.Field(i)
            s :=fmt.Sprintf("%v",value);
            // this little trick is to check if it is an empty value
            // so don't generate empty condition expressions
            if s == "" {
                 continue
            }
            fieldType := val.Type().Field(i)
            filters = append(filters, fmt.Sprintf(" eq(%s, %v) ", fieldType.Name, value))
        }
    
        return strings.Join(filters, join)
    }
    

    Here is the playground