1

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?

Jonathan Hall
  • 75,165
  • 16
  • 143
  • 189
  • 2
    Instead of using separate variables for `GetUser` method. Provide a struct and then use reflection to build your query. Make it as a helper function and use it where possible. – Eldar Jan 19 '20 at 17:30
  • @Eldar I have to research about reflection since I'm also new to Go. Thank you :) – Christopher Santos Jan 20 '20 at 10:42

2 Answers2

2

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

Eldar
  • 9,781
  • 2
  • 10
  • 35
1

I would suggest to refactor your filter logic as mentioned below:

package main

import (
    "fmt"
    "strings"
)

func getQuery(key, val string, filters *[]string) {
    if val != "" {
        *filters = append(*filters, fmt.Sprintf(`eq("%s", "%s")`, key, val))
    }
}
func main() {
    var filters []string
    email := "demo@demo.com"
    mobileNumber := "123456789"

    getQuery("email", email, &filters)
    getQuery("mobileNumber", mobileNumber, &filters)
    filter := strings.Join(filters, " OR ")
    fmt.Println(filter)
}
Prakash Kumar
  • 2,554
  • 2
  • 18
  • 28