Author | Topic: Xbase++ meet ArangoDB | |
---|---|---|
Osvaldo Ramirez | Xbase++ meet ArangoDB on Wed, 08 Dec 2021 11:49:46 -0700 Hello guys I want to shared some of my code, this is a little code that I am working on, It is not finish yet and there is not exist documentation yet! If some one found usefull, and want to see it in action, let me know and I can do an skype meeting. Note: It work with Xbase++ 1.9/2.0 using some stuffs from Pablo Botella. Regards Osvaldo Ramirez /// /// Autor : Osvaldo Ramirez /// Arango Class #include "xbp.ch" #include "common.ch" CLASS ArangoDB EXPORTED: VAR url string VAR port numeric VAR jwt string VAR http string VAR user string VAR password strgin VAR LastResponse Last response string of the last call VAR LastStatus Last response string of the last call VAR currentDB METHOD init() Create Objet METHOD login METHOD CreateDatabase METHOD CurrentDatabase Current Database METHOD ListDatabase METHOD SetDatabase METHOD CreateCollection Create Table = DBCREATE() METHOD DropCollection Drop Table = DELETEFILE METHOD TruncateCollection Truncate Table = DELETE ALL METHOD ImportDBF METHOD InsertDocument Insert Record METHOD ReplaceDocument Update Record METHOD UpdateDocument Delete Record METHOD RemoveDocument Delete Record METHOD QueryDocument Delete Record METHOD execute ENDCLASS Init class, this method is execute when the class is initialized ():new() METHOD ArangoDB:init(cURL ,nPort ) DEFAULT cURL TO "http://127.0.0.1" DEFAULT nPort TO 8529 ::url := cURL ::Port := nPort ::jwt := "" RETURN SELF This method is reposible of all call api function, METHOD ArangoDB:execute( cMetodo , cURL , cJson ) * Begin local lError := .f. local oHttp we declared local becuase every http call, it finish if valtype( cJson ) = 'O' Si the cJson is an object, the we cast it to character type, cJson := json_serialize( cJson ) endif oHttp := TServerXMLHTTPRequest():New() /// Pablo Botella oHttp:Open( cMetodo , cUrl , .F. ) oHttp:SetReQuestHeader( 'Connection', 'Keep-Alive') if ! ( "_open/auth" $ cURL ) If the cAPI is not login, then we use the jwt, previusley got from login. oHttp:SetReQuestHeader( 'Authorization' , 'Bearer ' + ::jwt ) endif oHttp:send( cJson ) Save the response ::LastResponse := json_unserialize( oHttp:responseText , @lError) ::LastStatus := oHttp:status RETURN ::LastStatus /* { "username": "root", "password": "" } */ METHOD ArangoDB:login(cUser,cPassword) * Begin local oJson oJson := json_Container():New() Pablo Botella Wrapper to create a Json file oJson:username := cuser oJson:password := cPassword if ::execute( "POST" , ::url + ":" + alltrim( var2char(::Port)) + "/_open/auth" , oJson ) == 200 ::jwt := ::LastResponse:jwt endif RETURN ( ::LastStatus == 200 ) GET /_api/database/current Se graba un json en currentDB y solo regresa name del result //{ "error" : false, "code" : 200, "result" : { "id" : "1", "name" : "_system", "isSystem" : true, "path" : "none" } //} METHOD ArangoDB:CurrentDatabase(lReConnect) * Begin local cAPI := "/_api/database/current" default lReConnect TO .f. if lReConnect .or. ::CurrentDB = NIL ::CurrentDB := NIL ::execute( "GET" , cAPI , NIL ) if valtype( ::LastResponse:error ) = 'O' IF .NOT. ::LastResponse:error ::CurrentDB := ::LastResponse:result:name ENDIF endif endif RETURN ::currentDB /// POST "/_api/collection" /// /// Sample /// /// { /// "name" : "testCollectionUsers", /// "keyOptions" : { /// "type" : "autoincrement", /// "increment" : 5, /// "allowUserKeys" : true /// } /// } METHOD ArangoDB:CreateCollection( cCollectionName ) local oJson local oKeyOptions oKeyOptions := json_Container():New() oKeyOptions:type := "autoincrement" oKeyOptions:increment := 5 oKeyOptions:allowUserKeys := .t. oJson := json_Container():New() Pablo Botella Wrapper oJson:name := cCollectionName oJson:keyOptions := oKeyOptions ::execute( "POST" , ::url + ":" + alltrim( var2char(::Port)) + "/_db/" + ::currentDB+"/_api/collection" , oJson ) RETURN ( ::LastStatus == 200 .or. ::LastStatus = 409 ) METHOD ArangoDB:DropCollection( cCollectionName ) ::execute( "DELETE" , ::url + ":" + alltrim( var2char(::Port)) + "/_db/" + ::currentDB+"/_api/collection/" + cCollectionName ) RETURN ( ::LastStatus == 200 .or. ::LastStatus = 409 ) METHOD ArangoDB:TruncateCollection( cCollectionName ) ::execute( "PUT" , ::url + ":" + alltrim( var2char(::Port)) + "/_db/" + ::currentDB+"/_api/collection/" + cCollectionName ) RETURN ( ::LastStatus == 200 .or. ::LastStatus = 409 ) /// POST /// ///_api/database /// /// Json Sample ... /// /// { /// "name" : "mydb8", /// "users" : [ /// { /// "username" : "admin", /// "passwd" : "secret", /// "active" : true /// }, /// { /// "username" : "tester", /// "passwd" : "test001", /// "active" : false /// } /// ] ///} /// /// Tutorial /// /// https://www.arangodb.com/docs/stable/http/database-database-management.html /// /// This function is the same that execute function, just return True o False, but and execute return the status code METHOD ArangoDB:CreateDatabase( cJson ) RETURN ( ::execute( "POST" ,::url + ":" + alltrim( var2char(::Port)) + "/_api/database" , cJson ) == 201) METHOD ArangoDB:ListDatabase() ::execute( "GET" , ::url + ":" + alltrim( var2char(::Port)) + "/_api/database" ) RETURN ::LastResponse:result METHOD ArangoDB:SetDatabase(cDB) local lReturn := .f. local aListDB := ::ListDatabase() if valtype( aListDB ) = 'A' if ascan( aListDB , cDB ) > 0 Si existe la base de datos, por lo tanto la ponemos como actual. ::CurrentDB := cDB lReturn := .t. endif endif RETURN lReturn METHOD ArangoDB:InsertDocument( cTable, cJsonDocument ) ::execute( "POST" , ::url + ":" + alltrim( var2char(::Port)) + "/_db/" + ::currentDB+"/_api/document/"+cTable , cJsonDocument ) RETURN ::LastStatus /// xCriteria, Could be the document "_key" /// replaceDocument es diferente a updatedocumento, ver la ayuda para un mejor entendimiento METHOD ArangoDB:ReplaceDocument( cTable, cJsonDocument , nKey ) ::execute( "PUT" , ::url + ":" + alltrim( var2char(::Port)) + "/_db/" + ::currentDB+"/_api/document/"+cTable+"/"+var2char(nKey) , cJsonDocument ) RETURN ( ::LastStatus == 201 .or. ::LastStatus == 202 ) /// Ver la documentaion, ya que update, puede ser usando para inserta tambien otro json dentro del json /// ?mergeObjects=false or ?mergeObjects=true /// Tambien ver estos parametros ?returnOld=true, ?returnNew=true /// Ya que un update nos puede arrojar el antiguo valor o el nuevo valor y con esto saber si se inserto bien, aparte del status METHOD ArangoDB:UpdateDocument( cTable, cJsonDocument , nKey ) ::execute( "UPDATE" , ::url + ":" + alltrim( var2char(::Port)) + "/_db/" + ::currentDB+"/_api/document/"+cTable+"/"+var2char(nKey) , cJsonDocument ) RETURN ( ::LastStatus == 201 .or. ::LastStatus == 202 ) METHOD ArangoDB:RemoveDocument( cTable, nKey ) ::execute( "DELETE" , ::url + ":" + alltrim( var2char(::Port)) + "/_db/" + ::currentDB+"/_api/document/"+cTable+"/"+var2char(nKey) ) RETURN ( ::LastStatus == 200 .or. ::LastStatus == 202 ) /// { "query" : "FOR u IN contactos LIMIT 5 RETURN u", "count" : true, "batchSize" : 2 } /// las respuesta puede tener el valor hasmore = true y tambien tien un Id, es el el cual podemos usar para recuperar el cursor METHOD ArangoDB:QueryDocument( xCriteria ) if valtype( xCriteria ) ='N' Signfica que ya hubo una busqueda y se requiere mas datos del cursor , PAGINACION ::execute( "POST" , ::url + ":" + alltrim( var2char(::Port)) + "/_db/" + ::currentDB+"/_api/cursor/"+var2char(xCriteria) ) elseif valtype( xCriteria ) $ 'OC' si es un json o un caracter, entonces apenas vamos a hacer el query, PAGINACION ::execute( "POST" , ::url + ":" + alltrim( var2char(::Port)) + "/_db/" + ::currentDB+"/_api/cursor" , xCriteria ) endif RETURN ::LastResponse /// { "query" : "FOR u IN contactos LIMIT 5 RETURN u", "count" : true, "batchSize" : 2 } /// las respuesta puede tener el valor hasmore = true y tambien tien un Id, es el el cual podemos usar para recuperar el cursor METHOD ArangoDB:ImportDBF( cDBF , cCollectionName ) * Begin local nFields local aFields local oJsonTemplate local oJson local xValue , i local lReturn := .f. DEFAULT cCollectionName TO "" if FExists( cDBF ) IF ! empty( cCollectionName ) cCollectionName := lower( cCollectionName ) SELECT 0 use ( cDBF ) SHARED alias ( cCollectionName ) ELSE SELECT 0 use ( cDBF ) SHARED cCollectionName := lower( alias() ) ENDIF IF neterr() RETURN .f. ENDIF aFields := dbstruct() nFields := len( aFields ) Creamos la tabla if ::CreateCollection(cCollectionName) Preparamos un json vacio con la estructura oJsonTemplate := json_Container():New() Pablo Botella Wrapper to create a Json file for i := 1 to nFields oJsonTemplate:&(lower(aFields[i][1])) := NIL next do while ! ( cCollectionName )->(eof()) oJson := oJsonTemplate for i := 1 to nFields xValue := &( cCollectionName + "->"+aFields[i][1]) do case case aFields[i][2] = 'C' xValue := alltrim(xValue) case aFields[i][2] = 'D' xValue := dtos( xValue ) case aFields[i][2] = 'L' xValue := iif( xvalue , "TRUE","FALSE" ) endcase oJson:&(lower(aFields[i][1])) := xValue next ::InsertDocument(cCollectionName, json_serialize( oJson )) (cCollectionName)->(DbSkip(1)) enddo lReturn := .t. endif close all// ( cCollectionName ) endif RETURN lReturn |