lancecarlson/couchgo

start_time and end_time confusion

airomega opened this issue · 3 comments

I tried using the couchgo View option and either misunderstood it or hit a bug.

I want to use start_time and end_time as outlined on the couchdb api:
https://wiki.apache.org/couchdb/HTTP_view_API#Querying_Options

The start and end_times should have format [time]

I create url.Values
params := url.Values{lumencouch.IncludeDocs:[]string{"true"}, lumencouch.StartKey:[]string{startKey)}, lumencouch.EndKey:[]string{endKey)}}

However when I query the params end up being:

end_key=1478192152775412649&include_docs=true&start_key=1478192092063988716

It seems that as this was a single value in an array the square braces are removed and the query doesn't work

I tried adding the braces manually
params := url.Values{lumencouch.IncludeDocs:[]string{"true"}, lumencouch.StartKey:[]string{fmt.Sprintf("[%s]",startKey)}, lumencouch.EndKey:[]string{fmt.Sprintf("[%s]",endKey)}}

However, this resulted in the braces being encoded

end_key=%5B1478193145176006200%5D&include_docs=true&start_key=%5B1478193025035134981%5D 200

I'm guessing the keys parameter might be of some assistance here but it's not clear how.

Is this a bug or an I misunderstanding something?

I don't think this an issue in with couchgo - I think this is an issue with couchdb having a bad api.

Golangs net.url requires the Values to be in format map[string][]string

In the couchgo Client UrlString correctly calls the following:

u.RawQuery = values.Encode()

So it's impossible to pass a string array as a value and if you try to pass "[val]" it encodes the square brackets. I don't think Go is wrong here - I think the couchdb API is not well formed.

Any suggestions of a workaround would be welcome but I guess I can just Get the URL directly without using the client.View function.

This is an interesting issue! To be honest, I haven't looked at this library in a long time. Any suggestions how it could be made to work without blowing up existing functionality?

I've found a workaround however it's not pretty and there may be a better method. It also seems I have to bypass couchgo.View. As mentioned previously I don't think this is the fault of couchgo - it seems to be shortsightedness of the couch view API not conforming to JSON standards

The following is a struct I've created which allows me to accomplish my goal - frankly I hate the implementation. I check for the members which require [ ] - extract the values from the params map, delete the values from the params map, encode the params map and then concatenate the values onto the encoded string.

Feel free to suggest a better method :)


type CouchView struct {
    CouchDB    *CouchDatabase
    Design     string
    View       string
    Params     *url.Values
    Keys       *[]string
}

const viewString = `/_design/%s/_view/%s?%s`

func (cv *CouchView) GetResult() (*couch.MultiDocResponse, error) {
    baseUrl := cv.CouchDB.getURL()
    fullURI := baseUrl + cv.getQueryString()
    return queryView(fullURI)
}

func (cv *CouchView) getQueryString() string {
    sKey := cv.Params.Get(StartKey)
    cv.Params.Del(StartKey)
    eKey := cv.Params.Get(EndKey)
    cv.Params.Del(EndKey)

    queryString := cv.Params.Encode()

    if len(sKey) > 0 {
        queryString = queryString + fmt.Sprintf("&%s=[%s]", StartKey, sKey)
    }

    if len(eKey) > 0 {
        queryString = queryString + fmt.Sprintf("&%s=[%s]", EndKey, eKey)
    }

    return fmt.Sprintf(viewString, cv.Design, cv.View, queryString)
}

func queryView(uri string) (res *couch.MultiDocResponse, err error) {
    client, err := couch.NewClientURL(uri)
    if err != nil {
        return nil, err
    }

    body := new(bytes.Buffer)
    req, err := client.NewRequest("GET", uri, body, nil)
    if err != nil {
        return nil, err
    }

    httpResp, err := http.DefaultClient.Do(req)
    if httpResp.StatusCode != 200 {
        return res, errors.New(httpResp.Status)
    }
    if err != nil {
        return nil, err
    }

    respBody, err := ioutil.ReadAll(httpResp.Body)
    if err != nil {
        return nil, err
    }

    if err = json.Unmarshal(respBody, &res); err != nil {
        return nil, err
    }
    return res, nil
}