Search interface + impl to find file schema refs by their content ref

This commit is contained in:
Brad Fitzpatrick 2011-06-09 12:55:38 -07:00
parent e5acdb2e38
commit 3b3b682c1e
3 changed files with 115 additions and 27 deletions

View File

@ -197,3 +197,37 @@ func (mi *Indexer) GetBlobMimeType(blob *blobref.BlobRef) (mime string, size int
size, _ = row[1].(int64)
return
}
func (mi *Indexer) ExistingFileSchemas(bytesRef *blobref.BlobRef) (files []*blobref.BlobRef, err os.Error) {
client, err := mi.getConnection()
if err != nil {
return
}
defer func() {
if err == nil {
mi.releaseConnection(client)
} else {
client.Close()
}
}()
err = client.Query(fmt.Sprintf("SELECT fileschemaref FROM files WHERE bytesref=%q", bytesRef.String()))
if err != nil {
return
}
result, err := client.StoreResult()
if err != nil {
return
}
defer client.FreeResult()
for {
row := result.FetchRow()
if row == nil {
break
}
files = append(files, blobref.Parse(row[0].(string)))
}
return
}

View File

@ -68,31 +68,45 @@ func newHandlerFromConfig(ld blobserver.Loader, conf jsonconfig.Obj) (http.Handl
}
type jsonMap map[string]interface{}
func jsonMap() map[string]interface{} {
return make(map[string]interface{})
}
func jsonMapList() []map[string]interface{} {
return make([]map[string]interface{}, 0)
}
func (sh *searchHandler) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
_ = req.Header.Get("X-PrefixHandler-PathBase")
suffix := req.Header.Get("X-PrefixHandler-PathSuffix")
log.Printf("suffix = %q", suffix)
if req.Method != "GET" {
http.Error(rw, "Unsupported method", 400)
return
if req.Method == "GET" {
switch suffix {
case "camli/search", "camli/search/recent":
sh.serveRecentPermanodes(rw, req)
return
case "camli/search/describe":
sh.serveDescribe(rw, req)
return
case "camli/search/claims":
sh.serveClaims(rw, req)
return
case "camli/search/files":
sh.serveFiles(rw, req)
return
}
}
switch suffix {
case "camli/search", "camli/search/recent":
sh.serveRecentPermanodes(rw, req)
case "camli/search/describe":
sh.serveDescribe(rw, req)
case "camli/search/claims":
sh.serveClaims(rw, req)
}
// TODO: discovery for the endpoints & better error message with link to discovery info
ret := jsonMap()
ret["error"] = "Unsupported search path or method"
ret["errorType"] = "input"
httputil.ReturnJson(rw, ret)
}
func (sh *searchHandler) serveRecentPermanodes(rw http.ResponseWriter, req *http.Request) {
ch := make(chan *Result)
results := make([]jsonMap, 0)
results := jsonMapList()
errch := make(chan os.Error)
go func() {
log.Printf("finding recent permanodes for %s", sh.owner)
@ -101,7 +115,7 @@ func (sh *searchHandler) serveRecentPermanodes(rw http.ResponseWriter, req *http
wg := new(sync.WaitGroup)
for res := range ch {
jm := make(jsonMap)
jm := jsonMap()
jm["blobref"] = res.BlobRef.String()
jm["owner"] = res.Signer.String()
t := time.SecondsToUTC(res.LastModTime)
@ -117,7 +131,7 @@ func (sh *searchHandler) serveRecentPermanodes(rw http.ResponseWriter, req *http
err := <-errch
ret := make(jsonMap)
ret := jsonMap()
ret["results"] = results
if err != nil {
// TODO: return error status code
@ -127,7 +141,7 @@ func (sh *searchHandler) serveRecentPermanodes(rw http.ResponseWriter, req *http
}
func (sh *searchHandler) serveClaims(rw http.ResponseWriter, req *http.Request) {
ret := make(jsonMap)
ret := jsonMap()
pn := blobref.Parse(req.FormValue("permanode"))
if pn == nil {
@ -141,10 +155,10 @@ func (sh *searchHandler) serveClaims(rw http.ResponseWriter, req *http.Request)
log.Printf("Error getting claims of %s: %v", pn.String(), err)
} else {
sort.Sort(claims)
jclaims := make([]jsonMap, 0)
jclaims := jsonMapList()
for _, claim := range claims {
jclaim := make(jsonMap)
jclaim := jsonMap()
jclaim["blobref"] = claim.BlobRef.String()
jclaim["signer"] = claim.Signer.String()
jclaim["permanode"] = claim.Permanode.String()
@ -166,19 +180,19 @@ func (sh *searchHandler) serveClaims(rw http.ResponseWriter, req *http.Request)
}
func (sh *searchHandler) serveDescribe(rw http.ResponseWriter, req *http.Request) {
ret := make(jsonMap)
ret := jsonMap()
br := blobref.Parse(req.FormValue("blobref"))
if br == nil {
http.Error(rw, "Missing or invalid 'blobref' param", 400)
return
}
dmap := func(b *blobref.BlobRef) jsonMap {
dmap := func(b *blobref.BlobRef) map[string]interface{} {
bs := b.String()
if m, ok := ret[bs]; ok {
return m.(jsonMap)
return m.(map[string]interface{})
}
m := make(jsonMap)
m := jsonMap()
ret[bs] = m
return m
}
@ -193,7 +207,7 @@ func (sh *searchHandler) serveDescribe(rw http.ResponseWriter, req *http.Request
m["size"] = size
if mime == "application/json; camliType=permanode" {
pm := make(jsonMap)
pm := jsonMap()
m["permanode"] = pm
sh.populatePermanodeFields(pm, br, sh.owner, dmap)
}
@ -203,10 +217,41 @@ func (sh *searchHandler) serveDescribe(rw http.ResponseWriter, req *http.Request
httputil.ReturnJson(rw, ret)
}
func (sh *searchHandler) serveFiles(rw http.ResponseWriter, req *http.Request) {
ret := jsonMap()
defer httputil.ReturnJson(rw, ret)
br := blobref.Parse(req.FormValue("bytesref"))
if br == nil {
// TODO: formalize how errors are returned And make
// ReturnJson set the HTTP status to 400 automatically
// in some cases, if errorType is "input"? Document
// this somewhere. Are there existing JSON
// conventions to use?
ret["error"] = "Missing or invalid 'bytesref' param"
ret["errorType"] = "input"
return
}
files, err := sh.index.ExistingFileSchemas(br)
if err != nil {
ret["error"] = err.String()
ret["errorType"] = "server"
return
}
strList := []string{}
for _, br := range files {
strList = append(strList, br.String())
}
ret["files"] = strList
return
}
// dmap may be nil, returns the jsonMap to populate into
func (sh *searchHandler) populatePermanodeFields(jm jsonMap, pn, signer *blobref.BlobRef, dmap func(b *blobref.BlobRef) jsonMap) {
func (sh *searchHandler) populatePermanodeFields(jm map[string]interface{}, pn, signer *blobref.BlobRef, dmap func(b *blobref.BlobRef) map[string]interface{}) {
jm["content"] = ""
attr := make(jsonMap)
attr := jsonMap()
jm["attr"] = attr
claims, err := sh.index.GetOwnerClaims(pn, signer)
@ -278,7 +323,7 @@ func (sh *searchHandler) populatePermanodeFields(jm jsonMap, pn, signer *blobref
const camliTypePrefix = "application/json; camliType="
func setMimeType(m jsonMap, mime string) {
func setMimeType(m map[string]interface{}, mime string) {
m["type"] = mime
if strings.HasPrefix(mime, camliTypePrefix) {
m["camliType"] = mime[len(camliTypePrefix):]

View File

@ -65,4 +65,13 @@ type Index interface {
GetOwnerClaims(permaNode, owner *blobref.BlobRef) (ClaimList, os.Error)
GetBlobMimeType(blob *blobref.BlobRef) (mime string, size int64, err os.Error)
// ExistingFileSchemas returns 0 or more blobrefs of file
// schema blobs that represent the bytes of a file given in
// bytesRef. The file schema blobs returned are not
// guaranteed to reference chunks that still exist on the
// blobservers, though. It's purely a hint for clients to
// avoid uploads if possible. Before re-using any returned
// blobref they should be checked.
ExistingFileSchemas(bytesRef *blobref.BlobRef) ([]*blobref.BlobRef, os.Error)
}