Managing TLS Identities
Description — Couchbase Lite - this content covers how to manage TLS identities using Couchbase Lite
Related Content — API Reference | Passive Peer | Active Peer
Description — Couchbase Lite - this content covers how to manage TLS identities using Couchbase Lite
Overview
-
This describes the configuration and management of TLS identities
-
API: TLSIdentity
API References
You can find C API References here.
Creating TLS Identity
There are couple of options by which TLS Identity is created:
-
You can use the anonymous self-signed cert auto-generated by Couchbase Lite
-
You can import a cert to be bundled with the app and-or stored in the keychain
Use Anonymous Cert
Anonymous certification uses the self signed certificate auto-generated by Couchbase Lite when TLS is enabled, but no TLSIdentity is provided.
listenerConfig.disableTLS = false // Use with anonymous self signed cert
listenerConfig.tlsIdentity = nil
Import a Cert
Use the TLSIdentity class’s importIdentity(withData:password:label:error:) method to import a certificate that can be bundled with the app and-or added to the keychain.
-
First check the keychain to see if the identity already exists
-
Check for an existing resource bundle
// CBL Version 3.0.0 BETA #include <cbl/CouchbaseLite.h> #include <time.h> #include <inttypes.h> #ifdef _MSC_VER #include <direct.h> #include <Shlwapi.h> void usleep(unsigned int us) { Sleep(us / 1000); } #else #include <unistd.h> #endif static CBLDatabase* kDatabase; // Helper for stop replicator in the code snippet static void stop_replicator(CBLReplicator* replicator) { CBLReplicator_Stop(replicator); while(CBLReplicator_Status(replicator).activity != kCBLReplicatorStopped) { printf("Waiting for replicator to stop..."); usleep(200000); } CBLReplicator_Release(replicator); } // BEGIN lower-level function declarations // DOCS NOTE -- // These functions are referred to in subsequent code samples. // Their tags will ensure they are shown alongide the usage examples. // Functions used in more than one place may hve multiple tags. // tag::p2p-act-rep-func[] // tag::p2p-act-rep-add-change-listener[] // tag::replication-error-handling[] // Purpose -- illustrate a simple change listener static void simpleChangeListener(void* context, CBLReplicator* repl, const CBLReplicatorStatus* status) { if(status->error.code != 0) { printf("Error %d / %d\n", status->error.domain, status->error.code); } } // end::replication-error-handling[] // end::p2p-act-rep-add-change-listener[] // end::p2p-act-rep-func[] static const CBLDocument* simpleConflictResolver_localWins( void* context, FLString documentID, const CBLDocument* localDocument, const CBLDocument* remoteDocument) { return localDocument; } // tag::replication-push-filter[] // tag::replication-pull-filter[] // Purpose -- illustrate a simple replication filter function static bool simpleReplicationFilter(void* context, CBLDocument* argDoc, CBLDocumentFlags argFlags) { bool result = (argFlags == kCBLDocumentFlagsDeleted); return result; } // end::replication-push-filter[] // end::replication-pull-filter[] // tag::SimpleReplicationDocumentListener[] // Purpose -- Illustrate a simple replication document listener static void SimpleReplicationDocumentListener( void *context, CBLReplicator *replicator, bool isPush, unsigned numDocuments, const CBLReplicatedDocument *documents) { if(isPush) { printf("We pushed %d documents",numDocuments); } } // END lower-level function declarations static void getting_started_change_listener(void* context, CBLReplicator* repl, const CBLReplicatorStatus* status) { if(status->error.code != 0) { printf("Error %d / %d\n", status->error.domain, status->error.code); } } // Page=build and run // url=https://docs-staging.couchbase.com/couchbase-lite/current/c/gs-build.html static void getting_started() { // tag::getting-started[] // Purpose-- provide an overview of available crud and sync functionality // // Get the database (and create it if it doesn't exist) CBLError err; CBLDatabase* database = CBLDatabase_Open(FLSTR("mydb"), NULL, &err); if(!database) { // Error handling. For brevity, this is truncated in the rest of the snippet // and omitted in other doc code snippets fprintf(stderr, "Error opening database (%d / %d)\n", err.domain, err.code); FLSliceResult msg = CBLError_Message(&err); fprintf(stderr, "%.*s\n", (int)msg.size, (const char *)msg.buf); FLSliceResult_Release(msg); return; } // The lack of 'const' indicates this document is mutable // Create a new document (i.e. a record) in the database CBLDocument* mutableDoc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(mutableDoc); FLMutableDict_SetFloat(properties, FLSTR("version"), 3.0f); // Save it to the database if(!CBLDatabase_SaveDocument(database, mutableDoc, &err)) { // Failed to save, do error handling as above return; } // Since we will release the document, make a copy of the ID since it // is an internal pointer. Whenever we create or get an FLSliceResult // or FLStringResult we will need to free it later too! FLStringResult id = FLSlice_Copy(CBLDocument_ID(mutableDoc)); CBLDocument_Release(mutableDoc); // Update a document mutableDoc = CBLDatabase_GetMutableDocument(database, FLSliceResult_AsSlice(id), &err); if(!mutableDoc) { // Failed to retrieve, do error handling as above. NOTE: error code 0 simply means // the document does not exist. return; } properties = CBLDocument_MutableProperties(mutableDoc); FLMutableDict_SetString(properties, FLSTR("language"), FLSTR("C")); if(!CBLDatabase_SaveDocument(database, mutableDoc, &err)) { // Failed to save, do error handling as above return; } // Note const here, means readonly const CBLDocument* docAgain = CBLDatabase_GetDocument(database, FLSliceResult_AsSlice(id), &err); if(!docAgain) { // Failed to retrieve, do error handling as above. NOTE: error code 0 simply means // the document does not exist. return; } // No copy this time, so no release later (notice it is not FLStringResult this time) FLString retrievedID = CBLDocument_ID(docAgain); FLDict retrievedProperties = CBLDocument_Properties(docAgain); FLString retrievedLanguage = FLValue_AsString(FLDict_Get(retrievedProperties, FLSTR("language"))); printf("Document ID :: %.*s\n", (int)retrievedID.size, (const char *)retrievedID.buf); printf("Learning %.*s\n", (int)retrievedLanguage.size, (const char *)retrievedLanguage.buf); CBLDocument_Release(mutableDoc); CBLDocument_Release(docAgain); FLSliceResult_Release(id); // tag::query-syntax-n1ql[] // Create a query to fetch documents of type SDK int errorPos; CBLQuery* query = CBLDatabase_CreateQuery(database, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE type = \"SDK\""), &errorPos, &err); if(!query) { // Failed to create query, do error handling as above // Note that errorPos will contain the position in the N1QL string // that the parse failed, if applicable return; } CBLResultSet* result = CBLQuery_Execute(query, &err); if(!result) { // Failed to run query, do error handling as above return; } // end::query-syntax-n1ql[] // TODO: Result set count? CBLResultSet_Release(result); CBLQuery_Release(query); // Create replicator to push and pull changes to and from the cloud CBLEndpoint* targetEndpoint = CBLEndpoint_CreateWithURL(FLSTR("ws://localhost:4984/getting-started-db"), &err); if(!targetEndpoint) { // Failed to create endpoint, do error handling as above return; } CBLReplicatorConfiguration replConfig; CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("john"), FLSTR("pass")); memset(&replConfig, 0, sizeof(replConfig)); replConfig.database = database; replConfig.endpoint = targetEndpoint; replConfig.authenticator = basicAuth; CBLReplicator* replicator = CBLReplicator_Create(&replConfig, &err); CBLAuth_Free(basicAuth); CBLEndpoint_Free(targetEndpoint); if(!replicator) { // Failed to create replicator, do error handling as above return; } // Assume a function like the simple following // // static void getting_started_change_listener(void* context, CBLReplicator* repl, const CBLReplicatorStatus* status) { // if(status->error.code != 0) { // printf("Error %d / %d\n", status->error.domain, status->error.code); // } // } CBLListenerToken* token = CBLReplicator_AddChangeListener(replicator, getting_started_change_listener, NULL); CBLReplicator_Start(replicator, false); // Later, stop and release the replicator // end::getting-started[] // When finished release resources ... eg CBLListener_Remove(token); stop_replicator(replicator); } // tag::local-win-conflict-resolver[] static const CBLDocument* local_win_conflict_resolver(void* context, FLString documentID, const CBLDocument* localDocument, const CBLDocument* remoteDocument) { return localDocument; } // end::local-win-conflict-resolver[] // tag::remote-win-conflict-resolver[] static const CBLDocument* remote_win_conflict_resolver(void* context, FLString documentID, const CBLDocument* localDocument, const CBLDocument* remoteDocument) { return remoteDocument; } // end::remote-win-conflict-resolver[] // tag::merge-conflict-resolver[] static const CBLDocument* merge_conflict_resolver(void* context, FLString documentID, const CBLDocument* localDocument, const CBLDocument* remoteDocument) { FLDict localProps = CBLDocument_Properties(localDocument); FLDict remoteProps = CBLDocument_Properties(remoteDocument); FLMutableDict mergeProps = FLDict_MutableCopy(localProps, kFLDefaultCopy); FLDictIterator d; FLDictIterator_Begin(localProps, &d); FLValue value; while((value = FLDictIterator_GetValue(&d))) { FLString key = FLDictIterator_GetKeyString(&d); if(FLDict_Get(mergeProps, key)) { continue; } FLMutableDict_SetValue(mergeProps, key, value); FLDictIterator_Next(&d); } CBLDocument* mergeDocument = CBLDocument_CreateWithID(documentID); CBLDocument_SetProperties(mergeDocument, mergeProps); FLMutableDict_Release(mergeProps); return mergeDocument; } // end::merge-conflict-resolver[] static void test_replicator_conflict_resolve() { CBLDatabase* database = kDatabase; // tag::replication-conflict-resolver[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLEndpoint* target = CBLEndpoint_CreateWithURL(FLSTR("ws://localhost:4984/mydatabase"), &err); CBLReplicatorConfiguration replConfig; memset(&replConfig, 0, sizeof(replConfig)); replConfig.database = database; replConfig.endpoint = target; replConfig.conflictResolver = local_win_conflict_resolver; CBLReplicator* replicator = CBLReplicator_Create(&replConfig, &err); CBLEndpoint_Free(target); CBLReplicator_Start(replicator, false); // end::replication-conflict-resolver[] stop_replicator(replicator); } static bool custom_conflict_handler(void* context, CBLDocument* documentBeingSaved, const CBLDocument* conflictingDocument) { FLDict currentProps = CBLDocument_Properties(conflictingDocument); FLDict updatedProps = CBLDocument_Properties(documentBeingSaved); FLMutableDict newProps = FLDict_MutableCopy(updatedProps, kFLDefaultCopy); FLDictIterator d; FLDictIterator_Begin(currentProps, &d); FLValue currentValue; while((currentValue = FLDictIterator_GetValue(&d))) { FLString currentKey = FLDictIterator_GetKeyString(&d); if(FLDict_Get(newProps, currentKey)) { continue; } FLMutableDict_SetValue(newProps, currentKey, currentValue); FLDictIterator_Next(&d); } return true; } static void test_save_with_conflict_handler() { CBLDatabase* database = kDatabase; // tag::update-document-with-conflict-handler[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDocument* mutableDocument = CBLDatabase_GetMutableDocument(database, FLSTR("xyz"), &err); FLMutableDict properties = CBLDocument_MutableProperties(mutableDocument); FLMutableDict_SetString(properties, FLSTR("name"), FLSTR("apples")); /* static bool custom_conflict_handler(void* context, CBLDocument* documentBeingSaved, const CBLDocument* conflictingDocument) { FLDict currentProps = CBLDocument_Properties(conflictingDocument); FLDict updatedProps = CBLDocument_Properties(documentBeingSaved); FLMutableDict newProps = FLDict_MutableCopy(updatedProps, kFLDefaultCopy); FLDictIterator d; FLDictIterator_Begin(currentProps, &d); FLSlice currentKey = FLDictIterator_GetKeyString(&d); for(; currentKey.buf; currentKey = FLDictIterator_GetKeyString(&d)) { if(FLDict_Get(newProps, currentKey)) { continue; } FLValue currentValue = FLDictIterator_GetValue(&d); FLMutableDict_SetValue(newProps, currentKey, currentValue); } return true; } */ CBLDatabase_SaveDocumentWithConflictHandler(database, mutableDocument, custom_conflict_handler, NULL, &err); // end::update-document-with-conflict-handler[] } static void use_encryption() { #ifdef COUCHBASE_ENTERPRISE // tag::database-encryption[] // NOTE: No error handling, for brevity (see getting started) CBLDatabaseConfiguration config = CBLDatabaseConfiguration_Default(); // This returns a boolean, so check it in production code CBLEncryptionKey_FromPassword(&config.encryptionKey, FLSTR("password")); CBLError err; CBLDatabase* db = CBLDatabase_Open(FLSTR("seekrit"), &config, &err); // Change the encryption key (or add encryption if the DB is unencrypted) CBLEncryptionKey betterKey; CBLEncryptionKey_FromPassword(&betterKey, FLSTR("betterpassw0rd")); CBLDatabase_ChangeEncryptionKey(db, &betterKey, &err); // Remove encryption CBLDatabase_ChangeEncryptionKey(db, NULL, &err); // end::database-encryption[] #endif } static void reset_replicator_checkpoint() { CBLEndpoint* target = CBLEndpoint_CreateWithURL(FLSTR("ws://localhost:4984/db"), NULL); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = kDatabase; config.endpoint = target; CBLReplicator* replicator = CBLReplicator_Create(&config, NULL); CBLEndpoint_Free(target); // tag::replication-reset-checkpoint-full[] // replicator is a CBLReplicator* instance CBLReplicator_Start(replicator, true); (1) // end::replication-reset-checkpoint-full[] stop_replicator(replicator); } static void read_1x_attachment() { CBLDocument* document = CBLDocument_Create(); // tag::1x-attachment[] // NOTE: No error handling, for brevity (see getting started) CBLError err; FLDict properties = CBLDocument_Properties(document); FLDict attachments = FLValue_AsDict(FLDict_Get(properties, FLSTR("_attachments"))); const CBLBlob* avatar = FLDict_GetBlob(FLValue_AsDict(FLDict_Get(attachments, FLSTR("avatar")))); FLSliceResult content = CBLBlob_Content(avatar, &err); FLSliceResult_Release(content); // end::1x-attachment[] CBLDocument_Release(document); } static void create_new_database() { // tag::new-database[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDatabase* db = CBLDatabase_Open(FLSTR("my-database"), NULL, &err); // end::new-database[] kDatabase = db; } static void close_database() { CBLDatabase* db = kDatabase; // tag::close-database[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDatabase_Close(db, &err); // end::close-database[] } static void change_logging() { // tag::logging[] // For output to stdout CBLLog_SetConsoleLevel(kCBLLogVerbose); // For output to custom logging CBLLog_SetCallbackLevel(kCBLLogVerbose); // end::logging[] } static void load_prebuilt() { CBL_DeleteDatabase(FLSTR("travel-sample.cblite2"), kFLSliceNull, NULL); // tag::prebuilt-database[] // Note: Getting the path to a database is platform-specific. For desktop (including RPi) // this can be a simple filesystem path. For iOS you need to get the path from the // main bundle. For Android you need to extract it from your assets to a temporary directory // and then pass that path. // NOTE: No error handling, for brevity (see getting started) CBLError err; const char* path = "/path/to/travel-sample.cblite2"; if(!CBL_DatabaseExists(FLSTR("travel-sample.cblite2"), kFLSliceNull)) { CBL_CopyDatabase(FLStr(path), FLSTR("travel-sample"), NULL, &err); } // end::prebuilt-database[] CBLDatabase_Close(kDatabase, NULL); CBLDatabase_Release(kDatabase); kDatabase = CBLDatabase_Open(FLSTR("travel-sample"), NULL, NULL); } static void query_deleted_document() { CBLDatabase* db = kDatabase; // tag::query-deleted-documents[] // Query documents that have been deleted // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id FROM _ WHERE meta().deleted"), NULL, &err); // end::query-deleted-documents[] CBLQuery_Release(query); } static void create_document() { CBLDatabase* db = kDatabase; // tag::initializer[] // NOTE: No error handling, for brevity (see getting started) CBLDocument* newTask = CBLDocument_CreateWithID(FLSTR("xyz")); FLMutableDict properties = CBLDocument_MutableProperties(newTask); FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("task")); FLMutableDict_SetString(properties, FLSTR("owner"), FLSTR("todo")); // Storing time in millisecond, bluntly FLMutableDict_SetUInt(properties, FLSTR("createdAt"), time(NULL) * 1000); CBLError err; CBLDatabase_SaveDocument(db, newTask, &err); CBLDocument_Release(newTask); // end::initializer[] } static void update_document() { CBLDatabase* db = kDatabase; // tag::update-document[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDocument* mutableDocument = CBLDatabase_GetMutableDocument(db, FLSTR("xyz"), &err); FLMutableDict properties = CBLDocument_MutableProperties(mutableDocument); FLMutableDict_SetString(properties, FLSTR("name"), FLSTR("apples")); CBLDatabase_SaveDocument(db, mutableDocument, &err); CBLDocument_Release(mutableDocument); // end::update-document[] } // Note use_typed_accessors not applicable static void do_batch_operation() { CBLDatabase* db = kDatabase; // tag::batch[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDatabase_BeginTransaction(db, &err); char buffer[7]; for(int i = 0; i < 10; i++) { CBLDocument* doc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(doc); FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("user")); sprintf(buffer, "user %d", i); FLMutableDict_SetString(properties, FLSTR("name"), FLStr(buffer)); FLMutableDict_SetBool(properties, FLSTR("admin"), false); CBLDatabase_SaveDocument(db, doc, &err); CBLDocument_Release(doc); printf("Saved user document %s\n", buffer); } CBLDatabase_EndTransaction(db, true, &err); // end::batch[] } static void document_listener(void* context, const CBLDatabase* db, FLString id) { CBLError err; const CBLDocument* doc = CBLDatabase_GetDocument(db, id, &err); FLDict properties = CBLDocument_Properties(doc); FLString verified_account = FLValue_AsString(FLDict_Get(properties, FLSTR("verified_account"))); printf("Status :: %.*s\n", (int)verified_account.size, (const char *)verified_account.buf); CBLDocument_Release(doc); } static void database_change_listener() { CBLDatabase* db = kDatabase; // tag::document-listener[] /* static void document_listener(void* context, const CBLDatabase* db, FLString id) { CBLError err; const CBLDocument* doc = CBLDatabase_GetDocument(db, id, &err); FLDict properties = CBLDocument_Properties(doc); FLString verified_account = FLValue_AsString(FLDict_Get(properties, FLSTR("verified_account"))); printf("Status :: %.*s\n", (int)verified_account.size, (const char *)verified_account.buf); CBLDocument_Release(doc); } */ CBLListenerToken* token = CBLDatabase_AddDocumentChangeListener(db, FLSTR("user.john"), document_listener, NULL); // end::document-listener[] CBLListener_Remove(token); } static void document_expiration() { CBLDatabase* db = kDatabase; // tag::document-expiration[] // Purge the document one day from now // Overly simplistic for example purposes // NOTE: API takes milliseconds // NOTE: No error handling, for brevity (see getting started) time_t ttl = time(NULL) + 24 * 60 * 60; ttl *= 1000; CBLError err; CBLDatabase_SetDocumentExpiration(db, FLSTR("doc123"), ttl, &err); // Reset expiration CBLDatabase_SetDocumentExpiration(db, FLSTR("doc1"), 0, &err); // Query documents that will be expired in less than five minutes time_t fiveMinutesFromNow = time(NULL) + 5 * 60; fiveMinutesFromNow *= 1000; FLMutableDict parameters = FLMutableDict_New(); FLMutableDict_SetInt(parameters, FLSTR("five_minutes"), fiveMinutesFromNow); CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id FROM _ WHERE meta().expiration < $five_minutes"), NULL, &err); CBLQuery_SetParameters(query, parameters); FLMutableDict_Release(parameters); // end::document-expiration[] } static void use_blob() { CBLDatabase* db = kDatabase; CBLDocument* newTask = CBLDocument_Create(); // tag::blob[] // Note: Reading the data is implementation dependent, as with prebuilt databases // NOTE: No error handling, for brevity (see getting started) uint8_t buffer[128000]; FILE* avatar_file = fopen("avatar.jpg", "rb"); size_t read = fread(buffer, 1, 128000, avatar_file); (2) FLSliceResult avatar = FLSliceResult_CreateWith(buffer, read); CBLBlob* blob = CBLBlob_CreateWithData(FLSTR("image/jpeg"), FLSliceResult_AsSlice(avatar)); (3) FLSliceResult_Release(avatar); // TODO: Create shortcut blob method CBLError err; FLMutableDict properties = CBLDocument_MutableProperties(newTask); FLSlot_SetBlob(FLMutableDict_Set(properties, FLSTR("avatar")), blob); CBLDatabase_SaveDocument(db, newTask, &err); (4) // end::blob[] CBLDocument_Release(newTask); CBLBlob_Release(blob); } static void doc_json() { CBLDatabase* db = kDatabase; // tag::tojson-document[] FLString json = FLSTR("{\"id\":\"1002\",\"type\":\"hotel\",\"name\":\"Hotel Ned\",\"city\":\"Balmain\",\"country\":\"Australia\"}"); // Create a document and set the JSON data to the document CBLError err; CBLDocument* newDoc = CBLDocument_CreateWithID(FLSTR("hotel_1002")); CBLDocument_SetJSON(newDoc, json, &err); // Save the document to the database CBLDatabase_SaveDocument(db, newDoc, &err); // Release created doc after using it CBLDocument_Release(newDoc); // Get the document from the database const CBLDocument* doc = CBLDatabase_GetDocument(db, FLSTR("hotel_1002"), &err); // Get document body as JSON FLSliceResult docJson = CBLDocument_CreateJSON(doc); printf("Document in JSON :: %.*s\n", (int)docJson.size, (const char *)docJson.buf); // Release JSON data after using it FLSliceResult_Release(docJson); // Release doc read from the database after using it CBLDocument_Release(doc); // end::tojson-document[] } static void dict_json() { // tag::tojson-dictionary[] FLString json = FLSTR("{\"id\":\"1002\",\"type\":\"hotel\",\"name\":\"Hotel Ned\",\"city\":\"Balmain\",\"country\":\"Australia\"}"); // Create a dictionary from the JSON string FLError err; FLSliceResult jsonData1 = FLData_ConvertJSON(json, &err); FLDict hotel = FLValue_AsDict(FLValue_FromData(FLSliceResult_AsSlice(jsonData1), kFLTrusted)); // Iterate through the dictionary FLDictIterator iter; FLDictIterator_Begin(hotel, &iter); FLValue value; while (NULL != (value = FLDictIterator_GetValue(&iter))) { FLString key = FLDictIterator_GetKeyString(&iter); FLString strValue = FLValue_AsString(value); printf("%.*s :: %.*s\n", (int)key.size, (const char*)key.buf, (int)strValue.size, (const char*)strValue.buf); FLDictIterator_Next(&iter); } // Convert the dictionary to JSON FLSliceResult jsonData2 = FLValue_ToJSON((FLValue)hotel); printf("Hotel in JSON :: %.*s\n", (int)jsonData2.size, (const char *)jsonData2.buf); // Release JSON data after finish using it FLSliceResult_Release(jsonData1); FLSliceResult_Release(jsonData2); // end::tojson-dictionary[] } static void array_json() { // tag::tojson-array[] FLString json = FLSTR("[\"Hotel Ned\", \"Hotel Ted\"]"); // Create an array from the JSON string FLError err; FLSliceResult jsonData1 = FLData_ConvertJSON(json, &err); FLArray hotels = FLValue_AsArray(FLValue_FromData(FLSliceResult_AsSlice(jsonData1), kFLTrusted)); // Iterate through the array FLArrayIterator iter; FLArrayIterator_Begin(hotels, &iter); FLValue value; while (NULL != (value = FLArrayIterator_GetValue(&iter))) { FLString hotel = FLValue_AsString(value); printf("Hotel :: %.*s\n", (int)hotel.size, (const char *)hotel.buf); FLArrayIterator_Next(&iter); } // Convert the array to JSON FLSliceResult jsonData2 = FLValue_ToJSON((FLValue)hotels); printf("Hotels in JSON :: %.*s\n", (int)jsonData2.size, (const char *)jsonData2.buf); // Release JSON data after finish using it FLSliceResult_Release(jsonData1); FLSliceResult_Release(jsonData2); // end::tojson-array[] } static void datatype_dictionary() { CBLDatabase *db = kDatabase; // tag::datatype_dictionary[] CBLError err; const CBLDocument *doc = CBLDatabase_GetDocument(db, FLSTR("doc1"), &err); FLDict properties = CBLDocument_Properties(doc); // Getting a dictionary from the document's properties FLValue dictValue = FLDict_Get(properties, FLSTR("address")); FLDict dict = FLValue_AsDict(dictValue); // Access a value with a key from the dictionary FLValue streetVal = FLDict_Get(dict, FLSTR("street")); FLString street = FLValue_AsString(streetVal); // Iterate dictionary FLDictIterator iter; FLDictIterator_Begin(dict, &iter); FLValue value; while (NULL != (value = FLDictIterator_GetValue(&iter))) { FLString key = FLDictIterator_GetKeyString(&iter); FLString strValue = FLValue_AsString(value); printf("Key :: %.*s\n", (int)key.size, (const char *)key.buf); printf("Value :: %.*s\n", (int)strValue.size, (const char *)strValue.buf); // ... FLDictIterator_Next(&iter); } // Create a mutable copy. // kFLDefaultCopy is shallow which means the nested dictionaries and arrays will be // referenced but not copied. Use kFLDeepCopyImmutables for the deep copy. FLMutableDict mutableDict = FLDict_MutableCopy(dict, kFLDefaultCopy); // Release when finish using it FLMutableDict_Release(mutableDict); // end::datatype_dictionary[] } static void datatype_mutable_dictionary() { CBLDatabase *db = kDatabase; // tag::datatype_mutable_dictionary[] // tag::datatype_mutable_dictionary-create[] // Create a new mutable dictionary and populate some keys/values FLMutableDict dict = FLMutableDict_New(); FLMutableDict_SetString(dict, FLSTR("street"), FLSTR("1 Main st.")); FLMutableDict_SetString(dict, FLSTR("city"), FLSTR("San Francisco")); // end::datatype_mutable_dictionary-create[] // tag::datatype_mutable_dictionary-add-to-doc[] // Set the dictionary to document's properties and save the document CBLDocument *doc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(doc); FLMutableDict_SetDict(properties, FLSTR("address"), dict); // end::datatype_mutable_dictionary-add-to-doc[] CBLError err; CBLDatabase_SaveDocument(db, doc, &err); CBLDocument_Release(doc); // Release when finish using it FLMutableDict_Release(dict); // end::datatype_mutable_dictionary[] } static void datatype_array() { CBLDatabase *db = kDatabase; // tag::datatype_array[] CBLError err; const CBLDocument *doc = CBLDatabase_GetDocument(db, FLSTR("doc1"), &err); FLDict properties = CBLDocument_Properties(doc); // Getting a phones array from the document's properties FLValue arrayValue = FLDict_Get(properties, FLSTR("phones")); FLArray array = FLValue_AsArray(arrayValue); // Get element count int count = FLArray_Count(array); printf("Count :: %d\n", count); // Access an array element by index if (!FLArray_IsEmpty(array)) { FLValue phoneVal = FLArray_Get(array, 0); FLString phone = FLValue_AsString(phoneVal); printf("Value :: %.*s\n", (int)phone.size, (const char *)phone.buf); } // Iterate array FLArrayIterator iter; FLArrayIterator_Begin(array, &iter); FLValue val; while (NULL != (val = FLArrayIterator_GetValue(&iter))) { FLString str = FLValue_AsString(val); printf("Value :: %.*s\n", (int)str.size, (const char *)str.buf); FLArrayIterator_Next(&iter); } // end::datatype_array[] // Create a mutable copy. // kFLDefaultCopy is shallow which means the nested dictionaries and arrays will be // referenced but not copied. Use kFLDeepCopyImmutables for the deep copy. FLMutableArray mutableArray = FLArray_MutableCopy(array, kFLDefaultCopy); // Release when finish using it FLMutableArray_Release(mutableArray); } static void datatype_mutable_array() { CBLDatabase *db = kDatabase; // tag::datatype_mutable_array[] // tag::datatype_mutable_array-create[] // Create a new mutable array and populate data into the array FLMutableArray phones = FLMutableArray_New(); FLMutableArray_AppendString(phones, FLSTR("650-000-0000")); FLMutableArray_AppendString(phones, FLSTR("650-000-0001")); // end::datatype_mutable_array-create[] // tag::datatype_mutable_array-add-to-doc[] // Set the array to document's properties and save the document CBLDocument *doc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(doc); FLMutableDict_SetArray(properties, FLSTR("phones"), phones); // end::datatype_mutable_array-add-to-doc[] CBLError err; CBLDatabase_SaveDocument(db, doc, &err); CBLDocument_Release(doc); // Release the created dictionary FLMutableArray_Release(phones); // end::datatype_mutable_array[] } static void datatype_usage() { // tag::datatype_usage[] // tag::datatype_usage_createdb[] // Open or create DB if it doesn't exist CBLError err; CBLDatabase* database = CBLDatabase_Open(FLSTR("mydb"), NULL, &err); if(!database) { // Error handling. For brevity, this is truncated in the rest of the snippet // and omitted in other doc code snippets fprintf(stderr, "Error opening database (%d / %d)\n", err.domain, err.code); FLSliceResult msg = CBLError_Message(&err); fprintf(stderr, "%.*s\n", (int)msg.size, (const char *)msg.buf); FLSliceResult_Release(msg); return; } // end::datatype_usage_createdb[] // tag::datatype_usage_createdoc[] // Create your new document // The lack of 'const' indicates this document is mutable CBLDocument* mutableDoc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(mutableDoc); // end::datatype_usage_createdoc[] // tag::datatype_usage_mutdict[] // Create and populate mutable dictionary FLMutableDict address = FLMutableDict_New(); FLMutableDict_SetString(address, FLSTR("street"), FLSTR("1 Main st.")); FLMutableDict_SetString(address, FLSTR("city"), FLSTR("San Francisco")); FLMutableDict_SetString(address, FLSTR("state"), FLSTR("CA")); FLMutableDict_SetString(address, FLSTR("country"), FLSTR("USA")); FLMutableDict_SetString(address, FLSTR("code"), FLSTR("90210")); // end::datatype_usage_mutdict[] // tag::datatype_usage_mutarray[] // Create and populate mutable array FLMutableArray phones = FLMutableArray_New(); FLMutableArray_AppendString(phones, FLSTR("650-000-0000")); FLMutableArray_AppendString(phones, FLSTR("650-000-0001")); // end::datatype_usage_mutarray[] // tag::datatype_usage_populate[] // Initialize and populate the document // Add document type and hotel name as string FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("hotel")); FLMutableDict_SetString(properties, FLSTR("hotel"), FLSTR("")); // Add average room rate (float) FLMutableDict_SetFloat(properties, FLSTR("room_rate"), 121.75f); // Add address (dictionary) FLMutableDict_SetDict(properties, FLSTR("address"), dict); // Add phone numbers(array) FLMutableDict_SetArray(properties, FLSTR("phones"), phones); // end::datatype_usage_populate[] // tag::datatype_usage_persist[] CBLError err; CBLDatabase_SaveDocument(database, doc, &err); // end::datatype_usage_persist[] // tag::datatype_usage_closedb[] CBLError err; CBLDatabase_Close(database, &err); // end::datatype_usage_closedb[] // tag::datatype_usage_release[] CBLDatabase_Release(database); CBLDocument_Release(doc); FLMutableDict_Release(dict); FLMutableArray_Release(phones); // end::datatype_usage_release[] // end::datatype_usage[] } // end datatype_usage() static void create_index() { CBLDatabase* db = kDatabase; // tag::query-index[] // For value types, this is optional but provides performance enhancements // NOTE: No error handling, for brevity (see getting started) // Syntax for second argument is the same as taking from a N1QL SELECT // i.e. SELECT (type, name) FROM _; CBLValueIndexConfiguration config = { kCBLN1QLLanguage, FLSTR("type, name") }; CBLError err; CBLDatabase_CreateValueIndex(db, FLSTR("TypeNameIndex"), config, &err); // end::query-index[] } static void select_meta() { CBLDatabase* db = kDatabase; // tag::query-select-meta[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, type, name FROM _"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString id = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("id"))); FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Document ID :: %.*s\n", (int)id.size, (const char *)id.buf); printf("Document Name :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-select-meta[] } static void select_id() { CBLDatabase* db = kDatabase; CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id FROM _"), NULL, &err); // tag::query-access-id[] // NOTE: No error handling, for brevity (see getting started) CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString id = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("id"))); printf("Document ID :: %.*s\n", (int)id.size, (const char *)id.buf); } // end::query-access-id[] CBLResultSet_Release(results); CBLQuery_Release(query); } static void query_change_listener(void* context, CBLQuery* query, CBLListenerToken* token) { CBLError err; CBLResultSet* results = CBLQuery_CopyCurrentResults(query, token, &err); while(CBLResultSet_Next(results)) { // Update UI } } static void select_all() { CBLDatabase* db = kDatabase; // tag::query-select-all[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _"), NULL, &err); // All results will be available from the above query CBLQuery_Release(query); // end::query-select-all[] // tag::live-query[] /* static void query_change_listener(void* context, CBLQuery* query, CBLListenerToken* token) { CBLError err; CBLResultSet* results = CBLQuery_CopyCurrentResults(query, token, &err); while(CBLResultSet_Next(results)) { // Update UI } } */ query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _"), NULL, &err); (5) CBLListenerToken* token = CBLQuery_AddChangeListener(query, query_change_listener, NULL); (6) // end::live-query[] // tag::stop-live-query[] CBLListener_Remove(token); // The token received from AddChangeListener CBLQuery_Release(query); // end::stop-live-query[] } static void select_and_access_all() { CBLDatabase* db = kDatabase; CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _"), NULL, &err); // tag::query-access-all[] CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLDict dict = FLValue_AsDict(CBLResultSet_ValueForKey(results, FLSTR("_"))); FLString id = FLValue_AsString(FLDict_Get(dict, FLSTR("id"))); FLString type = FLValue_AsString(FLDict_Get(dict, FLSTR("type"))); FLString name = FLValue_AsString(FLDict_Get(dict, FLSTR("name"))); FLString city = FLValue_AsString(FLDict_Get(dict, FLSTR("city"))); printf("ID :: %.*s\n", (int)id.size, (const char *)id.buf); printf("Type :: %.*s\n", (int)type.size, (const char *)type.buf); printf("Name :: %.*s\n", (int)name.size, (const char *)name.buf); printf("City :: %.*s\n", (int)city.size, (const char *)city.buf); } // All results will be available from the above query CBLResultSet_Release(results); // end::query-access-all[] CBLQuery_Release(query); } static void select_props() { CBLDatabase* db = kDatabase; // tag::query-access-props[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT type, name, city FROM _"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString type = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("type"))); FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); FLString city = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("city"))); printf("Type :: %.*s\n", (int)type.size, (const char *)type.buf); printf("Name :: %.*s\n", (int)name.size, (const char *)name.buf); printf("City :: %.*s\n", (int)city.size, (const char *)city.buf); } // end::query-access-props[] // All results will be available from the above query CBLResultSet_Release(results); CBLQuery_Release(query); } static void select_where() { CBLDatabase* db = kDatabase; // tag::query-where[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE type = \"hotel\" LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLDict dict = FLValue_AsDict(CBLResultSet_ValueForKey(results, FLSTR("_"))); FLString name = FLValue_AsString(FLDict_Get(dict, FLSTR("name"))); printf("Document Name :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-where[] } static void use_collection_contains() { CBLDatabase* db = kDatabase; // tag::query-collection-operator-contains[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name, public_likes FROM _ WHERE type = \"hotel\" " "AND ARRAY_CONTAINS(public_likes, \"Armani Langworth\")"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLArray publicLikes = FLValue_AsArray(CBLResultSet_ValueForKey(results, FLSTR("public_likes"))); FLStringResult json = FLValue_ToJSON((FLValue)publicLikes); printf("Public Likes :: %.*s\n", (int)json.size, (const char *)json.buf); FLSliceResult_Release(json); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-collection-operator-contains[] } static void use_collection_in() { CBLDatabase* db = kDatabase; // tag::query-collection-operator-in[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE \"Armani\" IN (first, last, username)"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLDict body = FLValue_AsDict(CBLResultSet_ValueAtIndex(results, 0)); FLStringResult json = FLValue_ToJSON((FLValue)body); printf("In results :: %.*s\n", (int)json.size, (const char *)json.buf); FLSliceResult_Release(json); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-collection-operator-in[] } static void select_like() { CBLDatabase* db = kDatabase; // tag::query-like-operator[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" " "AND lower(name) LIKE \"Royal Engineers Museum\" LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-like-operator[] } static void select_wildcard_like() { CBLDatabase* db = kDatabase; // tag::query-like-operator-wildcard-match[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" " "AND lower(name) LIKE \"Eng%e%\" LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-like-operator-wildcard-match[] } static void select_wildcard_character_like() { CBLDatabase* db = kDatabase; // tag::query-like-operator-wildcard-character-match[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" " "AND lower(name) LIKE \"Royal Eng____rs Museum\" LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-like-operator-wildcard-character-match[] } static void select_regex() { CBLDatabase* db = kDatabase; // tag::query-regex-operator[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" " "AND regexp_like(name, \"\\bEng.*e\\b\") LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-regex-operator[] } static void select_join() { CBLDatabase* db = kDatabase; // tag::query-join[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT airline.name, airline.callsign, route.destinationairport, route.stops, route.airline " "FROM _ AS airline INNER JOIN _ AS route ON meta(airline).id = route.airlineid " "WHERE route.type = \"route\" AND airline.type = \"airline\" AND route.sourceairport = \"RIX\""), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-join[] } static void group_by() { CBLDatabase* db = kDatabase; // tag::query-groupby[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT count(*), country, tz FROM _ WHERE type = \"airport\" AND geo.alt >= 300 " "GROUP BY country, tz"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { int64_t count = FLValue_AsInt(CBLResultSet_ValueForKey(results, FLSTR("$1"))); FLString tz = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("tz"))); FLString country = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("country"))); printf("There are %" PRIi64 " airports in the %.*s timezone located in %.*s and above 300 ft\n", count, (int)tz.size, (const char *)tz.buf, (int)country.size, (const char *)country.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-groupby[] } static void order_by() { CBLDatabase* db = kDatabase; // tag::query-orderby[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, title FROM _ WHERE type = \"hotel\" ORDER BY title ASC LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString title = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("title"))); printf("Title :: %.*s\n", (int)title.size, (const char *)title.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-orderby[] } static void test_explain_statement() { CBLDatabase* db = kDatabase; { // tag::query-explain-all[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE type = \"hotel\" GROUP BY country ORDER BY title ASC LIMIT 10"), NULL, &err); FLSliceResult explanation = CBLQuery_Explain(query); printf("%.*s", (int)explanation.size, (const char *)explanation.buf); FLSliceResult_Release(explanation); // end::query-explain-all[] } // DOCS NOTE: Others omitted for now } static void query_result_json() { CBLDatabase* db = kDatabase; CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id as id, name, city, type FROM _ LIMIT 10"), NULL, &err); // tag::query-access-json[] CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLDict result = CBLResultSet_ResultDict(results); FLStringResult json = FLValue_ToJSON((FLValue)result); printf("JSON Result :: %.*s\n", (int)json.size, (const char *)json.buf); FLSliceResult_Release(json); } CBLResultSet_Release(results); // end::query-access-json[] CBLQuery_Release(query); } static void create_full_text_index() { CBLDatabase* db = kDatabase; const char* tasks[] = { "buy groceries", "play chess", "book travels", "buy museum tickets" }; char idBuffer[7]; for(int i = 0; i < 4; i++) { const char* task = tasks[i]; sprintf(idBuffer, "extra%d", i); const CBLDocument* doc = CBLDatabase_GetDocument(db, FLStr(idBuffer), NULL); if(doc) { CBLDocument_Release(doc); continue; } CBLDocument* mutableDoc = CBLDocument_CreateWithID(FLStr(idBuffer)); FLMutableDict properties = CBLDocument_MutableProperties(mutableDoc); FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("task")); FLMutableDict_SetString(properties, FLSTR("task"), FLStr(task)); CBLDatabase_SaveDocument(db, mutableDoc, NULL); CBLDocument_Release(mutableDoc); } // tag::fts-index[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLFullTextIndexConfiguration config = { kCBLN1QLLanguage, FLSTR("name"), false }; CBLDatabase_CreateFullTextIndex(db, FLSTR("nameFTSIndex"), config, &err); // end::fts-index[] } static void full_text_search() { CBLDatabase* db = kDatabase; // tag::fts-query[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id FROM _ WHERE MATCH(nameFTSIndex, \"'buy'\")"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString id = FLValue_AsString(CBLResultSet_ValueAtIndex(results, 0)); printf("Document id :: %.*s\n", (int)id.size, (const char *)id.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::fts-query[] } static void start_replication() { CBLDatabase* db = kDatabase; /* * This requires Sync Gateway running with the following config, or equivalent: * * { * "log":["*"], * "databases": { * "db": { * "server":"walrus:", * "users": { * "GUEST": {"disabled": false, "admin_channels": ["*"] } * } * } * } * } */ // tag::replication[] // NOTE: No error handling, for brevity (see getting started) // Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion) CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; config.replicatorType = kCBLReplicatorTypePull; CBLReplicator* replicator = CBLReplicator_Create(&config, &err); CBLEndpoint_Free(target); CBLReplicator_Start(replicator, false); // end::replication[] stop_replicator(replicator); } // Console logging domain methods are not applicable to C static void file_logging() { // tag::file-logging[] // NOTE: No error handling, for brevity (see getting started) // NOTE: You will need to use a platform appropriate method for finding // a temporary directory FLString tempFolder = FLSTR("/tmp/cbllog"); CBLLogFileConfiguration config; // Don't bother zeroing, since we set all properties config.level = kCBLLogInfo; config.directory = tempFolder; config.maxRotateCount = 5; config.maxSize = 10240; config.usePlaintext = false; CBLError err; CBLLog_SetFileConfig(config, &err); // end::file-logging[] } // tag::custom-logging[] static void custom_log_callback(CBLLogDomain domain, CBLLogLevel level, FLString message) { // handle the message, for example piping it to // a third party framework } // end::custom-logging[] static void enable_custom_logging() { // tag::set-custom-logging[] CBLLog_SetCallback(custom_log_callback); // end::set-custom-logging[] } static void enable_basic_auth() { CBLDatabase* db = kDatabase; // tag::basic-authentication-full[] // NOTE: No error handling, for brevity (see getting started) CBLError err; FLString url = FLSTR("ws://localhost:4984/mydatabase"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("john"), FLSTR("pass")); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; config.authenticator = basicAuth; CBLReplicator* replicator = CBLReplicator_Create(&config, &err); CBLEndpoint_Free(target); CBLAuth_Free(basicAuth); CBLReplicator_Start(replicator, false); // end::basic-authentication-full[] stop_replicator(replicator); } static void docsonly_N1QL_Params(CBLDatabase* argDb) { CBLDatabase* database = argDb; // tag::query-syntax-n1ql-params[] int errorPos; CBLError err; FLString n1qlstr = FLSTR("SELECT * FROM _ WHERE type = $type"); FLMutableDict n1qlparams = FLMutableDict_New(); FLMutableDict_SetString(n1qlparams, FLSTR("type"), FLSTR("hotel")); CBLQuery* query = CBLDatabase_CreateQuery(database, kCBLN1QLLanguage, n1qlstr, &errorPos, &err); CBLQuery_SetParameters(query, n1qlparams); if(!query) { /* Do appropriate error handling ... Note that (where applicable) errorPos contains the position in the N1QL string that the parse failed */ FLMutableDict_Release(n1qlparams); CBLQuery_Release(query); return; } CBLResultSet* result = CBLQuery_Execute(query, &err); if(!result) { // Failed to run query, do error handling ... return; } // Release query when finished with FLMutableDict_Release(n1qlparams); CBLQuery_Release(query); // ... process results as required // Release result set then finished with CBLResultSet_Release(result); // end::query-syntax-n1ql-params[] } // tag::console-logging-db[] //Placeholder for code to increase level of console logging for kCBLLogDomainDatabase domain // end::console-logging-db[] // tag::console-logging[] //Placeholder for code to increase level of console logging for all domains // end::console-logging[] // tag::date-getter[] //Placeholder for Date accessors. // end::date-getter[] // tag::query-index[] // placeholder // end::query-index[] // DOCS NOTE // Page=Data Sync >> Configuration Summary // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#configuration-summary static void docs_act_replication(CBLDatabase* argDb) { CBLDatabase* database = argDb; /* * This requires Sync Gateway running with the following config, or equivalent: * * { * "log":["*"], * "databases": { * "db": { * "server":"walrus:", * "users": { * "GUEST": {"disabled": false, "admin_channels": ["*"] } * } * } * } * } */ // tag::p2p-act-rep-func[] // Purpose -- Show configuration , initialization and running of a replicator // NOTE: No error handling, for brevity (see getting started) // Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion) CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); (7) CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = database; config.endpoint = target; (8) // tag::p2p-act-rep-config-cont[] // Set replication direction and mode config.replicatorType = kCBLReplicatorTypePull; (9) config.continuous = true; // end::p2p-act-rep-config-cont[] // Optionally, set auto-purge behavior (here we override default) config.disableAutoPurge = true; (10) // Optionally, configure Client Authentication // Here we are using to Basic Authentication, // Providing username and password credentials CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("username"), FLSTR("passwd")); (11) config.authenticator = basicAuth; // Optionally, configure how we handle conflicts config.conflictResolver = simpleConflictResolver_localWins; (12) // Initialize replicator with created config CBLReplicator* replicator = CBLReplicator_Create(&config, &err); (13) CBLEndpoint_Free(target); // Optionally, add change listener CBLListenerToken* token = CBLReplicator_AddChangeListener(replicator, simpleChangeListener, NULL); (14) // Start replication CBLReplicator_Start(replicator, false); (15) // end::p2p-act-rep-func[] // ... other processing as required // When finished release resources e.g. CBLAuth_Free(basicAuth); stop_replicator(replicator); } // END configuration summary snippets // DOCS NOTE: // Page=Data Sync >> Configuration // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#configuration-summary // This function is not pulled into docs en-bloc // it is a slightly more in-depth than the configurationsummary above // and the snippets within it are used individually or in sets // to illustrate specific points as required // static void docs_act_replication_config_section_snippets() { CBLDatabase* db = kDatabase; bool docs_example_ShowBasicAuth = false; bool docs_example_ShowSessionAuth = false; /* * This requires Sync Gateway running with the following config, or equivalent: * * { * "log":["*"], * "databases": { * "db": { * "server":"walrus:", * "users": { * "GUEST": {"disabled": false, "admin_channels": ["*"] } * } * } * } * } */ // tag::p2p-act-rep-func-full[] // NOTE: No error handling, for brevity (see getting started) // Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion) // tag::sgw-act-rep-initialize[] // Initialize the configuration object and set db target CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); (16) CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; (17) // end::sgw-act-rep-initialize[] // tag::p2p-act-rep-config-type[] config.replicatorType = kCBLReplicatorTypePull; // end::p2p-act-rep-config-type[] // tag::p2p-act-rep-config-cont[] config.continuous = true; // end::p2p-act-rep-config-cont[] // tag::replication-retry-config[] // Configure replication retries // tag::replication-set-heartbeat[] config.heartbeat = 120; // (18) // end::replication-set-heartbeat[] // tag::replication-set-maxattempts[] config.maxAttempts = 20; // (19) // end::replication-set-maxattempts[] // tag::replication-set-maxattemptwaittime[] config.maxAttemptWaitTime = 600; // (20) // end::replication-set-maxattemptwaittime[] // end::replication-retry-config[] // tag::basic-authentication[] // Configure Client Authentication to Basic Authentication // Providing username and password credentials if(docs_example_ShowBasicAuth) { CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("username"), FLSTR("passwd")); config.authenticator = basicAuth; (21) } // end::basic-authentication[] // tag::session-authentication[] if(docs_example_ShowSessionAuth) { CBLAuthenticator* sessionAuth = CBLAuth_CreateSession(FLSTR("904ac010862f37c8dd99015a33ab5a3565fd8447"), FLSTR("optionalCookieName")); config.authenticator = sessionAuth; (22) } // end::session-authentication[] // tag::replication-custom-header[] // Optionally, add custom headers FLMutableDict customHdrs = FLMutableDict_New(); FLMutableDict_SetString(customHdrs, FLSTR("customHeaderName"), FLSTR("customHeaderValue")); config.headers = customHdrs; // tag::certificate-pinning[] char cert_buf[10000]; FILE* cert_file = fopen("cert.pem", "r"); size_t read = fread(cert_buf, 1, sizeof(cert_buf), cert_file); config.pinnedServerCertificate = (FLSlice){cert_buf, read}; // end::certificate-pinning[] // end::replication-custom-header[] // FILTERS // tag::replication-push-filter[] // tag::replication-pull-filter[] // Purpose - Illustrate use of push and-or pull filter functions config.pushFilter = simpleReplicationFilter; config.pullFilter = simpleReplicationFilter; // end::replication-pull-filter[] // end::replication-push-filter[] // Auto-purge over-ride // tag::autopurge-override[] config.disableAutoPurge = true; (23) // end::autopurge-override[] // Initialize replicator with created config CBLReplicator* replicator = CBLReplicator_Create(&config, &err); (24) CBLEndpoint_Free(target); // Add optional change listener CBLListenerToken* token_ReplChangeListener = CBLReplicator_AddChangeListener(replicator, simpleChangeListener, NULL); (25) } // END replication.html >> configure section // PAGE=Data Sync >> Initialize section // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#lbl-init-repl static CBLReplicator* docs_act_replication_Intialize( void* context, CBLReplicatorConfiguration argConfig, bool argResetRequired) { CBLError err; bool docs_example_resetRequired = argResetRequired; // tag::p2p-act-rep-start-full[] CBLReplicator* thisRepl = CBLReplicator_Create(&argConfig, &err); (26) // end::p2p-act-rep-start-full[] if(!docs_example_resetRequired) { // tag::p2p-act-rep-start-full[] CBLReplicator_Start(thisRepl, false); (27) // end::p2p-act-rep-start-full[] } else { // tag::replication-reset-checkpoint[] CBLReplicator_Start(thisRepl, true); (28) // end::replication-reset-checkpoint[] } return thisRepl; } // END replication.html >> initialize section // PAGE=Data Sync >> Monitor section // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#lbl-repl-mon static void docs_act_replication_Monitor( void* context, CBLReplicator* argRepl) { CBLError err; CBLReplicator* thisRepl = argRepl; // tag::p2p-act-rep-add-change-listener[] // Purpose -- illustrate addition of a Replicator change listener CBLListenerToken* token_ReplChangeListener = CBLReplicator_AddChangeListener(thisRepl, simpleChangeListener, NULL); // end::p2p-act-rep-add-change-listener[] // tag::add-document-replication-listener[] // Purpose -- illustrate addition of a Document Replicator listener CBLListenerToken* token_ReplDocListener = CBLReplicator_AddDocumentReplicationListener( thisRepl, SimpleReplicationDocumentListener, context); // end::add-document-replication-listener[] // tag::remove-document-replication-listener[] // Purpose -- illustrate removal of a listener CBLListener_Remove(token_ReplDocListener); CBLListener_Remove(token_ReplChangeListener); // end::remove-document-replication-listener[] // tag::p2p-act-rep-status[] // Purpose -- illustrate use of CBLReplicator_Status() CBLReplicatorStatus thisState = CBLReplicator_Status(thisRepl); if(thisState.activity==kCBLReplicatorStopped) { if(thisState.error.code==0) { CBLReplicator_Start(thisRepl,false); } else { printf("Replicator stopped -- code %d", thisState.error.code); // ... handle error ... CBLReplicator_Release(thisRepl); } } // end::p2p-act-rep-status[] // tag::replication-pendingdocuments[] FLDict thisPendingIdList = CBLReplicator_PendingDocumentIDs(thisRepl, &err); (29) if(!FLDict_IsEmpty(thisPendingIdList)) { FLDictIterator item; FLDictIterator_Begin(thisPendingIdList, &item); FLValue itemValue; FLString pendingId; while(NULL != (itemValue = FLDictIterator_GetValue(&item))) { pendingId = FLValue_AsString(itemValue); if(CBLReplicator_IsDocumentPending(thisRepl, pendingId, &err)) { // ... process the still pending docid as required (30) } else { // Doc Id no longer pending if(err.code==0) { // No fail so must have already been pushed printf("Document already pushed"); } else { // Error detected so handle it printf("Error code %d checking for pendingId", err.code); break; } } FLDictIterator_Next(&item); } FLDictIterator_End(&item); FLValue_Release(itemValue); } else { printf("No Pending Id Docs to process"); } FLDict_Release(thisPendingIdList); // end::replication-pendingdocuments[] } // END replication.html >> Monitor section // BEGIN replication.html >> Stop section // PAGE=Data Sync >> Stop // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#lbl-repl-stop static void docs_act_replication_Stop( void* context, CBLReplicator* argRepl) { // tag::p2p-act-rep-stop[] // Purpose -- show how to stop a replication if(CBLReplicator_Status(argRepl).activity!=kCBLReplicatorStopped) { CBLReplicator_Stop(argRepl); } // end::p2p-act-rep-stop[] } // END replication.html >> Stop section static void replication_error_handling() { CBLDatabase* db = kDatabase; CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; CBLReplicator* replicator = CBLReplicator_Create(&config, &err); CBLEndpoint_Free(target); stop_replicator(replicator); } static void create_encryptable() { #ifdef COUCHBASE_ENTERPRISE // tag::encryptable[] // NOTE: No error handling, for brevity (see getting started) // Create with premitive type CBLEncryptable* encNull = CBLEncryptable_CreateWithNull(); CBLEncryptable* encBool = CBLEncryptable_CreateWithBool(true); CBLEncryptable* encInt = CBLEncryptable_CreateWithInt(256); CBLEncryptable* encUInt = CBLEncryptable_CreateWithUInt(1024); CBLEncryptable* encFloat = CBLEncryptable_CreateWithFloat(1.2); CBLEncryptable* encDouble = CBLEncryptable_CreateWithDouble(100.50); CBLEncryptable* encString = CBLEncryptable_CreateWithString(FLSTR("foo")); // Create with dictionary FLMutableDict dict = FLMutableDict_New(); FLSlot_SetString(FLMutableDict_Set(dict, FLSTR("greeting")), FLSTR("hello")); CBLEncryptable* encDict = CBLEncryptable_CreateWithDict(dict); // Create with array FLMutableArray array = FLMutableArray_New(); FLSlot_SetString(FLMutableArray_Append(array), FLSTR("item1")); CBLEncryptable* encArray = CBLEncryptable_CreateWithArray(array); // Create with FLValue FLMutableDict dict2 = FLMutableDict_New(); FLSlot_SetString(FLMutableDict_Set(dict2, FLSTR("greeting")), FLSTR("hello")); CBLEncryptable* encValue = CBLEncryptable_CreateWithValue((FLValue)dict2); // end::encryptable[] // Release after using it CBLEncryptable_Release(encNull); CBLEncryptable_Release(encBool); CBLEncryptable_Release(encInt); CBLEncryptable_Release(encBool); CBLEncryptable_Release(encUInt); CBLEncryptable_Release(encFloat); CBLEncryptable_Release(encDouble); CBLEncryptable_Release(encString); CBLEncryptable_Release(encDict); CBLEncryptable_Release(encArray); CBLEncryptable_Release(encValue); FLMutableDict_Release(dict); FLMutableDict_Release(dict2); FLMutableArray_Release(array); #endif } static void release_encryptable() { #ifdef COUCHBASE_ENTERPRISE CBLEncryptable* encValue = CBLEncryptable_CreateWithString(FLSTR("foo")); // tag::release_encryptable[] // Release the encryptable value after finish using it CBLEncryptable_Release(encValue); // end::release_encryptable[] #endif } static void use_encryptable() { #ifdef COUCHBASE_ENTERPRISE CBLDatabase* db = kDatabase; // tag::use_encryptable[] // NOTE: No error handling, for brevity (see getting started) CBLDocument* doc = CBLDocument_CreateWithID(FLSTR("doc1")); // Set encryptable: FLMutableDict props = CBLDocument_MutableProperties(doc); CBLEncryptable* encryptable = CBLEncryptable_CreateWithString(FLSTR("My secret")); FLSlot_SetEncryptableValue(FLMutableDict_Set(props, FLSTR("secret")), encryptable); CBLError error; CBLDatabase_SaveDocument(db, doc, &error); // Release CBLDocument_Release(doc); CBLEncryptable_Release(encryptable); // end::release_encryptable[] #endif } static void query_encryptable() { #ifdef COUCHBASE_ENTERPRISE CBLDatabase* db = kDatabase; // tag::use_encryptable[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT secret, secret.value as secretValue FROM _ WHERE type = \"profile\""), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { // Get secret as CBLEncryptable value FLValue value = CBLResultSet_ValueForKey(results, FLSTR("secret")); const CBLEncryptable* encValue = FLValue_GetEncryptableValue(value); FLString secretStr = FLValue_AsString(CBLEncryptable_Value(encValue)); printf("Secret :: %.*s\n", (int)secretStr.size, (const char *)secretStr.buf); // Get secret value directly value = CBLResultSet_ValueForKey(results, FLSTR("secretValue")); secretStr = FLValue_AsString(value); printf("Secret :: %.*s\n", (int)secretStr.size, (const char *)secretStr.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::release_encryptable[] #endif } #ifdef COUCHBASE_ENTERPRISE // tag::replicator_property_encryption[] // tag::replicator_property_encryptor_decryptor_sample[] // Purpose: Declare property-level encryptor callback functions static FLSliceResult my_cipher_function(FLSlice input) { FLSliceResult result = FLSliceResult_New(input.size); for(int i = 0; i < input.size; ++i) { ((uint8_t*)(result.buf))[i] = ((uint8_t*)input.buf)[i] ^ 'K';} return result; } static FLSliceResult property_encryptor(void* context, FLString docID, FLDict props, FLString path, FLSlice input, FLStringResult* algorithm, FLStringResult* keyID, CBLError* error) { *algorithm = FLSlice_Copy(FLSTR("MyEnc")); return my_cipher_function(input); } static FLSliceResult property_decryptor(void* context, FLString documentID, FLDict properties, FLString keyPath, FLSlice input, FLString algorithm, FLString keyID, CBLError* error) { return my_cipher_function(input); } // end::replicator_property_encryptor_decryptor_sample[] // end::replicator_property_encryption[] #endif // PAGE=Field Level Encryption // URL=https://docs.couchbase.com/couchbase-lite/current/c/field-level-encryption.html // static void replicator_property_encryption() { #ifdef COUCHBASE_ENTERPRISE CBLDatabase* db = kDatabase; // tag::replicator_property_encryption[] // Purpose: Show how to declare en(de)cryptors in replicator config // NOTE: No error handling, for brevity (see getting started) CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; config.propertyEncryptor = property_encryptor; (31) config.propertyDecryptor = property_decryptor; (32) CBLReplicator* replicator = CBLReplicator_Create(&config, &err); CBLEndpoint_Free(target); CBLReplicator_Start(replicator, false); // end::replicator_property_encryption[] stop_replicator(replicator); #endif } int main(int argc, char** argv) { create_new_database(); create_document(); update_document(); do_batch_operation(); // Disable use_blob() as no avatar.jpg to load and crash // use_blob(); doc_json(); dict_json(); array_json(); load_prebuilt(); create_index(); select_all(); select_and_access_all(); select_props(); select_meta(); select_id(); select_where(); use_collection_contains(); select_like(); select_wildcard_like(); select_wildcard_character_like(); select_regex(); select_join(); group_by(); order_by(); query_result_json(); create_full_text_index(); full_text_search(); start_replication(); replicator_property_encryption(); CBLDatabase_Close(kDatabase, NULL); return 0; } -
Import from an existing resource bundle
// CBL Version 3.0.0 BETA #include <cbl/CouchbaseLite.h> #include <time.h> #include <inttypes.h> #ifdef _MSC_VER #include <direct.h> #include <Shlwapi.h> void usleep(unsigned int us) { Sleep(us / 1000); } #else #include <unistd.h> #endif static CBLDatabase* kDatabase; // Helper for stop replicator in the code snippet static void stop_replicator(CBLReplicator* replicator) { CBLReplicator_Stop(replicator); while(CBLReplicator_Status(replicator).activity != kCBLReplicatorStopped) { printf("Waiting for replicator to stop..."); usleep(200000); } CBLReplicator_Release(replicator); } // BEGIN lower-level function declarations // DOCS NOTE -- // These functions are referred to in subsequent code samples. // Their tags will ensure they are shown alongide the usage examples. // Functions used in more than one place may hve multiple tags. // tag::p2p-act-rep-func[] // tag::p2p-act-rep-add-change-listener[] // tag::replication-error-handling[] // Purpose -- illustrate a simple change listener static void simpleChangeListener(void* context, CBLReplicator* repl, const CBLReplicatorStatus* status) { if(status->error.code != 0) { printf("Error %d / %d\n", status->error.domain, status->error.code); } } // end::replication-error-handling[] // end::p2p-act-rep-add-change-listener[] // end::p2p-act-rep-func[] static const CBLDocument* simpleConflictResolver_localWins( void* context, FLString documentID, const CBLDocument* localDocument, const CBLDocument* remoteDocument) { return localDocument; } // tag::replication-push-filter[] // tag::replication-pull-filter[] // Purpose -- illustrate a simple replication filter function static bool simpleReplicationFilter(void* context, CBLDocument* argDoc, CBLDocumentFlags argFlags) { bool result = (argFlags == kCBLDocumentFlagsDeleted); return result; } // end::replication-push-filter[] // end::replication-pull-filter[] // tag::SimpleReplicationDocumentListener[] // Purpose -- Illustrate a simple replication document listener static void SimpleReplicationDocumentListener( void *context, CBLReplicator *replicator, bool isPush, unsigned numDocuments, const CBLReplicatedDocument *documents) { if(isPush) { printf("We pushed %d documents",numDocuments); } } // END lower-level function declarations static void getting_started_change_listener(void* context, CBLReplicator* repl, const CBLReplicatorStatus* status) { if(status->error.code != 0) { printf("Error %d / %d\n", status->error.domain, status->error.code); } } // Page=build and run // url=https://docs-staging.couchbase.com/couchbase-lite/current/c/gs-build.html static void getting_started() { // tag::getting-started[] // Purpose-- provide an overview of available crud and sync functionality // // Get the database (and create it if it doesn't exist) CBLError err; CBLDatabase* database = CBLDatabase_Open(FLSTR("mydb"), NULL, &err); if(!database) { // Error handling. For brevity, this is truncated in the rest of the snippet // and omitted in other doc code snippets fprintf(stderr, "Error opening database (%d / %d)\n", err.domain, err.code); FLSliceResult msg = CBLError_Message(&err); fprintf(stderr, "%.*s\n", (int)msg.size, (const char *)msg.buf); FLSliceResult_Release(msg); return; } // The lack of 'const' indicates this document is mutable // Create a new document (i.e. a record) in the database CBLDocument* mutableDoc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(mutableDoc); FLMutableDict_SetFloat(properties, FLSTR("version"), 3.0f); // Save it to the database if(!CBLDatabase_SaveDocument(database, mutableDoc, &err)) { // Failed to save, do error handling as above return; } // Since we will release the document, make a copy of the ID since it // is an internal pointer. Whenever we create or get an FLSliceResult // or FLStringResult we will need to free it later too! FLStringResult id = FLSlice_Copy(CBLDocument_ID(mutableDoc)); CBLDocument_Release(mutableDoc); // Update a document mutableDoc = CBLDatabase_GetMutableDocument(database, FLSliceResult_AsSlice(id), &err); if(!mutableDoc) { // Failed to retrieve, do error handling as above. NOTE: error code 0 simply means // the document does not exist. return; } properties = CBLDocument_MutableProperties(mutableDoc); FLMutableDict_SetString(properties, FLSTR("language"), FLSTR("C")); if(!CBLDatabase_SaveDocument(database, mutableDoc, &err)) { // Failed to save, do error handling as above return; } // Note const here, means readonly const CBLDocument* docAgain = CBLDatabase_GetDocument(database, FLSliceResult_AsSlice(id), &err); if(!docAgain) { // Failed to retrieve, do error handling as above. NOTE: error code 0 simply means // the document does not exist. return; } // No copy this time, so no release later (notice it is not FLStringResult this time) FLString retrievedID = CBLDocument_ID(docAgain); FLDict retrievedProperties = CBLDocument_Properties(docAgain); FLString retrievedLanguage = FLValue_AsString(FLDict_Get(retrievedProperties, FLSTR("language"))); printf("Document ID :: %.*s\n", (int)retrievedID.size, (const char *)retrievedID.buf); printf("Learning %.*s\n", (int)retrievedLanguage.size, (const char *)retrievedLanguage.buf); CBLDocument_Release(mutableDoc); CBLDocument_Release(docAgain); FLSliceResult_Release(id); // tag::query-syntax-n1ql[] // Create a query to fetch documents of type SDK int errorPos; CBLQuery* query = CBLDatabase_CreateQuery(database, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE type = \"SDK\""), &errorPos, &err); if(!query) { // Failed to create query, do error handling as above // Note that errorPos will contain the position in the N1QL string // that the parse failed, if applicable return; } CBLResultSet* result = CBLQuery_Execute(query, &err); if(!result) { // Failed to run query, do error handling as above return; } // end::query-syntax-n1ql[] // TODO: Result set count? CBLResultSet_Release(result); CBLQuery_Release(query); // Create replicator to push and pull changes to and from the cloud CBLEndpoint* targetEndpoint = CBLEndpoint_CreateWithURL(FLSTR("ws://localhost:4984/getting-started-db"), &err); if(!targetEndpoint) { // Failed to create endpoint, do error handling as above return; } CBLReplicatorConfiguration replConfig; CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("john"), FLSTR("pass")); memset(&replConfig, 0, sizeof(replConfig)); replConfig.database = database; replConfig.endpoint = targetEndpoint; replConfig.authenticator = basicAuth; CBLReplicator* replicator = CBLReplicator_Create(&replConfig, &err); CBLAuth_Free(basicAuth); CBLEndpoint_Free(targetEndpoint); if(!replicator) { // Failed to create replicator, do error handling as above return; } // Assume a function like the simple following // // static void getting_started_change_listener(void* context, CBLReplicator* repl, const CBLReplicatorStatus* status) { // if(status->error.code != 0) { // printf("Error %d / %d\n", status->error.domain, status->error.code); // } // } CBLListenerToken* token = CBLReplicator_AddChangeListener(replicator, getting_started_change_listener, NULL); CBLReplicator_Start(replicator, false); // Later, stop and release the replicator // end::getting-started[] // When finished release resources ... eg CBLListener_Remove(token); stop_replicator(replicator); } // tag::local-win-conflict-resolver[] static const CBLDocument* local_win_conflict_resolver(void* context, FLString documentID, const CBLDocument* localDocument, const CBLDocument* remoteDocument) { return localDocument; } // end::local-win-conflict-resolver[] // tag::remote-win-conflict-resolver[] static const CBLDocument* remote_win_conflict_resolver(void* context, FLString documentID, const CBLDocument* localDocument, const CBLDocument* remoteDocument) { return remoteDocument; } // end::remote-win-conflict-resolver[] // tag::merge-conflict-resolver[] static const CBLDocument* merge_conflict_resolver(void* context, FLString documentID, const CBLDocument* localDocument, const CBLDocument* remoteDocument) { FLDict localProps = CBLDocument_Properties(localDocument); FLDict remoteProps = CBLDocument_Properties(remoteDocument); FLMutableDict mergeProps = FLDict_MutableCopy(localProps, kFLDefaultCopy); FLDictIterator d; FLDictIterator_Begin(localProps, &d); FLValue value; while((value = FLDictIterator_GetValue(&d))) { FLString key = FLDictIterator_GetKeyString(&d); if(FLDict_Get(mergeProps, key)) { continue; } FLMutableDict_SetValue(mergeProps, key, value); FLDictIterator_Next(&d); } CBLDocument* mergeDocument = CBLDocument_CreateWithID(documentID); CBLDocument_SetProperties(mergeDocument, mergeProps); FLMutableDict_Release(mergeProps); return mergeDocument; } // end::merge-conflict-resolver[] static void test_replicator_conflict_resolve() { CBLDatabase* database = kDatabase; // tag::replication-conflict-resolver[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLEndpoint* target = CBLEndpoint_CreateWithURL(FLSTR("ws://localhost:4984/mydatabase"), &err); CBLReplicatorConfiguration replConfig; memset(&replConfig, 0, sizeof(replConfig)); replConfig.database = database; replConfig.endpoint = target; replConfig.conflictResolver = local_win_conflict_resolver; CBLReplicator* replicator = CBLReplicator_Create(&replConfig, &err); CBLEndpoint_Free(target); CBLReplicator_Start(replicator, false); // end::replication-conflict-resolver[] stop_replicator(replicator); } static bool custom_conflict_handler(void* context, CBLDocument* documentBeingSaved, const CBLDocument* conflictingDocument) { FLDict currentProps = CBLDocument_Properties(conflictingDocument); FLDict updatedProps = CBLDocument_Properties(documentBeingSaved); FLMutableDict newProps = FLDict_MutableCopy(updatedProps, kFLDefaultCopy); FLDictIterator d; FLDictIterator_Begin(currentProps, &d); FLValue currentValue; while((currentValue = FLDictIterator_GetValue(&d))) { FLString currentKey = FLDictIterator_GetKeyString(&d); if(FLDict_Get(newProps, currentKey)) { continue; } FLMutableDict_SetValue(newProps, currentKey, currentValue); FLDictIterator_Next(&d); } return true; } static void test_save_with_conflict_handler() { CBLDatabase* database = kDatabase; // tag::update-document-with-conflict-handler[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDocument* mutableDocument = CBLDatabase_GetMutableDocument(database, FLSTR("xyz"), &err); FLMutableDict properties = CBLDocument_MutableProperties(mutableDocument); FLMutableDict_SetString(properties, FLSTR("name"), FLSTR("apples")); /* static bool custom_conflict_handler(void* context, CBLDocument* documentBeingSaved, const CBLDocument* conflictingDocument) { FLDict currentProps = CBLDocument_Properties(conflictingDocument); FLDict updatedProps = CBLDocument_Properties(documentBeingSaved); FLMutableDict newProps = FLDict_MutableCopy(updatedProps, kFLDefaultCopy); FLDictIterator d; FLDictIterator_Begin(currentProps, &d); FLSlice currentKey = FLDictIterator_GetKeyString(&d); for(; currentKey.buf; currentKey = FLDictIterator_GetKeyString(&d)) { if(FLDict_Get(newProps, currentKey)) { continue; } FLValue currentValue = FLDictIterator_GetValue(&d); FLMutableDict_SetValue(newProps, currentKey, currentValue); } return true; } */ CBLDatabase_SaveDocumentWithConflictHandler(database, mutableDocument, custom_conflict_handler, NULL, &err); // end::update-document-with-conflict-handler[] } static void use_encryption() { #ifdef COUCHBASE_ENTERPRISE // tag::database-encryption[] // NOTE: No error handling, for brevity (see getting started) CBLDatabaseConfiguration config = CBLDatabaseConfiguration_Default(); // This returns a boolean, so check it in production code CBLEncryptionKey_FromPassword(&config.encryptionKey, FLSTR("password")); CBLError err; CBLDatabase* db = CBLDatabase_Open(FLSTR("seekrit"), &config, &err); // Change the encryption key (or add encryption if the DB is unencrypted) CBLEncryptionKey betterKey; CBLEncryptionKey_FromPassword(&betterKey, FLSTR("betterpassw0rd")); CBLDatabase_ChangeEncryptionKey(db, &betterKey, &err); // Remove encryption CBLDatabase_ChangeEncryptionKey(db, NULL, &err); // end::database-encryption[] #endif } static void reset_replicator_checkpoint() { CBLEndpoint* target = CBLEndpoint_CreateWithURL(FLSTR("ws://localhost:4984/db"), NULL); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = kDatabase; config.endpoint = target; CBLReplicator* replicator = CBLReplicator_Create(&config, NULL); CBLEndpoint_Free(target); // tag::replication-reset-checkpoint-full[] // replicator is a CBLReplicator* instance CBLReplicator_Start(replicator, true); (1) // end::replication-reset-checkpoint-full[] stop_replicator(replicator); } static void read_1x_attachment() { CBLDocument* document = CBLDocument_Create(); // tag::1x-attachment[] // NOTE: No error handling, for brevity (see getting started) CBLError err; FLDict properties = CBLDocument_Properties(document); FLDict attachments = FLValue_AsDict(FLDict_Get(properties, FLSTR("_attachments"))); const CBLBlob* avatar = FLDict_GetBlob(FLValue_AsDict(FLDict_Get(attachments, FLSTR("avatar")))); FLSliceResult content = CBLBlob_Content(avatar, &err); FLSliceResult_Release(content); // end::1x-attachment[] CBLDocument_Release(document); } static void create_new_database() { // tag::new-database[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDatabase* db = CBLDatabase_Open(FLSTR("my-database"), NULL, &err); // end::new-database[] kDatabase = db; } static void close_database() { CBLDatabase* db = kDatabase; // tag::close-database[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDatabase_Close(db, &err); // end::close-database[] } static void change_logging() { // tag::logging[] // For output to stdout CBLLog_SetConsoleLevel(kCBLLogVerbose); // For output to custom logging CBLLog_SetCallbackLevel(kCBLLogVerbose); // end::logging[] } static void load_prebuilt() { CBL_DeleteDatabase(FLSTR("travel-sample.cblite2"), kFLSliceNull, NULL); // tag::prebuilt-database[] // Note: Getting the path to a database is platform-specific. For desktop (including RPi) // this can be a simple filesystem path. For iOS you need to get the path from the // main bundle. For Android you need to extract it from your assets to a temporary directory // and then pass that path. // NOTE: No error handling, for brevity (see getting started) CBLError err; const char* path = "/path/to/travel-sample.cblite2"; if(!CBL_DatabaseExists(FLSTR("travel-sample.cblite2"), kFLSliceNull)) { CBL_CopyDatabase(FLStr(path), FLSTR("travel-sample"), NULL, &err); } // end::prebuilt-database[] CBLDatabase_Close(kDatabase, NULL); CBLDatabase_Release(kDatabase); kDatabase = CBLDatabase_Open(FLSTR("travel-sample"), NULL, NULL); } static void query_deleted_document() { CBLDatabase* db = kDatabase; // tag::query-deleted-documents[] // Query documents that have been deleted // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id FROM _ WHERE meta().deleted"), NULL, &err); // end::query-deleted-documents[] CBLQuery_Release(query); } static void create_document() { CBLDatabase* db = kDatabase; // tag::initializer[] // NOTE: No error handling, for brevity (see getting started) CBLDocument* newTask = CBLDocument_CreateWithID(FLSTR("xyz")); FLMutableDict properties = CBLDocument_MutableProperties(newTask); FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("task")); FLMutableDict_SetString(properties, FLSTR("owner"), FLSTR("todo")); // Storing time in millisecond, bluntly FLMutableDict_SetUInt(properties, FLSTR("createdAt"), time(NULL) * 1000); CBLError err; CBLDatabase_SaveDocument(db, newTask, &err); CBLDocument_Release(newTask); // end::initializer[] } static void update_document() { CBLDatabase* db = kDatabase; // tag::update-document[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDocument* mutableDocument = CBLDatabase_GetMutableDocument(db, FLSTR("xyz"), &err); FLMutableDict properties = CBLDocument_MutableProperties(mutableDocument); FLMutableDict_SetString(properties, FLSTR("name"), FLSTR("apples")); CBLDatabase_SaveDocument(db, mutableDocument, &err); CBLDocument_Release(mutableDocument); // end::update-document[] } // Note use_typed_accessors not applicable static void do_batch_operation() { CBLDatabase* db = kDatabase; // tag::batch[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDatabase_BeginTransaction(db, &err); char buffer[7]; for(int i = 0; i < 10; i++) { CBLDocument* doc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(doc); FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("user")); sprintf(buffer, "user %d", i); FLMutableDict_SetString(properties, FLSTR("name"), FLStr(buffer)); FLMutableDict_SetBool(properties, FLSTR("admin"), false); CBLDatabase_SaveDocument(db, doc, &err); CBLDocument_Release(doc); printf("Saved user document %s\n", buffer); } CBLDatabase_EndTransaction(db, true, &err); // end::batch[] } static void document_listener(void* context, const CBLDatabase* db, FLString id) { CBLError err; const CBLDocument* doc = CBLDatabase_GetDocument(db, id, &err); FLDict properties = CBLDocument_Properties(doc); FLString verified_account = FLValue_AsString(FLDict_Get(properties, FLSTR("verified_account"))); printf("Status :: %.*s\n", (int)verified_account.size, (const char *)verified_account.buf); CBLDocument_Release(doc); } static void database_change_listener() { CBLDatabase* db = kDatabase; // tag::document-listener[] /* static void document_listener(void* context, const CBLDatabase* db, FLString id) { CBLError err; const CBLDocument* doc = CBLDatabase_GetDocument(db, id, &err); FLDict properties = CBLDocument_Properties(doc); FLString verified_account = FLValue_AsString(FLDict_Get(properties, FLSTR("verified_account"))); printf("Status :: %.*s\n", (int)verified_account.size, (const char *)verified_account.buf); CBLDocument_Release(doc); } */ CBLListenerToken* token = CBLDatabase_AddDocumentChangeListener(db, FLSTR("user.john"), document_listener, NULL); // end::document-listener[] CBLListener_Remove(token); } static void document_expiration() { CBLDatabase* db = kDatabase; // tag::document-expiration[] // Purge the document one day from now // Overly simplistic for example purposes // NOTE: API takes milliseconds // NOTE: No error handling, for brevity (see getting started) time_t ttl = time(NULL) + 24 * 60 * 60; ttl *= 1000; CBLError err; CBLDatabase_SetDocumentExpiration(db, FLSTR("doc123"), ttl, &err); // Reset expiration CBLDatabase_SetDocumentExpiration(db, FLSTR("doc1"), 0, &err); // Query documents that will be expired in less than five minutes time_t fiveMinutesFromNow = time(NULL) + 5 * 60; fiveMinutesFromNow *= 1000; FLMutableDict parameters = FLMutableDict_New(); FLMutableDict_SetInt(parameters, FLSTR("five_minutes"), fiveMinutesFromNow); CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id FROM _ WHERE meta().expiration < $five_minutes"), NULL, &err); CBLQuery_SetParameters(query, parameters); FLMutableDict_Release(parameters); // end::document-expiration[] } static void use_blob() { CBLDatabase* db = kDatabase; CBLDocument* newTask = CBLDocument_Create(); // tag::blob[] // Note: Reading the data is implementation dependent, as with prebuilt databases // NOTE: No error handling, for brevity (see getting started) uint8_t buffer[128000]; FILE* avatar_file = fopen("avatar.jpg", "rb"); size_t read = fread(buffer, 1, 128000, avatar_file); (2) FLSliceResult avatar = FLSliceResult_CreateWith(buffer, read); CBLBlob* blob = CBLBlob_CreateWithData(FLSTR("image/jpeg"), FLSliceResult_AsSlice(avatar)); (3) FLSliceResult_Release(avatar); // TODO: Create shortcut blob method CBLError err; FLMutableDict properties = CBLDocument_MutableProperties(newTask); FLSlot_SetBlob(FLMutableDict_Set(properties, FLSTR("avatar")), blob); CBLDatabase_SaveDocument(db, newTask, &err); (4) // end::blob[] CBLDocument_Release(newTask); CBLBlob_Release(blob); } static void doc_json() { CBLDatabase* db = kDatabase; // tag::tojson-document[] FLString json = FLSTR("{\"id\":\"1002\",\"type\":\"hotel\",\"name\":\"Hotel Ned\",\"city\":\"Balmain\",\"country\":\"Australia\"}"); // Create a document and set the JSON data to the document CBLError err; CBLDocument* newDoc = CBLDocument_CreateWithID(FLSTR("hotel_1002")); CBLDocument_SetJSON(newDoc, json, &err); // Save the document to the database CBLDatabase_SaveDocument(db, newDoc, &err); // Release created doc after using it CBLDocument_Release(newDoc); // Get the document from the database const CBLDocument* doc = CBLDatabase_GetDocument(db, FLSTR("hotel_1002"), &err); // Get document body as JSON FLSliceResult docJson = CBLDocument_CreateJSON(doc); printf("Document in JSON :: %.*s\n", (int)docJson.size, (const char *)docJson.buf); // Release JSON data after using it FLSliceResult_Release(docJson); // Release doc read from the database after using it CBLDocument_Release(doc); // end::tojson-document[] } static void dict_json() { // tag::tojson-dictionary[] FLString json = FLSTR("{\"id\":\"1002\",\"type\":\"hotel\",\"name\":\"Hotel Ned\",\"city\":\"Balmain\",\"country\":\"Australia\"}"); // Create a dictionary from the JSON string FLError err; FLSliceResult jsonData1 = FLData_ConvertJSON(json, &err); FLDict hotel = FLValue_AsDict(FLValue_FromData(FLSliceResult_AsSlice(jsonData1), kFLTrusted)); // Iterate through the dictionary FLDictIterator iter; FLDictIterator_Begin(hotel, &iter); FLValue value; while (NULL != (value = FLDictIterator_GetValue(&iter))) { FLString key = FLDictIterator_GetKeyString(&iter); FLString strValue = FLValue_AsString(value); printf("%.*s :: %.*s\n", (int)key.size, (const char*)key.buf, (int)strValue.size, (const char*)strValue.buf); FLDictIterator_Next(&iter); } // Convert the dictionary to JSON FLSliceResult jsonData2 = FLValue_ToJSON((FLValue)hotel); printf("Hotel in JSON :: %.*s\n", (int)jsonData2.size, (const char *)jsonData2.buf); // Release JSON data after finish using it FLSliceResult_Release(jsonData1); FLSliceResult_Release(jsonData2); // end::tojson-dictionary[] } static void array_json() { // tag::tojson-array[] FLString json = FLSTR("[\"Hotel Ned\", \"Hotel Ted\"]"); // Create an array from the JSON string FLError err; FLSliceResult jsonData1 = FLData_ConvertJSON(json, &err); FLArray hotels = FLValue_AsArray(FLValue_FromData(FLSliceResult_AsSlice(jsonData1), kFLTrusted)); // Iterate through the array FLArrayIterator iter; FLArrayIterator_Begin(hotels, &iter); FLValue value; while (NULL != (value = FLArrayIterator_GetValue(&iter))) { FLString hotel = FLValue_AsString(value); printf("Hotel :: %.*s\n", (int)hotel.size, (const char *)hotel.buf); FLArrayIterator_Next(&iter); } // Convert the array to JSON FLSliceResult jsonData2 = FLValue_ToJSON((FLValue)hotels); printf("Hotels in JSON :: %.*s\n", (int)jsonData2.size, (const char *)jsonData2.buf); // Release JSON data after finish using it FLSliceResult_Release(jsonData1); FLSliceResult_Release(jsonData2); // end::tojson-array[] } static void datatype_dictionary() { CBLDatabase *db = kDatabase; // tag::datatype_dictionary[] CBLError err; const CBLDocument *doc = CBLDatabase_GetDocument(db, FLSTR("doc1"), &err); FLDict properties = CBLDocument_Properties(doc); // Getting a dictionary from the document's properties FLValue dictValue = FLDict_Get(properties, FLSTR("address")); FLDict dict = FLValue_AsDict(dictValue); // Access a value with a key from the dictionary FLValue streetVal = FLDict_Get(dict, FLSTR("street")); FLString street = FLValue_AsString(streetVal); // Iterate dictionary FLDictIterator iter; FLDictIterator_Begin(dict, &iter); FLValue value; while (NULL != (value = FLDictIterator_GetValue(&iter))) { FLString key = FLDictIterator_GetKeyString(&iter); FLString strValue = FLValue_AsString(value); printf("Key :: %.*s\n", (int)key.size, (const char *)key.buf); printf("Value :: %.*s\n", (int)strValue.size, (const char *)strValue.buf); // ... FLDictIterator_Next(&iter); } // Create a mutable copy. // kFLDefaultCopy is shallow which means the nested dictionaries and arrays will be // referenced but not copied. Use kFLDeepCopyImmutables for the deep copy. FLMutableDict mutableDict = FLDict_MutableCopy(dict, kFLDefaultCopy); // Release when finish using it FLMutableDict_Release(mutableDict); // end::datatype_dictionary[] } static void datatype_mutable_dictionary() { CBLDatabase *db = kDatabase; // tag::datatype_mutable_dictionary[] // tag::datatype_mutable_dictionary-create[] // Create a new mutable dictionary and populate some keys/values FLMutableDict dict = FLMutableDict_New(); FLMutableDict_SetString(dict, FLSTR("street"), FLSTR("1 Main st.")); FLMutableDict_SetString(dict, FLSTR("city"), FLSTR("San Francisco")); // end::datatype_mutable_dictionary-create[] // tag::datatype_mutable_dictionary-add-to-doc[] // Set the dictionary to document's properties and save the document CBLDocument *doc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(doc); FLMutableDict_SetDict(properties, FLSTR("address"), dict); // end::datatype_mutable_dictionary-add-to-doc[] CBLError err; CBLDatabase_SaveDocument(db, doc, &err); CBLDocument_Release(doc); // Release when finish using it FLMutableDict_Release(dict); // end::datatype_mutable_dictionary[] } static void datatype_array() { CBLDatabase *db = kDatabase; // tag::datatype_array[] CBLError err; const CBLDocument *doc = CBLDatabase_GetDocument(db, FLSTR("doc1"), &err); FLDict properties = CBLDocument_Properties(doc); // Getting a phones array from the document's properties FLValue arrayValue = FLDict_Get(properties, FLSTR("phones")); FLArray array = FLValue_AsArray(arrayValue); // Get element count int count = FLArray_Count(array); printf("Count :: %d\n", count); // Access an array element by index if (!FLArray_IsEmpty(array)) { FLValue phoneVal = FLArray_Get(array, 0); FLString phone = FLValue_AsString(phoneVal); printf("Value :: %.*s\n", (int)phone.size, (const char *)phone.buf); } // Iterate array FLArrayIterator iter; FLArrayIterator_Begin(array, &iter); FLValue val; while (NULL != (val = FLArrayIterator_GetValue(&iter))) { FLString str = FLValue_AsString(val); printf("Value :: %.*s\n", (int)str.size, (const char *)str.buf); FLArrayIterator_Next(&iter); } // end::datatype_array[] // Create a mutable copy. // kFLDefaultCopy is shallow which means the nested dictionaries and arrays will be // referenced but not copied. Use kFLDeepCopyImmutables for the deep copy. FLMutableArray mutableArray = FLArray_MutableCopy(array, kFLDefaultCopy); // Release when finish using it FLMutableArray_Release(mutableArray); } static void datatype_mutable_array() { CBLDatabase *db = kDatabase; // tag::datatype_mutable_array[] // tag::datatype_mutable_array-create[] // Create a new mutable array and populate data into the array FLMutableArray phones = FLMutableArray_New(); FLMutableArray_AppendString(phones, FLSTR("650-000-0000")); FLMutableArray_AppendString(phones, FLSTR("650-000-0001")); // end::datatype_mutable_array-create[] // tag::datatype_mutable_array-add-to-doc[] // Set the array to document's properties and save the document CBLDocument *doc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(doc); FLMutableDict_SetArray(properties, FLSTR("phones"), phones); // end::datatype_mutable_array-add-to-doc[] CBLError err; CBLDatabase_SaveDocument(db, doc, &err); CBLDocument_Release(doc); // Release the created dictionary FLMutableArray_Release(phones); // end::datatype_mutable_array[] } static void datatype_usage() { // tag::datatype_usage[] // tag::datatype_usage_createdb[] // Open or create DB if it doesn't exist CBLError err; CBLDatabase* database = CBLDatabase_Open(FLSTR("mydb"), NULL, &err); if(!database) { // Error handling. For brevity, this is truncated in the rest of the snippet // and omitted in other doc code snippets fprintf(stderr, "Error opening database (%d / %d)\n", err.domain, err.code); FLSliceResult msg = CBLError_Message(&err); fprintf(stderr, "%.*s\n", (int)msg.size, (const char *)msg.buf); FLSliceResult_Release(msg); return; } // end::datatype_usage_createdb[] // tag::datatype_usage_createdoc[] // Create your new document // The lack of 'const' indicates this document is mutable CBLDocument* mutableDoc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(mutableDoc); // end::datatype_usage_createdoc[] // tag::datatype_usage_mutdict[] // Create and populate mutable dictionary FLMutableDict address = FLMutableDict_New(); FLMutableDict_SetString(address, FLSTR("street"), FLSTR("1 Main st.")); FLMutableDict_SetString(address, FLSTR("city"), FLSTR("San Francisco")); FLMutableDict_SetString(address, FLSTR("state"), FLSTR("CA")); FLMutableDict_SetString(address, FLSTR("country"), FLSTR("USA")); FLMutableDict_SetString(address, FLSTR("code"), FLSTR("90210")); // end::datatype_usage_mutdict[] // tag::datatype_usage_mutarray[] // Create and populate mutable array FLMutableArray phones = FLMutableArray_New(); FLMutableArray_AppendString(phones, FLSTR("650-000-0000")); FLMutableArray_AppendString(phones, FLSTR("650-000-0001")); // end::datatype_usage_mutarray[] // tag::datatype_usage_populate[] // Initialize and populate the document // Add document type and hotel name as string FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("hotel")); FLMutableDict_SetString(properties, FLSTR("hotel"), FLSTR("")); // Add average room rate (float) FLMutableDict_SetFloat(properties, FLSTR("room_rate"), 121.75f); // Add address (dictionary) FLMutableDict_SetDict(properties, FLSTR("address"), dict); // Add phone numbers(array) FLMutableDict_SetArray(properties, FLSTR("phones"), phones); // end::datatype_usage_populate[] // tag::datatype_usage_persist[] CBLError err; CBLDatabase_SaveDocument(database, doc, &err); // end::datatype_usage_persist[] // tag::datatype_usage_closedb[] CBLError err; CBLDatabase_Close(database, &err); // end::datatype_usage_closedb[] // tag::datatype_usage_release[] CBLDatabase_Release(database); CBLDocument_Release(doc); FLMutableDict_Release(dict); FLMutableArray_Release(phones); // end::datatype_usage_release[] // end::datatype_usage[] } // end datatype_usage() static void create_index() { CBLDatabase* db = kDatabase; // tag::query-index[] // For value types, this is optional but provides performance enhancements // NOTE: No error handling, for brevity (see getting started) // Syntax for second argument is the same as taking from a N1QL SELECT // i.e. SELECT (type, name) FROM _; CBLValueIndexConfiguration config = { kCBLN1QLLanguage, FLSTR("type, name") }; CBLError err; CBLDatabase_CreateValueIndex(db, FLSTR("TypeNameIndex"), config, &err); // end::query-index[] } static void select_meta() { CBLDatabase* db = kDatabase; // tag::query-select-meta[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, type, name FROM _"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString id = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("id"))); FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Document ID :: %.*s\n", (int)id.size, (const char *)id.buf); printf("Document Name :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-select-meta[] } static void select_id() { CBLDatabase* db = kDatabase; CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id FROM _"), NULL, &err); // tag::query-access-id[] // NOTE: No error handling, for brevity (see getting started) CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString id = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("id"))); printf("Document ID :: %.*s\n", (int)id.size, (const char *)id.buf); } // end::query-access-id[] CBLResultSet_Release(results); CBLQuery_Release(query); } static void query_change_listener(void* context, CBLQuery* query, CBLListenerToken* token) { CBLError err; CBLResultSet* results = CBLQuery_CopyCurrentResults(query, token, &err); while(CBLResultSet_Next(results)) { // Update UI } } static void select_all() { CBLDatabase* db = kDatabase; // tag::query-select-all[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _"), NULL, &err); // All results will be available from the above query CBLQuery_Release(query); // end::query-select-all[] // tag::live-query[] /* static void query_change_listener(void* context, CBLQuery* query, CBLListenerToken* token) { CBLError err; CBLResultSet* results = CBLQuery_CopyCurrentResults(query, token, &err); while(CBLResultSet_Next(results)) { // Update UI } } */ query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _"), NULL, &err); (5) CBLListenerToken* token = CBLQuery_AddChangeListener(query, query_change_listener, NULL); (6) // end::live-query[] // tag::stop-live-query[] CBLListener_Remove(token); // The token received from AddChangeListener CBLQuery_Release(query); // end::stop-live-query[] } static void select_and_access_all() { CBLDatabase* db = kDatabase; CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _"), NULL, &err); // tag::query-access-all[] CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLDict dict = FLValue_AsDict(CBLResultSet_ValueForKey(results, FLSTR("_"))); FLString id = FLValue_AsString(FLDict_Get(dict, FLSTR("id"))); FLString type = FLValue_AsString(FLDict_Get(dict, FLSTR("type"))); FLString name = FLValue_AsString(FLDict_Get(dict, FLSTR("name"))); FLString city = FLValue_AsString(FLDict_Get(dict, FLSTR("city"))); printf("ID :: %.*s\n", (int)id.size, (const char *)id.buf); printf("Type :: %.*s\n", (int)type.size, (const char *)type.buf); printf("Name :: %.*s\n", (int)name.size, (const char *)name.buf); printf("City :: %.*s\n", (int)city.size, (const char *)city.buf); } // All results will be available from the above query CBLResultSet_Release(results); // end::query-access-all[] CBLQuery_Release(query); } static void select_props() { CBLDatabase* db = kDatabase; // tag::query-access-props[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT type, name, city FROM _"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString type = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("type"))); FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); FLString city = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("city"))); printf("Type :: %.*s\n", (int)type.size, (const char *)type.buf); printf("Name :: %.*s\n", (int)name.size, (const char *)name.buf); printf("City :: %.*s\n", (int)city.size, (const char *)city.buf); } // end::query-access-props[] // All results will be available from the above query CBLResultSet_Release(results); CBLQuery_Release(query); } static void select_where() { CBLDatabase* db = kDatabase; // tag::query-where[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE type = \"hotel\" LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLDict dict = FLValue_AsDict(CBLResultSet_ValueForKey(results, FLSTR("_"))); FLString name = FLValue_AsString(FLDict_Get(dict, FLSTR("name"))); printf("Document Name :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-where[] } static void use_collection_contains() { CBLDatabase* db = kDatabase; // tag::query-collection-operator-contains[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name, public_likes FROM _ WHERE type = \"hotel\" " "AND ARRAY_CONTAINS(public_likes, \"Armani Langworth\")"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLArray publicLikes = FLValue_AsArray(CBLResultSet_ValueForKey(results, FLSTR("public_likes"))); FLStringResult json = FLValue_ToJSON((FLValue)publicLikes); printf("Public Likes :: %.*s\n", (int)json.size, (const char *)json.buf); FLSliceResult_Release(json); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-collection-operator-contains[] } static void use_collection_in() { CBLDatabase* db = kDatabase; // tag::query-collection-operator-in[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE \"Armani\" IN (first, last, username)"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLDict body = FLValue_AsDict(CBLResultSet_ValueAtIndex(results, 0)); FLStringResult json = FLValue_ToJSON((FLValue)body); printf("In results :: %.*s\n", (int)json.size, (const char *)json.buf); FLSliceResult_Release(json); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-collection-operator-in[] } static void select_like() { CBLDatabase* db = kDatabase; // tag::query-like-operator[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" " "AND lower(name) LIKE \"Royal Engineers Museum\" LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-like-operator[] } static void select_wildcard_like() { CBLDatabase* db = kDatabase; // tag::query-like-operator-wildcard-match[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" " "AND lower(name) LIKE \"Eng%e%\" LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-like-operator-wildcard-match[] } static void select_wildcard_character_like() { CBLDatabase* db = kDatabase; // tag::query-like-operator-wildcard-character-match[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" " "AND lower(name) LIKE \"Royal Eng____rs Museum\" LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-like-operator-wildcard-character-match[] } static void select_regex() { CBLDatabase* db = kDatabase; // tag::query-regex-operator[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" " "AND regexp_like(name, \"\\bEng.*e\\b\") LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-regex-operator[] } static void select_join() { CBLDatabase* db = kDatabase; // tag::query-join[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT airline.name, airline.callsign, route.destinationairport, route.stops, route.airline " "FROM _ AS airline INNER JOIN _ AS route ON meta(airline).id = route.airlineid " "WHERE route.type = \"route\" AND airline.type = \"airline\" AND route.sourceairport = \"RIX\""), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-join[] } static void group_by() { CBLDatabase* db = kDatabase; // tag::query-groupby[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT count(*), country, tz FROM _ WHERE type = \"airport\" AND geo.alt >= 300 " "GROUP BY country, tz"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { int64_t count = FLValue_AsInt(CBLResultSet_ValueForKey(results, FLSTR("$1"))); FLString tz = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("tz"))); FLString country = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("country"))); printf("There are %" PRIi64 " airports in the %.*s timezone located in %.*s and above 300 ft\n", count, (int)tz.size, (const char *)tz.buf, (int)country.size, (const char *)country.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-groupby[] } static void order_by() { CBLDatabase* db = kDatabase; // tag::query-orderby[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, title FROM _ WHERE type = \"hotel\" ORDER BY title ASC LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString title = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("title"))); printf("Title :: %.*s\n", (int)title.size, (const char *)title.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-orderby[] } static void test_explain_statement() { CBLDatabase* db = kDatabase; { // tag::query-explain-all[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE type = \"hotel\" GROUP BY country ORDER BY title ASC LIMIT 10"), NULL, &err); FLSliceResult explanation = CBLQuery_Explain(query); printf("%.*s", (int)explanation.size, (const char *)explanation.buf); FLSliceResult_Release(explanation); // end::query-explain-all[] } // DOCS NOTE: Others omitted for now } static void query_result_json() { CBLDatabase* db = kDatabase; CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id as id, name, city, type FROM _ LIMIT 10"), NULL, &err); // tag::query-access-json[] CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLDict result = CBLResultSet_ResultDict(results); FLStringResult json = FLValue_ToJSON((FLValue)result); printf("JSON Result :: %.*s\n", (int)json.size, (const char *)json.buf); FLSliceResult_Release(json); } CBLResultSet_Release(results); // end::query-access-json[] CBLQuery_Release(query); } static void create_full_text_index() { CBLDatabase* db = kDatabase; const char* tasks[] = { "buy groceries", "play chess", "book travels", "buy museum tickets" }; char idBuffer[7]; for(int i = 0; i < 4; i++) { const char* task = tasks[i]; sprintf(idBuffer, "extra%d", i); const CBLDocument* doc = CBLDatabase_GetDocument(db, FLStr(idBuffer), NULL); if(doc) { CBLDocument_Release(doc); continue; } CBLDocument* mutableDoc = CBLDocument_CreateWithID(FLStr(idBuffer)); FLMutableDict properties = CBLDocument_MutableProperties(mutableDoc); FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("task")); FLMutableDict_SetString(properties, FLSTR("task"), FLStr(task)); CBLDatabase_SaveDocument(db, mutableDoc, NULL); CBLDocument_Release(mutableDoc); } // tag::fts-index[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLFullTextIndexConfiguration config = { kCBLN1QLLanguage, FLSTR("name"), false }; CBLDatabase_CreateFullTextIndex(db, FLSTR("nameFTSIndex"), config, &err); // end::fts-index[] } static void full_text_search() { CBLDatabase* db = kDatabase; // tag::fts-query[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id FROM _ WHERE MATCH(nameFTSIndex, \"'buy'\")"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString id = FLValue_AsString(CBLResultSet_ValueAtIndex(results, 0)); printf("Document id :: %.*s\n", (int)id.size, (const char *)id.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::fts-query[] } static void start_replication() { CBLDatabase* db = kDatabase; /* * This requires Sync Gateway running with the following config, or equivalent: * * { * "log":["*"], * "databases": { * "db": { * "server":"walrus:", * "users": { * "GUEST": {"disabled": false, "admin_channels": ["*"] } * } * } * } * } */ // tag::replication[] // NOTE: No error handling, for brevity (see getting started) // Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion) CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; config.replicatorType = kCBLReplicatorTypePull; CBLReplicator* replicator = CBLReplicator_Create(&config, &err); CBLEndpoint_Free(target); CBLReplicator_Start(replicator, false); // end::replication[] stop_replicator(replicator); } // Console logging domain methods are not applicable to C static void file_logging() { // tag::file-logging[] // NOTE: No error handling, for brevity (see getting started) // NOTE: You will need to use a platform appropriate method for finding // a temporary directory FLString tempFolder = FLSTR("/tmp/cbllog"); CBLLogFileConfiguration config; // Don't bother zeroing, since we set all properties config.level = kCBLLogInfo; config.directory = tempFolder; config.maxRotateCount = 5; config.maxSize = 10240; config.usePlaintext = false; CBLError err; CBLLog_SetFileConfig(config, &err); // end::file-logging[] } // tag::custom-logging[] static void custom_log_callback(CBLLogDomain domain, CBLLogLevel level, FLString message) { // handle the message, for example piping it to // a third party framework } // end::custom-logging[] static void enable_custom_logging() { // tag::set-custom-logging[] CBLLog_SetCallback(custom_log_callback); // end::set-custom-logging[] } static void enable_basic_auth() { CBLDatabase* db = kDatabase; // tag::basic-authentication-full[] // NOTE: No error handling, for brevity (see getting started) CBLError err; FLString url = FLSTR("ws://localhost:4984/mydatabase"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("john"), FLSTR("pass")); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; config.authenticator = basicAuth; CBLReplicator* replicator = CBLReplicator_Create(&config, &err); CBLEndpoint_Free(target); CBLAuth_Free(basicAuth); CBLReplicator_Start(replicator, false); // end::basic-authentication-full[] stop_replicator(replicator); } static void docsonly_N1QL_Params(CBLDatabase* argDb) { CBLDatabase* database = argDb; // tag::query-syntax-n1ql-params[] int errorPos; CBLError err; FLString n1qlstr = FLSTR("SELECT * FROM _ WHERE type = $type"); FLMutableDict n1qlparams = FLMutableDict_New(); FLMutableDict_SetString(n1qlparams, FLSTR("type"), FLSTR("hotel")); CBLQuery* query = CBLDatabase_CreateQuery(database, kCBLN1QLLanguage, n1qlstr, &errorPos, &err); CBLQuery_SetParameters(query, n1qlparams); if(!query) { /* Do appropriate error handling ... Note that (where applicable) errorPos contains the position in the N1QL string that the parse failed */ FLMutableDict_Release(n1qlparams); CBLQuery_Release(query); return; } CBLResultSet* result = CBLQuery_Execute(query, &err); if(!result) { // Failed to run query, do error handling ... return; } // Release query when finished with FLMutableDict_Release(n1qlparams); CBLQuery_Release(query); // ... process results as required // Release result set then finished with CBLResultSet_Release(result); // end::query-syntax-n1ql-params[] } // tag::console-logging-db[] //Placeholder for code to increase level of console logging for kCBLLogDomainDatabase domain // end::console-logging-db[] // tag::console-logging[] //Placeholder for code to increase level of console logging for all domains // end::console-logging[] // tag::date-getter[] //Placeholder for Date accessors. // end::date-getter[] // tag::query-index[] // placeholder // end::query-index[] // DOCS NOTE // Page=Data Sync >> Configuration Summary // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#configuration-summary static void docs_act_replication(CBLDatabase* argDb) { CBLDatabase* database = argDb; /* * This requires Sync Gateway running with the following config, or equivalent: * * { * "log":["*"], * "databases": { * "db": { * "server":"walrus:", * "users": { * "GUEST": {"disabled": false, "admin_channels": ["*"] } * } * } * } * } */ // tag::p2p-act-rep-func[] // Purpose -- Show configuration , initialization and running of a replicator // NOTE: No error handling, for brevity (see getting started) // Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion) CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); (7) CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = database; config.endpoint = target; (8) // tag::p2p-act-rep-config-cont[] // Set replication direction and mode config.replicatorType = kCBLReplicatorTypePull; (9) config.continuous = true; // end::p2p-act-rep-config-cont[] // Optionally, set auto-purge behavior (here we override default) config.disableAutoPurge = true; (10) // Optionally, configure Client Authentication // Here we are using to Basic Authentication, // Providing username and password credentials CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("username"), FLSTR("passwd")); (11) config.authenticator = basicAuth; // Optionally, configure how we handle conflicts config.conflictResolver = simpleConflictResolver_localWins; (12) // Initialize replicator with created config CBLReplicator* replicator = CBLReplicator_Create(&config, &err); (13) CBLEndpoint_Free(target); // Optionally, add change listener CBLListenerToken* token = CBLReplicator_AddChangeListener(replicator, simpleChangeListener, NULL); (14) // Start replication CBLReplicator_Start(replicator, false); (15) // end::p2p-act-rep-func[] // ... other processing as required // When finished release resources e.g. CBLAuth_Free(basicAuth); stop_replicator(replicator); } // END configuration summary snippets // DOCS NOTE: // Page=Data Sync >> Configuration // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#configuration-summary // This function is not pulled into docs en-bloc // it is a slightly more in-depth than the configurationsummary above // and the snippets within it are used individually or in sets // to illustrate specific points as required // static void docs_act_replication_config_section_snippets() { CBLDatabase* db = kDatabase; bool docs_example_ShowBasicAuth = false; bool docs_example_ShowSessionAuth = false; /* * This requires Sync Gateway running with the following config, or equivalent: * * { * "log":["*"], * "databases": { * "db": { * "server":"walrus:", * "users": { * "GUEST": {"disabled": false, "admin_channels": ["*"] } * } * } * } * } */ // tag::p2p-act-rep-func-full[] // NOTE: No error handling, for brevity (see getting started) // Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion) // tag::sgw-act-rep-initialize[] // Initialize the configuration object and set db target CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); (16) CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; (17) // end::sgw-act-rep-initialize[] // tag::p2p-act-rep-config-type[] config.replicatorType = kCBLReplicatorTypePull; // end::p2p-act-rep-config-type[] // tag::p2p-act-rep-config-cont[] config.continuous = true; // end::p2p-act-rep-config-cont[] // tag::replication-retry-config[] // Configure replication retries // tag::replication-set-heartbeat[] config.heartbeat = 120; // (18) // end::replication-set-heartbeat[] // tag::replication-set-maxattempts[] config.maxAttempts = 20; // (19) // end::replication-set-maxattempts[] // tag::replication-set-maxattemptwaittime[] config.maxAttemptWaitTime = 600; // (20) // end::replication-set-maxattemptwaittime[] // end::replication-retry-config[] // tag::basic-authentication[] // Configure Client Authentication to Basic Authentication // Providing username and password credentials if(docs_example_ShowBasicAuth) { CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("username"), FLSTR("passwd")); config.authenticator = basicAuth; (21) } // end::basic-authentication[] // tag::session-authentication[] if(docs_example_ShowSessionAuth) { CBLAuthenticator* sessionAuth = CBLAuth_CreateSession(FLSTR("904ac010862f37c8dd99015a33ab5a3565fd8447"), FLSTR("optionalCookieName")); config.authenticator = sessionAuth; (22) } // end::session-authentication[] // tag::replication-custom-header[] // Optionally, add custom headers FLMutableDict customHdrs = FLMutableDict_New(); FLMutableDict_SetString(customHdrs, FLSTR("customHeaderName"), FLSTR("customHeaderValue")); config.headers = customHdrs; // tag::certificate-pinning[] char cert_buf[10000]; FILE* cert_file = fopen("cert.pem", "r"); size_t read = fread(cert_buf, 1, sizeof(cert_buf), cert_file); config.pinnedServerCertificate = (FLSlice){cert_buf, read}; // end::certificate-pinning[] // end::replication-custom-header[] // FILTERS // tag::replication-push-filter[] // tag::replication-pull-filter[] // Purpose - Illustrate use of push and-or pull filter functions config.pushFilter = simpleReplicationFilter; config.pullFilter = simpleReplicationFilter; // end::replication-pull-filter[] // end::replication-push-filter[] // Auto-purge over-ride // tag::autopurge-override[] config.disableAutoPurge = true; (23) // end::autopurge-override[] // Initialize replicator with created config CBLReplicator* replicator = CBLReplicator_Create(&config, &err); (24) CBLEndpoint_Free(target); // Add optional change listener CBLListenerToken* token_ReplChangeListener = CBLReplicator_AddChangeListener(replicator, simpleChangeListener, NULL); (25) } // END replication.html >> configure section // PAGE=Data Sync >> Initialize section // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#lbl-init-repl static CBLReplicator* docs_act_replication_Intialize( void* context, CBLReplicatorConfiguration argConfig, bool argResetRequired) { CBLError err; bool docs_example_resetRequired = argResetRequired; // tag::p2p-act-rep-start-full[] CBLReplicator* thisRepl = CBLReplicator_Create(&argConfig, &err); (26) // end::p2p-act-rep-start-full[] if(!docs_example_resetRequired) { // tag::p2p-act-rep-start-full[] CBLReplicator_Start(thisRepl, false); (27) // end::p2p-act-rep-start-full[] } else { // tag::replication-reset-checkpoint[] CBLReplicator_Start(thisRepl, true); (28) // end::replication-reset-checkpoint[] } return thisRepl; } // END replication.html >> initialize section // PAGE=Data Sync >> Monitor section // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#lbl-repl-mon static void docs_act_replication_Monitor( void* context, CBLReplicator* argRepl) { CBLError err; CBLReplicator* thisRepl = argRepl; // tag::p2p-act-rep-add-change-listener[] // Purpose -- illustrate addition of a Replicator change listener CBLListenerToken* token_ReplChangeListener = CBLReplicator_AddChangeListener(thisRepl, simpleChangeListener, NULL); // end::p2p-act-rep-add-change-listener[] // tag::add-document-replication-listener[] // Purpose -- illustrate addition of a Document Replicator listener CBLListenerToken* token_ReplDocListener = CBLReplicator_AddDocumentReplicationListener( thisRepl, SimpleReplicationDocumentListener, context); // end::add-document-replication-listener[] // tag::remove-document-replication-listener[] // Purpose -- illustrate removal of a listener CBLListener_Remove(token_ReplDocListener); CBLListener_Remove(token_ReplChangeListener); // end::remove-document-replication-listener[] // tag::p2p-act-rep-status[] // Purpose -- illustrate use of CBLReplicator_Status() CBLReplicatorStatus thisState = CBLReplicator_Status(thisRepl); if(thisState.activity==kCBLReplicatorStopped) { if(thisState.error.code==0) { CBLReplicator_Start(thisRepl,false); } else { printf("Replicator stopped -- code %d", thisState.error.code); // ... handle error ... CBLReplicator_Release(thisRepl); } } // end::p2p-act-rep-status[] // tag::replication-pendingdocuments[] FLDict thisPendingIdList = CBLReplicator_PendingDocumentIDs(thisRepl, &err); (29) if(!FLDict_IsEmpty(thisPendingIdList)) { FLDictIterator item; FLDictIterator_Begin(thisPendingIdList, &item); FLValue itemValue; FLString pendingId; while(NULL != (itemValue = FLDictIterator_GetValue(&item))) { pendingId = FLValue_AsString(itemValue); if(CBLReplicator_IsDocumentPending(thisRepl, pendingId, &err)) { // ... process the still pending docid as required (30) } else { // Doc Id no longer pending if(err.code==0) { // No fail so must have already been pushed printf("Document already pushed"); } else { // Error detected so handle it printf("Error code %d checking for pendingId", err.code); break; } } FLDictIterator_Next(&item); } FLDictIterator_End(&item); FLValue_Release(itemValue); } else { printf("No Pending Id Docs to process"); } FLDict_Release(thisPendingIdList); // end::replication-pendingdocuments[] } // END replication.html >> Monitor section // BEGIN replication.html >> Stop section // PAGE=Data Sync >> Stop // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#lbl-repl-stop static void docs_act_replication_Stop( void* context, CBLReplicator* argRepl) { // tag::p2p-act-rep-stop[] // Purpose -- show how to stop a replication if(CBLReplicator_Status(argRepl).activity!=kCBLReplicatorStopped) { CBLReplicator_Stop(argRepl); } // end::p2p-act-rep-stop[] } // END replication.html >> Stop section static void replication_error_handling() { CBLDatabase* db = kDatabase; CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; CBLReplicator* replicator = CBLReplicator_Create(&config, &err); CBLEndpoint_Free(target); stop_replicator(replicator); } static void create_encryptable() { #ifdef COUCHBASE_ENTERPRISE // tag::encryptable[] // NOTE: No error handling, for brevity (see getting started) // Create with premitive type CBLEncryptable* encNull = CBLEncryptable_CreateWithNull(); CBLEncryptable* encBool = CBLEncryptable_CreateWithBool(true); CBLEncryptable* encInt = CBLEncryptable_CreateWithInt(256); CBLEncryptable* encUInt = CBLEncryptable_CreateWithUInt(1024); CBLEncryptable* encFloat = CBLEncryptable_CreateWithFloat(1.2); CBLEncryptable* encDouble = CBLEncryptable_CreateWithDouble(100.50); CBLEncryptable* encString = CBLEncryptable_CreateWithString(FLSTR("foo")); // Create with dictionary FLMutableDict dict = FLMutableDict_New(); FLSlot_SetString(FLMutableDict_Set(dict, FLSTR("greeting")), FLSTR("hello")); CBLEncryptable* encDict = CBLEncryptable_CreateWithDict(dict); // Create with array FLMutableArray array = FLMutableArray_New(); FLSlot_SetString(FLMutableArray_Append(array), FLSTR("item1")); CBLEncryptable* encArray = CBLEncryptable_CreateWithArray(array); // Create with FLValue FLMutableDict dict2 = FLMutableDict_New(); FLSlot_SetString(FLMutableDict_Set(dict2, FLSTR("greeting")), FLSTR("hello")); CBLEncryptable* encValue = CBLEncryptable_CreateWithValue((FLValue)dict2); // end::encryptable[] // Release after using it CBLEncryptable_Release(encNull); CBLEncryptable_Release(encBool); CBLEncryptable_Release(encInt); CBLEncryptable_Release(encBool); CBLEncryptable_Release(encUInt); CBLEncryptable_Release(encFloat); CBLEncryptable_Release(encDouble); CBLEncryptable_Release(encString); CBLEncryptable_Release(encDict); CBLEncryptable_Release(encArray); CBLEncryptable_Release(encValue); FLMutableDict_Release(dict); FLMutableDict_Release(dict2); FLMutableArray_Release(array); #endif } static void release_encryptable() { #ifdef COUCHBASE_ENTERPRISE CBLEncryptable* encValue = CBLEncryptable_CreateWithString(FLSTR("foo")); // tag::release_encryptable[] // Release the encryptable value after finish using it CBLEncryptable_Release(encValue); // end::release_encryptable[] #endif } static void use_encryptable() { #ifdef COUCHBASE_ENTERPRISE CBLDatabase* db = kDatabase; // tag::use_encryptable[] // NOTE: No error handling, for brevity (see getting started) CBLDocument* doc = CBLDocument_CreateWithID(FLSTR("doc1")); // Set encryptable: FLMutableDict props = CBLDocument_MutableProperties(doc); CBLEncryptable* encryptable = CBLEncryptable_CreateWithString(FLSTR("My secret")); FLSlot_SetEncryptableValue(FLMutableDict_Set(props, FLSTR("secret")), encryptable); CBLError error; CBLDatabase_SaveDocument(db, doc, &error); // Release CBLDocument_Release(doc); CBLEncryptable_Release(encryptable); // end::release_encryptable[] #endif } static void query_encryptable() { #ifdef COUCHBASE_ENTERPRISE CBLDatabase* db = kDatabase; // tag::use_encryptable[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT secret, secret.value as secretValue FROM _ WHERE type = \"profile\""), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { // Get secret as CBLEncryptable value FLValue value = CBLResultSet_ValueForKey(results, FLSTR("secret")); const CBLEncryptable* encValue = FLValue_GetEncryptableValue(value); FLString secretStr = FLValue_AsString(CBLEncryptable_Value(encValue)); printf("Secret :: %.*s\n", (int)secretStr.size, (const char *)secretStr.buf); // Get secret value directly value = CBLResultSet_ValueForKey(results, FLSTR("secretValue")); secretStr = FLValue_AsString(value); printf("Secret :: %.*s\n", (int)secretStr.size, (const char *)secretStr.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::release_encryptable[] #endif } #ifdef COUCHBASE_ENTERPRISE // tag::replicator_property_encryption[] // tag::replicator_property_encryptor_decryptor_sample[] // Purpose: Declare property-level encryptor callback functions static FLSliceResult my_cipher_function(FLSlice input) { FLSliceResult result = FLSliceResult_New(input.size); for(int i = 0; i < input.size; ++i) { ((uint8_t*)(result.buf))[i] = ((uint8_t*)input.buf)[i] ^ 'K';} return result; } static FLSliceResult property_encryptor(void* context, FLString docID, FLDict props, FLString path, FLSlice input, FLStringResult* algorithm, FLStringResult* keyID, CBLError* error) { *algorithm = FLSlice_Copy(FLSTR("MyEnc")); return my_cipher_function(input); } static FLSliceResult property_decryptor(void* context, FLString documentID, FLDict properties, FLString keyPath, FLSlice input, FLString algorithm, FLString keyID, CBLError* error) { return my_cipher_function(input); } // end::replicator_property_encryptor_decryptor_sample[] // end::replicator_property_encryption[] #endif // PAGE=Field Level Encryption // URL=https://docs.couchbase.com/couchbase-lite/current/c/field-level-encryption.html // static void replicator_property_encryption() { #ifdef COUCHBASE_ENTERPRISE CBLDatabase* db = kDatabase; // tag::replicator_property_encryption[] // Purpose: Show how to declare en(de)cryptors in replicator config // NOTE: No error handling, for brevity (see getting started) CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; config.propertyEncryptor = property_encryptor; (31) config.propertyDecryptor = property_decryptor; (32) CBLReplicator* replicator = CBLReplicator_Create(&config, &err); CBLEndpoint_Free(target); CBLReplicator_Start(replicator, false); // end::replicator_property_encryption[] stop_replicator(replicator); #endif } int main(int argc, char** argv) { create_new_database(); create_document(); update_document(); do_batch_operation(); // Disable use_blob() as no avatar.jpg to load and crash // use_blob(); doc_json(); dict_json(); array_json(); load_prebuilt(); create_index(); select_all(); select_and_access_all(); select_props(); select_meta(); select_id(); select_where(); use_collection_contains(); select_like(); select_wildcard_like(); select_wildcard_character_like(); select_regex(); select_join(); group_by(); order_by(); query_result_json(); create_full_text_index(); full_text_search(); start_replication(); replicator_property_encryption(); CBLDatabase_Close(kDatabase, NULL); return 0; } -
Store imported identity in keychain
// CBL Version 3.0.0 BETA
#include <cbl/CouchbaseLite.h>
#include <time.h>
#include <inttypes.h>
#ifdef _MSC_VER
#include <direct.h>
#include <Shlwapi.h>
void usleep(unsigned int us) {
Sleep(us / 1000);
}
#else
#include <unistd.h>
#endif
static CBLDatabase* kDatabase;
// Helper for stop replicator in the code snippet
static void stop_replicator(CBLReplicator* replicator) {
CBLReplicator_Stop(replicator);
while(CBLReplicator_Status(replicator).activity != kCBLReplicatorStopped) {
printf("Waiting for replicator to stop...");
usleep(200000);
}
CBLReplicator_Release(replicator);
}
// BEGIN lower-level function declarations
// DOCS NOTE --
// These functions are referred to in subsequent code samples.
// Their tags will ensure they are shown alongide the usage examples.
// Functions used in more than one place may hve multiple tags.
// tag::p2p-act-rep-func[]
// tag::p2p-act-rep-add-change-listener[]
// tag::replication-error-handling[]
// Purpose -- illustrate a simple change listener
static void simpleChangeListener(void* context,
CBLReplicator* repl,
const CBLReplicatorStatus* status)
{
if(status->error.code != 0) {
printf("Error %d / %d\n",
status->error.domain,
status->error.code);
}
}
// end::replication-error-handling[]
// end::p2p-act-rep-add-change-listener[]
// end::p2p-act-rep-func[]
static const CBLDocument* simpleConflictResolver_localWins(
void* context, FLString documentID,
const CBLDocument* localDocument,
const CBLDocument* remoteDocument)
{
return localDocument;
}
// tag::replication-push-filter[]
// tag::replication-pull-filter[]
// Purpose -- illustrate a simple replication filter function
static bool simpleReplicationFilter(void* context,
CBLDocument* argDoc,
CBLDocumentFlags argFlags)
{
bool result = (argFlags == kCBLDocumentFlagsDeleted);
return result;
}
// end::replication-push-filter[]
// end::replication-pull-filter[]
// tag::SimpleReplicationDocumentListener[]
// Purpose -- Illustrate a simple replication document listener
static void SimpleReplicationDocumentListener(
void *context,
CBLReplicator *replicator,
bool isPush,
unsigned numDocuments,
const CBLReplicatedDocument *documents)
{
if(isPush) {
printf("We pushed %d documents",numDocuments);
}
}
// END lower-level function declarations
static void getting_started_change_listener(void* context,
CBLReplicator* repl,
const CBLReplicatorStatus* status)
{
if(status->error.code != 0) {
printf("Error %d / %d\n", status->error.domain, status->error.code);
}
}
// Page=build and run
// url=https://docs-staging.couchbase.com/couchbase-lite/current/c/gs-build.html
static void getting_started() {
// tag::getting-started[]
// Purpose-- provide an overview of available crud and sync functionality
//
// Get the database (and create it if it doesn't exist)
CBLError err;
CBLDatabase* database = CBLDatabase_Open(FLSTR("mydb"), NULL, &err);
if(!database) {
// Error handling. For brevity, this is truncated in the rest of the snippet
// and omitted in other doc code snippets
fprintf(stderr, "Error opening database (%d / %d)\n", err.domain, err.code);
FLSliceResult msg = CBLError_Message(&err);
fprintf(stderr, "%.*s\n", (int)msg.size, (const char *)msg.buf);
FLSliceResult_Release(msg);
return;
}
// The lack of 'const' indicates this document is mutable
// Create a new document (i.e. a record) in the database
CBLDocument* mutableDoc = CBLDocument_Create();
FLMutableDict properties = CBLDocument_MutableProperties(mutableDoc);
FLMutableDict_SetFloat(properties, FLSTR("version"), 3.0f);
// Save it to the database
if(!CBLDatabase_SaveDocument(database, mutableDoc, &err)) {
// Failed to save, do error handling as above
return;
}
// Since we will release the document, make a copy of the ID since it
// is an internal pointer. Whenever we create or get an FLSliceResult
// or FLStringResult we will need to free it later too!
FLStringResult id = FLSlice_Copy(CBLDocument_ID(mutableDoc));
CBLDocument_Release(mutableDoc);
// Update a document
mutableDoc = CBLDatabase_GetMutableDocument(database, FLSliceResult_AsSlice(id), &err);
if(!mutableDoc) {
// Failed to retrieve, do error handling as above. NOTE: error code 0 simply means
// the document does not exist.
return;
}
properties = CBLDocument_MutableProperties(mutableDoc);
FLMutableDict_SetString(properties, FLSTR("language"), FLSTR("C"));
if(!CBLDatabase_SaveDocument(database, mutableDoc, &err)) {
// Failed to save, do error handling as above
return;
}
// Note const here, means readonly
const CBLDocument* docAgain = CBLDatabase_GetDocument(database, FLSliceResult_AsSlice(id), &err);
if(!docAgain) {
// Failed to retrieve, do error handling as above. NOTE: error code 0 simply means
// the document does not exist.
return;
}
// No copy this time, so no release later (notice it is not FLStringResult this time)
FLString retrievedID = CBLDocument_ID(docAgain);
FLDict retrievedProperties = CBLDocument_Properties(docAgain);
FLString retrievedLanguage = FLValue_AsString(FLDict_Get(retrievedProperties, FLSTR("language")));
printf("Document ID :: %.*s\n", (int)retrievedID.size, (const char *)retrievedID.buf);
printf("Learning %.*s\n", (int)retrievedLanguage.size, (const char *)retrievedLanguage.buf);
CBLDocument_Release(mutableDoc);
CBLDocument_Release(docAgain);
FLSliceResult_Release(id);
// tag::query-syntax-n1ql[]
// Create a query to fetch documents of type SDK
int errorPos;
CBLQuery* query = CBLDatabase_CreateQuery(database, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE type = \"SDK\""), &errorPos, &err);
if(!query) {
// Failed to create query, do error handling as above
// Note that errorPos will contain the position in the N1QL string
// that the parse failed, if applicable
return;
}
CBLResultSet* result = CBLQuery_Execute(query, &err);
if(!result) {
// Failed to run query, do error handling as above
return;
}
// end::query-syntax-n1ql[]
// TODO: Result set count?
CBLResultSet_Release(result);
CBLQuery_Release(query);
// Create replicator to push and pull changes to and from the cloud
CBLEndpoint* targetEndpoint = CBLEndpoint_CreateWithURL(FLSTR("ws://localhost:4984/getting-started-db"), &err);
if(!targetEndpoint) {
// Failed to create endpoint, do error handling as above
return;
}
CBLReplicatorConfiguration replConfig;
CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("john"), FLSTR("pass"));
memset(&replConfig, 0, sizeof(replConfig));
replConfig.database = database;
replConfig.endpoint = targetEndpoint;
replConfig.authenticator = basicAuth;
CBLReplicator* replicator = CBLReplicator_Create(&replConfig, &err);
CBLAuth_Free(basicAuth);
CBLEndpoint_Free(targetEndpoint);
if(!replicator) {
// Failed to create replicator, do error handling as above
return;
}
// Assume a function like the simple following
//
// static void getting_started_change_listener(void* context, CBLReplicator* repl, const CBLReplicatorStatus* status) {
// if(status->error.code != 0) {
// printf("Error %d / %d\n", status->error.domain, status->error.code);
// }
// }
CBLListenerToken* token = CBLReplicator_AddChangeListener(replicator, getting_started_change_listener, NULL);
CBLReplicator_Start(replicator, false);
// Later, stop and release the replicator
// end::getting-started[]
// When finished release resources ... eg
CBLListener_Remove(token);
stop_replicator(replicator);
}
// tag::local-win-conflict-resolver[]
static const CBLDocument* local_win_conflict_resolver(void* context,
FLString documentID,
const CBLDocument* localDocument,
const CBLDocument* remoteDocument)
{
return localDocument;
}
// end::local-win-conflict-resolver[]
// tag::remote-win-conflict-resolver[]
static const CBLDocument* remote_win_conflict_resolver(void* context,
FLString documentID,
const CBLDocument* localDocument,
const CBLDocument* remoteDocument)
{
return remoteDocument;
}
// end::remote-win-conflict-resolver[]
// tag::merge-conflict-resolver[]
static const CBLDocument* merge_conflict_resolver(void* context,
FLString documentID,
const CBLDocument* localDocument,
const CBLDocument* remoteDocument)
{
FLDict localProps = CBLDocument_Properties(localDocument);
FLDict remoteProps = CBLDocument_Properties(remoteDocument);
FLMutableDict mergeProps = FLDict_MutableCopy(localProps, kFLDefaultCopy);
FLDictIterator d;
FLDictIterator_Begin(localProps, &d);
FLValue value;
while((value = FLDictIterator_GetValue(&d))) {
FLString key = FLDictIterator_GetKeyString(&d);
if(FLDict_Get(mergeProps, key)) {
continue;
}
FLMutableDict_SetValue(mergeProps, key, value);
FLDictIterator_Next(&d);
}
CBLDocument* mergeDocument = CBLDocument_CreateWithID(documentID);
CBLDocument_SetProperties(mergeDocument, mergeProps);
FLMutableDict_Release(mergeProps);
return mergeDocument;
}
// end::merge-conflict-resolver[]
static void test_replicator_conflict_resolve() {
CBLDatabase* database = kDatabase;
// tag::replication-conflict-resolver[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLEndpoint* target = CBLEndpoint_CreateWithURL(FLSTR("ws://localhost:4984/mydatabase"), &err);
CBLReplicatorConfiguration replConfig;
memset(&replConfig, 0, sizeof(replConfig));
replConfig.database = database;
replConfig.endpoint = target;
replConfig.conflictResolver = local_win_conflict_resolver;
CBLReplicator* replicator = CBLReplicator_Create(&replConfig, &err);
CBLEndpoint_Free(target);
CBLReplicator_Start(replicator, false);
// end::replication-conflict-resolver[]
stop_replicator(replicator);
}
static bool custom_conflict_handler(void* context,
CBLDocument* documentBeingSaved,
const CBLDocument* conflictingDocument)
{
FLDict currentProps = CBLDocument_Properties(conflictingDocument);
FLDict updatedProps = CBLDocument_Properties(documentBeingSaved);
FLMutableDict newProps = FLDict_MutableCopy(updatedProps, kFLDefaultCopy);
FLDictIterator d;
FLDictIterator_Begin(currentProps, &d);
FLValue currentValue;
while((currentValue = FLDictIterator_GetValue(&d))) {
FLString currentKey = FLDictIterator_GetKeyString(&d);
if(FLDict_Get(newProps, currentKey)) {
continue;
}
FLMutableDict_SetValue(newProps, currentKey, currentValue);
FLDictIterator_Next(&d);
}
return true;
}
static void test_save_with_conflict_handler() {
CBLDatabase* database = kDatabase;
// tag::update-document-with-conflict-handler[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLDocument* mutableDocument = CBLDatabase_GetMutableDocument(database, FLSTR("xyz"), &err);
FLMutableDict properties = CBLDocument_MutableProperties(mutableDocument);
FLMutableDict_SetString(properties, FLSTR("name"), FLSTR("apples"));
/*
static bool custom_conflict_handler(void* context, CBLDocument* documentBeingSaved,
const CBLDocument* conflictingDocument) {
FLDict currentProps = CBLDocument_Properties(conflictingDocument);
FLDict updatedProps = CBLDocument_Properties(documentBeingSaved);
FLMutableDict newProps = FLDict_MutableCopy(updatedProps, kFLDefaultCopy);
FLDictIterator d;
FLDictIterator_Begin(currentProps, &d);
FLSlice currentKey = FLDictIterator_GetKeyString(&d);
for(; currentKey.buf; currentKey = FLDictIterator_GetKeyString(&d)) {
if(FLDict_Get(newProps, currentKey)) {
continue;
}
FLValue currentValue = FLDictIterator_GetValue(&d);
FLMutableDict_SetValue(newProps, currentKey, currentValue);
}
return true;
}
*/
CBLDatabase_SaveDocumentWithConflictHandler(database, mutableDocument, custom_conflict_handler, NULL, &err);
// end::update-document-with-conflict-handler[]
}
static void use_encryption() {
#ifdef COUCHBASE_ENTERPRISE
// tag::database-encryption[]
// NOTE: No error handling, for brevity (see getting started)
CBLDatabaseConfiguration config = CBLDatabaseConfiguration_Default();
// This returns a boolean, so check it in production code
CBLEncryptionKey_FromPassword(&config.encryptionKey, FLSTR("password"));
CBLError err;
CBLDatabase* db = CBLDatabase_Open(FLSTR("seekrit"), &config, &err);
// Change the encryption key (or add encryption if the DB is unencrypted)
CBLEncryptionKey betterKey;
CBLEncryptionKey_FromPassword(&betterKey, FLSTR("betterpassw0rd"));
CBLDatabase_ChangeEncryptionKey(db, &betterKey, &err);
// Remove encryption
CBLDatabase_ChangeEncryptionKey(db, NULL, &err);
// end::database-encryption[]
#endif
}
static void reset_replicator_checkpoint() {
CBLEndpoint* target = CBLEndpoint_CreateWithURL(FLSTR("ws://localhost:4984/db"), NULL);
CBLReplicatorConfiguration config;
memset(&config, 0, sizeof(CBLReplicatorConfiguration));
config.database = kDatabase;
config.endpoint = target;
CBLReplicator* replicator = CBLReplicator_Create(&config, NULL);
CBLEndpoint_Free(target);
// tag::replication-reset-checkpoint-full[]
// replicator is a CBLReplicator* instance
CBLReplicator_Start(replicator, true); (1)
// end::replication-reset-checkpoint-full[]
stop_replicator(replicator);
}
static void read_1x_attachment() {
CBLDocument* document = CBLDocument_Create();
// tag::1x-attachment[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
FLDict properties = CBLDocument_Properties(document);
FLDict attachments = FLValue_AsDict(FLDict_Get(properties, FLSTR("_attachments")));
const CBLBlob* avatar = FLDict_GetBlob(FLValue_AsDict(FLDict_Get(attachments, FLSTR("avatar"))));
FLSliceResult content = CBLBlob_Content(avatar, &err);
FLSliceResult_Release(content);
// end::1x-attachment[]
CBLDocument_Release(document);
}
static void create_new_database() {
// tag::new-database[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLDatabase* db = CBLDatabase_Open(FLSTR("my-database"), NULL, &err);
// end::new-database[]
kDatabase = db;
}
static void close_database() {
CBLDatabase* db = kDatabase;
// tag::close-database[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLDatabase_Close(db, &err);
// end::close-database[]
}
static void change_logging() {
// tag::logging[]
// For output to stdout
CBLLog_SetConsoleLevel(kCBLLogVerbose);
// For output to custom logging
CBLLog_SetCallbackLevel(kCBLLogVerbose);
// end::logging[]
}
static void load_prebuilt() {
CBL_DeleteDatabase(FLSTR("travel-sample.cblite2"), kFLSliceNull, NULL);
// tag::prebuilt-database[]
// Note: Getting the path to a database is platform-specific. For desktop (including RPi)
// this can be a simple filesystem path. For iOS you need to get the path from the
// main bundle. For Android you need to extract it from your assets to a temporary directory
// and then pass that path.
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
const char* path = "/path/to/travel-sample.cblite2";
if(!CBL_DatabaseExists(FLSTR("travel-sample.cblite2"), kFLSliceNull)) {
CBL_CopyDatabase(FLStr(path), FLSTR("travel-sample"), NULL, &err);
}
// end::prebuilt-database[]
CBLDatabase_Close(kDatabase, NULL);
CBLDatabase_Release(kDatabase);
kDatabase = CBLDatabase_Open(FLSTR("travel-sample"), NULL, NULL);
}
static void query_deleted_document() {
CBLDatabase* db = kDatabase;
// tag::query-deleted-documents[]
// Query documents that have been deleted
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT meta().id FROM _ WHERE meta().deleted"), NULL, &err);
// end::query-deleted-documents[]
CBLQuery_Release(query);
}
static void create_document() {
CBLDatabase* db = kDatabase;
// tag::initializer[]
// NOTE: No error handling, for brevity (see getting started)
CBLDocument* newTask = CBLDocument_CreateWithID(FLSTR("xyz"));
FLMutableDict properties = CBLDocument_MutableProperties(newTask);
FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("task"));
FLMutableDict_SetString(properties, FLSTR("owner"), FLSTR("todo"));
// Storing time in millisecond, bluntly
FLMutableDict_SetUInt(properties, FLSTR("createdAt"), time(NULL) * 1000);
CBLError err;
CBLDatabase_SaveDocument(db, newTask, &err);
CBLDocument_Release(newTask);
// end::initializer[]
}
static void update_document() {
CBLDatabase* db = kDatabase;
// tag::update-document[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLDocument* mutableDocument = CBLDatabase_GetMutableDocument(db, FLSTR("xyz"), &err);
FLMutableDict properties = CBLDocument_MutableProperties(mutableDocument);
FLMutableDict_SetString(properties, FLSTR("name"), FLSTR("apples"));
CBLDatabase_SaveDocument(db, mutableDocument, &err);
CBLDocument_Release(mutableDocument);
// end::update-document[]
}
// Note use_typed_accessors not applicable
static void do_batch_operation() {
CBLDatabase* db = kDatabase;
// tag::batch[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLDatabase_BeginTransaction(db, &err);
char buffer[7];
for(int i = 0; i < 10; i++) {
CBLDocument* doc = CBLDocument_Create();
FLMutableDict properties = CBLDocument_MutableProperties(doc);
FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("user"));
sprintf(buffer, "user %d", i);
FLMutableDict_SetString(properties, FLSTR("name"), FLStr(buffer));
FLMutableDict_SetBool(properties, FLSTR("admin"), false);
CBLDatabase_SaveDocument(db, doc, &err);
CBLDocument_Release(doc);
printf("Saved user document %s\n", buffer);
}
CBLDatabase_EndTransaction(db, true, &err);
// end::batch[]
}
static void document_listener(void* context, const CBLDatabase* db, FLString id) {
CBLError err;
const CBLDocument* doc = CBLDatabase_GetDocument(db, id, &err);
FLDict properties = CBLDocument_Properties(doc);
FLString verified_account = FLValue_AsString(FLDict_Get(properties, FLSTR("verified_account")));
printf("Status :: %.*s\n", (int)verified_account.size, (const char *)verified_account.buf);
CBLDocument_Release(doc);
}
static void database_change_listener() {
CBLDatabase* db = kDatabase;
// tag::document-listener[]
/*
static void document_listener(void* context, const CBLDatabase* db, FLString id) {
CBLError err;
const CBLDocument* doc = CBLDatabase_GetDocument(db, id, &err);
FLDict properties = CBLDocument_Properties(doc);
FLString verified_account = FLValue_AsString(FLDict_Get(properties, FLSTR("verified_account")));
printf("Status :: %.*s\n", (int)verified_account.size, (const char *)verified_account.buf);
CBLDocument_Release(doc);
}
*/
CBLListenerToken* token = CBLDatabase_AddDocumentChangeListener(db, FLSTR("user.john"),
document_listener, NULL);
// end::document-listener[]
CBLListener_Remove(token);
}
static void document_expiration() {
CBLDatabase* db = kDatabase;
// tag::document-expiration[]
// Purge the document one day from now
// Overly simplistic for example purposes
// NOTE: API takes milliseconds
// NOTE: No error handling, for brevity (see getting started)
time_t ttl = time(NULL) + 24 * 60 * 60;
ttl *= 1000;
CBLError err;
CBLDatabase_SetDocumentExpiration(db, FLSTR("doc123"), ttl, &err);
// Reset expiration
CBLDatabase_SetDocumentExpiration(db, FLSTR("doc1"), 0, &err);
// Query documents that will be expired in less than five minutes
time_t fiveMinutesFromNow = time(NULL) + 5 * 60;
fiveMinutesFromNow *= 1000;
FLMutableDict parameters = FLMutableDict_New();
FLMutableDict_SetInt(parameters, FLSTR("five_minutes"), fiveMinutesFromNow);
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT meta().id FROM _ WHERE meta().expiration < $five_minutes"), NULL, &err);
CBLQuery_SetParameters(query, parameters);
FLMutableDict_Release(parameters);
// end::document-expiration[]
}
static void use_blob() {
CBLDatabase* db = kDatabase;
CBLDocument* newTask = CBLDocument_Create();
// tag::blob[]
// Note: Reading the data is implementation dependent, as with prebuilt databases
// NOTE: No error handling, for brevity (see getting started)
uint8_t buffer[128000];
FILE* avatar_file = fopen("avatar.jpg", "rb");
size_t read = fread(buffer, 1, 128000, avatar_file); (2)
FLSliceResult avatar = FLSliceResult_CreateWith(buffer, read);
CBLBlob* blob = CBLBlob_CreateWithData(FLSTR("image/jpeg"), FLSliceResult_AsSlice(avatar)); (3)
FLSliceResult_Release(avatar);
// TODO: Create shortcut blob method
CBLError err;
FLMutableDict properties = CBLDocument_MutableProperties(newTask);
FLSlot_SetBlob(FLMutableDict_Set(properties, FLSTR("avatar")), blob);
CBLDatabase_SaveDocument(db, newTask, &err); (4)
// end::blob[]
CBLDocument_Release(newTask);
CBLBlob_Release(blob);
}
static void doc_json() {
CBLDatabase* db = kDatabase;
// tag::tojson-document[]
FLString json = FLSTR("{\"id\":\"1002\",\"type\":\"hotel\",\"name\":\"Hotel Ned\",\"city\":\"Balmain\",\"country\":\"Australia\"}");
// Create a document and set the JSON data to the document
CBLError err;
CBLDocument* newDoc = CBLDocument_CreateWithID(FLSTR("hotel_1002"));
CBLDocument_SetJSON(newDoc, json, &err);
// Save the document to the database
CBLDatabase_SaveDocument(db, newDoc, &err);
// Release created doc after using it
CBLDocument_Release(newDoc);
// Get the document from the database
const CBLDocument* doc = CBLDatabase_GetDocument(db, FLSTR("hotel_1002"), &err);
// Get document body as JSON
FLSliceResult docJson = CBLDocument_CreateJSON(doc);
printf("Document in JSON :: %.*s\n", (int)docJson.size, (const char *)docJson.buf);
// Release JSON data after using it
FLSliceResult_Release(docJson);
// Release doc read from the database after using it
CBLDocument_Release(doc);
// end::tojson-document[]
}
static void dict_json() {
// tag::tojson-dictionary[]
FLString json = FLSTR("{\"id\":\"1002\",\"type\":\"hotel\",\"name\":\"Hotel Ned\",\"city\":\"Balmain\",\"country\":\"Australia\"}");
// Create a dictionary from the JSON string
FLError err;
FLSliceResult jsonData1 = FLData_ConvertJSON(json, &err);
FLDict hotel = FLValue_AsDict(FLValue_FromData(FLSliceResult_AsSlice(jsonData1), kFLTrusted));
// Iterate through the dictionary
FLDictIterator iter;
FLDictIterator_Begin(hotel, &iter);
FLValue value;
while (NULL != (value = FLDictIterator_GetValue(&iter))) {
FLString key = FLDictIterator_GetKeyString(&iter);
FLString strValue = FLValue_AsString(value);
printf("%.*s :: %.*s\n", (int)key.size, (const char*)key.buf, (int)strValue.size, (const char*)strValue.buf);
FLDictIterator_Next(&iter);
}
// Convert the dictionary to JSON
FLSliceResult jsonData2 = FLValue_ToJSON((FLValue)hotel);
printf("Hotel in JSON :: %.*s\n", (int)jsonData2.size, (const char *)jsonData2.buf);
// Release JSON data after finish using it
FLSliceResult_Release(jsonData1);
FLSliceResult_Release(jsonData2);
// end::tojson-dictionary[]
}
static void array_json() {
// tag::tojson-array[]
FLString json = FLSTR("[\"Hotel Ned\", \"Hotel Ted\"]");
// Create an array from the JSON string
FLError err;
FLSliceResult jsonData1 = FLData_ConvertJSON(json, &err);
FLArray hotels = FLValue_AsArray(FLValue_FromData(FLSliceResult_AsSlice(jsonData1), kFLTrusted));
// Iterate through the array
FLArrayIterator iter;
FLArrayIterator_Begin(hotels, &iter);
FLValue value;
while (NULL != (value = FLArrayIterator_GetValue(&iter))) {
FLString hotel = FLValue_AsString(value);
printf("Hotel :: %.*s\n", (int)hotel.size, (const char *)hotel.buf);
FLArrayIterator_Next(&iter);
}
// Convert the array to JSON
FLSliceResult jsonData2 = FLValue_ToJSON((FLValue)hotels);
printf("Hotels in JSON :: %.*s\n", (int)jsonData2.size, (const char *)jsonData2.buf);
// Release JSON data after finish using it
FLSliceResult_Release(jsonData1);
FLSliceResult_Release(jsonData2);
// end::tojson-array[]
}
static void datatype_dictionary()
{
CBLDatabase *db = kDatabase;
// tag::datatype_dictionary[]
CBLError err;
const CBLDocument *doc = CBLDatabase_GetDocument(db, FLSTR("doc1"), &err);
FLDict properties = CBLDocument_Properties(doc);
// Getting a dictionary from the document's properties
FLValue dictValue = FLDict_Get(properties, FLSTR("address"));
FLDict dict = FLValue_AsDict(dictValue);
// Access a value with a key from the dictionary
FLValue streetVal = FLDict_Get(dict, FLSTR("street"));
FLString street = FLValue_AsString(streetVal);
// Iterate dictionary
FLDictIterator iter;
FLDictIterator_Begin(dict, &iter);
FLValue value;
while (NULL != (value = FLDictIterator_GetValue(&iter))) {
FLString key = FLDictIterator_GetKeyString(&iter);
FLString strValue = FLValue_AsString(value);
printf("Key :: %.*s\n", (int)key.size, (const char *)key.buf);
printf("Value :: %.*s\n", (int)strValue.size, (const char *)strValue.buf);
// ...
FLDictIterator_Next(&iter);
}
// Create a mutable copy.
// kFLDefaultCopy is shallow which means the nested dictionaries and arrays will be
// referenced but not copied. Use kFLDeepCopyImmutables for the deep copy.
FLMutableDict mutableDict = FLDict_MutableCopy(dict, kFLDefaultCopy);
// Release when finish using it
FLMutableDict_Release(mutableDict);
// end::datatype_dictionary[]
}
static void datatype_mutable_dictionary()
{
CBLDatabase *db = kDatabase;
// tag::datatype_mutable_dictionary[]
// tag::datatype_mutable_dictionary-create[]
// Create a new mutable dictionary and populate some keys/values
FLMutableDict dict = FLMutableDict_New();
FLMutableDict_SetString(dict, FLSTR("street"), FLSTR("1 Main st."));
FLMutableDict_SetString(dict, FLSTR("city"), FLSTR("San Francisco"));
// end::datatype_mutable_dictionary-create[]
// tag::datatype_mutable_dictionary-add-to-doc[]
// Set the dictionary to document's properties and save the document
CBLDocument *doc = CBLDocument_Create();
FLMutableDict properties = CBLDocument_MutableProperties(doc);
FLMutableDict_SetDict(properties, FLSTR("address"), dict);
// end::datatype_mutable_dictionary-add-to-doc[]
CBLError err;
CBLDatabase_SaveDocument(db, doc, &err);
CBLDocument_Release(doc);
// Release when finish using it
FLMutableDict_Release(dict);
// end::datatype_mutable_dictionary[]
}
static void datatype_array()
{
CBLDatabase *db = kDatabase;
// tag::datatype_array[]
CBLError err;
const CBLDocument *doc = CBLDatabase_GetDocument(db, FLSTR("doc1"), &err);
FLDict properties = CBLDocument_Properties(doc);
// Getting a phones array from the document's properties
FLValue arrayValue = FLDict_Get(properties, FLSTR("phones"));
FLArray array = FLValue_AsArray(arrayValue);
// Get element count
int count = FLArray_Count(array);
printf("Count :: %d\n", count);
// Access an array element by index
if (!FLArray_IsEmpty(array)) {
FLValue phoneVal = FLArray_Get(array, 0);
FLString phone = FLValue_AsString(phoneVal);
printf("Value :: %.*s\n", (int)phone.size, (const char *)phone.buf);
}
// Iterate array
FLArrayIterator iter;
FLArrayIterator_Begin(array, &iter);
FLValue val;
while (NULL != (val = FLArrayIterator_GetValue(&iter)))
{
FLString str = FLValue_AsString(val);
printf("Value :: %.*s\n", (int)str.size, (const char *)str.buf);
FLArrayIterator_Next(&iter);
}
// end::datatype_array[]
// Create a mutable copy.
// kFLDefaultCopy is shallow which means the nested dictionaries and arrays will be
// referenced but not copied. Use kFLDeepCopyImmutables for the deep copy.
FLMutableArray mutableArray = FLArray_MutableCopy(array, kFLDefaultCopy);
// Release when finish using it
FLMutableArray_Release(mutableArray);
}
static void datatype_mutable_array()
{
CBLDatabase *db = kDatabase;
// tag::datatype_mutable_array[]
// tag::datatype_mutable_array-create[]
// Create a new mutable array and populate data into the array
FLMutableArray phones = FLMutableArray_New();
FLMutableArray_AppendString(phones, FLSTR("650-000-0000"));
FLMutableArray_AppendString(phones, FLSTR("650-000-0001"));
// end::datatype_mutable_array-create[]
// tag::datatype_mutable_array-add-to-doc[]
// Set the array to document's properties and save the document
CBLDocument *doc = CBLDocument_Create();
FLMutableDict properties = CBLDocument_MutableProperties(doc);
FLMutableDict_SetArray(properties, FLSTR("phones"), phones);
// end::datatype_mutable_array-add-to-doc[]
CBLError err;
CBLDatabase_SaveDocument(db, doc, &err);
CBLDocument_Release(doc);
// Release the created dictionary
FLMutableArray_Release(phones);
// end::datatype_mutable_array[]
}
static void datatype_usage() {
// tag::datatype_usage[]
// tag::datatype_usage_createdb[]
// Open or create DB if it doesn't exist
CBLError err;
CBLDatabase* database = CBLDatabase_Open(FLSTR("mydb"), NULL, &err);
if(!database) {
// Error handling. For brevity, this is truncated in the rest of the snippet
// and omitted in other doc code snippets
fprintf(stderr, "Error opening database (%d / %d)\n", err.domain, err.code);
FLSliceResult msg = CBLError_Message(&err);
fprintf(stderr, "%.*s\n", (int)msg.size, (const char *)msg.buf);
FLSliceResult_Release(msg);
return;
}
// end::datatype_usage_createdb[]
// tag::datatype_usage_createdoc[]
// Create your new document
// The lack of 'const' indicates this document is mutable
CBLDocument* mutableDoc = CBLDocument_Create();
FLMutableDict properties = CBLDocument_MutableProperties(mutableDoc);
// end::datatype_usage_createdoc[]
// tag::datatype_usage_mutdict[]
// Create and populate mutable dictionary
FLMutableDict address = FLMutableDict_New();
FLMutableDict_SetString(address, FLSTR("street"), FLSTR("1 Main st."));
FLMutableDict_SetString(address, FLSTR("city"), FLSTR("San Francisco"));
FLMutableDict_SetString(address, FLSTR("state"), FLSTR("CA"));
FLMutableDict_SetString(address, FLSTR("country"), FLSTR("USA"));
FLMutableDict_SetString(address, FLSTR("code"), FLSTR("90210"));
// end::datatype_usage_mutdict[]
// tag::datatype_usage_mutarray[]
// Create and populate mutable array
FLMutableArray phones = FLMutableArray_New();
FLMutableArray_AppendString(phones, FLSTR("650-000-0000"));
FLMutableArray_AppendString(phones, FLSTR("650-000-0001"));
// end::datatype_usage_mutarray[]
// tag::datatype_usage_populate[]
// Initialize and populate the document
// Add document type and hotel name as string
FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("hotel"));
FLMutableDict_SetString(properties, FLSTR("hotel"), FLSTR(""));
// Add average room rate (float)
FLMutableDict_SetFloat(properties, FLSTR("room_rate"), 121.75f);
// Add address (dictionary)
FLMutableDict_SetDict(properties, FLSTR("address"), dict);
// Add phone numbers(array)
FLMutableDict_SetArray(properties, FLSTR("phones"), phones);
// end::datatype_usage_populate[]
// tag::datatype_usage_persist[]
CBLError err;
CBLDatabase_SaveDocument(database, doc, &err);
// end::datatype_usage_persist[]
// tag::datatype_usage_closedb[]
CBLError err;
CBLDatabase_Close(database, &err);
// end::datatype_usage_closedb[]
// tag::datatype_usage_release[]
CBLDatabase_Release(database);
CBLDocument_Release(doc);
FLMutableDict_Release(dict);
FLMutableArray_Release(phones);
// end::datatype_usage_release[]
// end::datatype_usage[]
} // end datatype_usage()
static void create_index() {
CBLDatabase* db = kDatabase;
// tag::query-index[]
// For value types, this is optional but provides performance enhancements
// NOTE: No error handling, for brevity (see getting started)
// Syntax for second argument is the same as taking from a N1QL SELECT
// i.e. SELECT (type, name) FROM _;
CBLValueIndexConfiguration config = {
kCBLN1QLLanguage,
FLSTR("type, name")
};
CBLError err;
CBLDatabase_CreateValueIndex(db, FLSTR("TypeNameIndex"), config, &err);
// end::query-index[]
}
static void select_meta() {
CBLDatabase* db = kDatabase;
// tag::query-select-meta[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT meta().id, type, name FROM _"), NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLString id = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("id")));
FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name")));
printf("Document ID :: %.*s\n", (int)id.size, (const char *)id.buf);
printf("Document Name :: %.*s\n", (int)name.size, (const char *)name.buf);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::query-select-meta[]
}
static void select_id() {
CBLDatabase* db = kDatabase;
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT meta().id FROM _"), NULL, &err);
// tag::query-access-id[]
// NOTE: No error handling, for brevity (see getting started)
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLString id = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("id")));
printf("Document ID :: %.*s\n", (int)id.size, (const char *)id.buf);
}
// end::query-access-id[]
CBLResultSet_Release(results);
CBLQuery_Release(query);
}
static void query_change_listener(void* context, CBLQuery* query, CBLListenerToken* token) {
CBLError err;
CBLResultSet* results = CBLQuery_CopyCurrentResults(query, token, &err);
while(CBLResultSet_Next(results)) {
// Update UI
}
}
static void select_all() {
CBLDatabase* db = kDatabase;
// tag::query-select-all[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT * FROM _"), NULL, &err);
// All results will be available from the above query
CBLQuery_Release(query);
// end::query-select-all[]
// tag::live-query[]
/*
static void query_change_listener(void* context, CBLQuery* query, CBLListenerToken* token) {
CBLError err;
CBLResultSet* results = CBLQuery_CopyCurrentResults(query, token, &err);
while(CBLResultSet_Next(results)) {
// Update UI
}
}
*/
query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT * FROM _"), NULL, &err); (5)
CBLListenerToken* token = CBLQuery_AddChangeListener(query, query_change_listener, NULL); (6)
// end::live-query[]
// tag::stop-live-query[]
CBLListener_Remove(token); // The token received from AddChangeListener
CBLQuery_Release(query);
// end::stop-live-query[]
}
static void select_and_access_all() {
CBLDatabase* db = kDatabase;
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT * FROM _"), NULL, &err);
// tag::query-access-all[]
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLDict dict = FLValue_AsDict(CBLResultSet_ValueForKey(results, FLSTR("_")));
FLString id = FLValue_AsString(FLDict_Get(dict, FLSTR("id")));
FLString type = FLValue_AsString(FLDict_Get(dict, FLSTR("type")));
FLString name = FLValue_AsString(FLDict_Get(dict, FLSTR("name")));
FLString city = FLValue_AsString(FLDict_Get(dict, FLSTR("city")));
printf("ID :: %.*s\n", (int)id.size, (const char *)id.buf);
printf("Type :: %.*s\n", (int)type.size, (const char *)type.buf);
printf("Name :: %.*s\n", (int)name.size, (const char *)name.buf);
printf("City :: %.*s\n", (int)city.size, (const char *)city.buf);
}
// All results will be available from the above query
CBLResultSet_Release(results);
// end::query-access-all[]
CBLQuery_Release(query);
}
static void select_props() {
CBLDatabase* db = kDatabase;
// tag::query-access-props[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT type, name, city FROM _"), NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLString type = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("type")));
FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name")));
FLString city = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("city")));
printf("Type :: %.*s\n", (int)type.size, (const char *)type.buf);
printf("Name :: %.*s\n", (int)name.size, (const char *)name.buf);
printf("City :: %.*s\n", (int)city.size, (const char *)city.buf);
}
// end::query-access-props[]
// All results will be available from the above query
CBLResultSet_Release(results);
CBLQuery_Release(query);
}
static void select_where() {
CBLDatabase* db = kDatabase;
// tag::query-where[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT * FROM _ WHERE type = \"hotel\" LIMIT 10"), NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLDict dict = FLValue_AsDict(CBLResultSet_ValueForKey(results, FLSTR("_")));
FLString name = FLValue_AsString(FLDict_Get(dict, FLSTR("name")));
printf("Document Name :: %.*s\n", (int)name.size, (const char *)name.buf);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::query-where[]
}
static void use_collection_contains() {
CBLDatabase* db = kDatabase;
// tag::query-collection-operator-contains[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT meta().id, name, public_likes FROM _ WHERE type = \"hotel\" "
"AND ARRAY_CONTAINS(public_likes, \"Armani Langworth\")"), NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLArray publicLikes = FLValue_AsArray(CBLResultSet_ValueForKey(results, FLSTR("public_likes")));
FLStringResult json = FLValue_ToJSON((FLValue)publicLikes);
printf("Public Likes :: %.*s\n", (int)json.size, (const char *)json.buf);
FLSliceResult_Release(json);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::query-collection-operator-contains[]
}
static void use_collection_in() {
CBLDatabase* db = kDatabase;
// tag::query-collection-operator-in[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT * FROM _ WHERE \"Armani\" IN (first, last, username)"),
NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLDict body = FLValue_AsDict(CBLResultSet_ValueAtIndex(results, 0));
FLStringResult json = FLValue_ToJSON((FLValue)body);
printf("In results :: %.*s\n", (int)json.size, (const char *)json.buf);
FLSliceResult_Release(json);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::query-collection-operator-in[]
}
static void select_like() {
CBLDatabase* db = kDatabase;
// tag::query-like-operator[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" "
"AND lower(name) LIKE \"Royal Engineers Museum\" LIMIT 10"),
NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name")));
printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::query-like-operator[]
}
static void select_wildcard_like() {
CBLDatabase* db = kDatabase;
// tag::query-like-operator-wildcard-match[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" "
"AND lower(name) LIKE \"Eng%e%\" LIMIT 10"),
NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name")));
printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::query-like-operator-wildcard-match[]
}
static void select_wildcard_character_like() {
CBLDatabase* db = kDatabase;
// tag::query-like-operator-wildcard-character-match[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" "
"AND lower(name) LIKE \"Royal Eng____rs Museum\" LIMIT 10"),
NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name")));
printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::query-like-operator-wildcard-character-match[]
}
static void select_regex() {
CBLDatabase* db = kDatabase;
// tag::query-regex-operator[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" "
"AND regexp_like(name, \"\\bEng.*e\\b\") LIMIT 10"),
NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name")));
printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::query-regex-operator[]
}
static void select_join() {
CBLDatabase* db = kDatabase;
// tag::query-join[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT airline.name, airline.callsign, route.destinationairport, route.stops, route.airline "
"FROM _ AS airline INNER JOIN _ AS route ON meta(airline).id = route.airlineid "
"WHERE route.type = \"route\" AND airline.type = \"airline\" AND route.sourceairport = \"RIX\""),
NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name")));
printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::query-join[]
}
static void group_by() {
CBLDatabase* db = kDatabase;
// tag::query-groupby[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT count(*), country, tz FROM _ WHERE type = \"airport\" AND geo.alt >= 300 "
"GROUP BY country, tz"),
NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
int64_t count = FLValue_AsInt(CBLResultSet_ValueForKey(results, FLSTR("$1")));
FLString tz = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("tz")));
FLString country = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("country")));
printf("There are %" PRIi64 " airports in the %.*s timezone located in %.*s and above 300 ft\n",
count, (int)tz.size, (const char *)tz.buf, (int)country.size, (const char *)country.buf);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::query-groupby[]
}
static void order_by() {
CBLDatabase* db = kDatabase;
// tag::query-orderby[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT meta().id, title FROM _ WHERE type = \"hotel\" ORDER BY title ASC LIMIT 10"),
NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLString title = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("title")));
printf("Title :: %.*s\n", (int)title.size, (const char *)title.buf);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::query-orderby[]
}
static void test_explain_statement() {
CBLDatabase* db = kDatabase;
{
// tag::query-explain-all[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT * FROM _ WHERE type = \"hotel\" GROUP BY country ORDER BY title ASC LIMIT 10"),
NULL, &err);
FLSliceResult explanation = CBLQuery_Explain(query);
printf("%.*s", (int)explanation.size, (const char *)explanation.buf);
FLSliceResult_Release(explanation);
// end::query-explain-all[]
}
// DOCS NOTE: Others omitted for now
}
static void query_result_json() {
CBLDatabase* db = kDatabase;
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT meta().id as id, name, city, type FROM _ LIMIT 10"),
NULL, &err);
// tag::query-access-json[]
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLDict result = CBLResultSet_ResultDict(results);
FLStringResult json = FLValue_ToJSON((FLValue)result);
printf("JSON Result :: %.*s\n", (int)json.size, (const char *)json.buf);
FLSliceResult_Release(json);
}
CBLResultSet_Release(results);
// end::query-access-json[]
CBLQuery_Release(query);
}
static void create_full_text_index() {
CBLDatabase* db = kDatabase;
const char* tasks[] = { "buy groceries", "play chess", "book travels", "buy museum tickets" };
char idBuffer[7];
for(int i = 0; i < 4; i++) {
const char* task = tasks[i];
sprintf(idBuffer, "extra%d", i);
const CBLDocument* doc = CBLDatabase_GetDocument(db, FLStr(idBuffer), NULL);
if(doc) {
CBLDocument_Release(doc);
continue;
}
CBLDocument* mutableDoc = CBLDocument_CreateWithID(FLStr(idBuffer));
FLMutableDict properties = CBLDocument_MutableProperties(mutableDoc);
FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("task"));
FLMutableDict_SetString(properties, FLSTR("task"), FLStr(task));
CBLDatabase_SaveDocument(db, mutableDoc, NULL);
CBLDocument_Release(mutableDoc);
}
// tag::fts-index[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLFullTextIndexConfiguration config = {
kCBLN1QLLanguage,
FLSTR("name"),
false
};
CBLDatabase_CreateFullTextIndex(db, FLSTR("nameFTSIndex"), config, &err);
// end::fts-index[]
}
static void full_text_search() {
CBLDatabase* db = kDatabase;
// tag::fts-query[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT meta().id FROM _ WHERE MATCH(nameFTSIndex, \"'buy'\")"),
NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
FLString id = FLValue_AsString(CBLResultSet_ValueAtIndex(results, 0));
printf("Document id :: %.*s\n", (int)id.size, (const char *)id.buf);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::fts-query[]
}
static void start_replication() {
CBLDatabase* db = kDatabase;
/*
* This requires Sync Gateway running with the following config, or equivalent:
*
* {
* "log":["*"],
* "databases": {
* "db": {
* "server":"walrus:",
* "users": {
* "GUEST": {"disabled": false, "admin_channels": ["*"] }
* }
* }
* }
* }
*/
// tag::replication[]
// NOTE: No error handling, for brevity (see getting started)
// Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion)
CBLError err;
FLString url = FLSTR("ws://localhost:4984/db");
CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err);
CBLReplicatorConfiguration config;
memset(&config, 0, sizeof(CBLReplicatorConfiguration));
config.database = db;
config.endpoint = target;
config.replicatorType = kCBLReplicatorTypePull;
CBLReplicator* replicator = CBLReplicator_Create(&config, &err);
CBLEndpoint_Free(target);
CBLReplicator_Start(replicator, false);
// end::replication[]
stop_replicator(replicator);
}
// Console logging domain methods are not applicable to C
static void file_logging() {
// tag::file-logging[]
// NOTE: No error handling, for brevity (see getting started)
// NOTE: You will need to use a platform appropriate method for finding
// a temporary directory
FLString tempFolder = FLSTR("/tmp/cbllog");
CBLLogFileConfiguration config; // Don't bother zeroing, since we set all properties
config.level = kCBLLogInfo;
config.directory = tempFolder;
config.maxRotateCount = 5;
config.maxSize = 10240;
config.usePlaintext = false;
CBLError err;
CBLLog_SetFileConfig(config, &err);
// end::file-logging[]
}
// tag::custom-logging[]
static void custom_log_callback(CBLLogDomain domain, CBLLogLevel level, FLString message) {
// handle the message, for example piping it to
// a third party framework
}
// end::custom-logging[]
static void enable_custom_logging() {
// tag::set-custom-logging[]
CBLLog_SetCallback(custom_log_callback);
// end::set-custom-logging[]
}
static void enable_basic_auth() {
CBLDatabase* db = kDatabase;
// tag::basic-authentication-full[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
FLString url = FLSTR("ws://localhost:4984/mydatabase");
CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err);
CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("john"), FLSTR("pass"));
CBLReplicatorConfiguration config;
memset(&config, 0, sizeof(CBLReplicatorConfiguration));
config.database = db;
config.endpoint = target;
config.authenticator = basicAuth;
CBLReplicator* replicator = CBLReplicator_Create(&config, &err);
CBLEndpoint_Free(target);
CBLAuth_Free(basicAuth);
CBLReplicator_Start(replicator, false);
// end::basic-authentication-full[]
stop_replicator(replicator);
}
static void docsonly_N1QL_Params(CBLDatabase* argDb)
{
CBLDatabase* database = argDb;
// tag::query-syntax-n1ql-params[]
int errorPos;
CBLError err;
FLString n1qlstr = FLSTR("SELECT * FROM _ WHERE type = $type");
FLMutableDict n1qlparams = FLMutableDict_New();
FLMutableDict_SetString(n1qlparams, FLSTR("type"), FLSTR("hotel"));
CBLQuery* query = CBLDatabase_CreateQuery(database,
kCBLN1QLLanguage,
n1qlstr,
&errorPos,
&err);
CBLQuery_SetParameters(query, n1qlparams);
if(!query) {
/* Do appropriate error handling ...
Note that (where applicable) errorPos contains the position
in the N1QL string that the parse failed
*/
FLMutableDict_Release(n1qlparams);
CBLQuery_Release(query);
return;
}
CBLResultSet* result = CBLQuery_Execute(query, &err);
if(!result) {
// Failed to run query, do error handling ...
return;
}
// Release query when finished with
FLMutableDict_Release(n1qlparams);
CBLQuery_Release(query);
// ... process results as required
// Release result set then finished with
CBLResultSet_Release(result);
// end::query-syntax-n1ql-params[]
}
// tag::console-logging-db[]
//Placeholder for code to increase level of console logging for kCBLLogDomainDatabase domain
// end::console-logging-db[]
// tag::console-logging[]
//Placeholder for code to increase level of console logging for all domains
// end::console-logging[]
// tag::date-getter[]
//Placeholder for Date accessors.
// end::date-getter[]
// tag::query-index[]
// placeholder
// end::query-index[]
// DOCS NOTE
// Page=Data Sync >> Configuration Summary
// URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#configuration-summary
static void docs_act_replication(CBLDatabase* argDb)
{
CBLDatabase* database = argDb;
/*
* This requires Sync Gateway running with the following config, or equivalent:
*
* {
* "log":["*"],
* "databases": {
* "db": {
* "server":"walrus:",
* "users": {
* "GUEST": {"disabled": false, "admin_channels": ["*"] }
* }
* }
* }
* }
*/
// tag::p2p-act-rep-func[]
// Purpose -- Show configuration , initialization and running of a replicator
// NOTE: No error handling, for brevity (see getting started)
// Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion)
CBLError err;
FLString url = FLSTR("ws://localhost:4984/db");
CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); (7)
CBLReplicatorConfiguration config;
memset(&config, 0, sizeof(CBLReplicatorConfiguration));
config.database = database;
config.endpoint = target; (8)
// tag::p2p-act-rep-config-cont[]
// Set replication direction and mode
config.replicatorType = kCBLReplicatorTypePull; (9)
config.continuous = true;
// end::p2p-act-rep-config-cont[]
// Optionally, set auto-purge behavior (here we override default)
config.disableAutoPurge = true; (10)
// Optionally, configure Client Authentication
// Here we are using to Basic Authentication,
// Providing username and password credentials
CBLAuthenticator* basicAuth =
CBLAuth_CreatePassword(FLSTR("username"),
FLSTR("passwd")); (11)
config.authenticator = basicAuth;
// Optionally, configure how we handle conflicts
config.conflictResolver = simpleConflictResolver_localWins; (12)
// Initialize replicator with created config
CBLReplicator* replicator =
CBLReplicator_Create(&config, &err); (13)
CBLEndpoint_Free(target);
// Optionally, add change listener
CBLListenerToken* token =
CBLReplicator_AddChangeListener(replicator,
simpleChangeListener,
NULL); (14)
// Start replication
CBLReplicator_Start(replicator, false); (15)
// end::p2p-act-rep-func[]
// ... other processing as required
// When finished release resources e.g.
CBLAuth_Free(basicAuth);
stop_replicator(replicator);
}
// END configuration summary snippets
// DOCS NOTE:
// Page=Data Sync >> Configuration
// URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#configuration-summary
// This function is not pulled into docs en-bloc
// it is a slightly more in-depth than the configurationsummary above
// and the snippets within it are used individually or in sets
// to illustrate specific points as required
//
static void docs_act_replication_config_section_snippets()
{
CBLDatabase* db = kDatabase;
bool docs_example_ShowBasicAuth = false;
bool docs_example_ShowSessionAuth = false;
/*
* This requires Sync Gateway running with the following config, or equivalent:
*
* {
* "log":["*"],
* "databases": {
* "db": {
* "server":"walrus:",
* "users": {
* "GUEST": {"disabled": false, "admin_channels": ["*"] }
* }
* }
* }
* }
*/
// tag::p2p-act-rep-func-full[]
// NOTE: No error handling, for brevity (see getting started)
// Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion)
// tag::sgw-act-rep-initialize[]
// Initialize the configuration object and set db target
CBLError err;
FLString url = FLSTR("ws://localhost:4984/db");
CBLEndpoint* target =
CBLEndpoint_CreateWithURL(url, &err); (16)
CBLReplicatorConfiguration config;
memset(&config, 0, sizeof(CBLReplicatorConfiguration));
config.database = db;
config.endpoint = target; (17)
// end::sgw-act-rep-initialize[]
// tag::p2p-act-rep-config-type[]
config.replicatorType = kCBLReplicatorTypePull;
// end::p2p-act-rep-config-type[]
// tag::p2p-act-rep-config-cont[]
config.continuous = true;
// end::p2p-act-rep-config-cont[]
// tag::replication-retry-config[]
// Configure replication retries
// tag::replication-set-heartbeat[]
config.heartbeat = 120; // (18)
// end::replication-set-heartbeat[]
// tag::replication-set-maxattempts[]
config.maxAttempts = 20; // (19)
// end::replication-set-maxattempts[]
// tag::replication-set-maxattemptwaittime[]
config.maxAttemptWaitTime = 600; // (20)
// end::replication-set-maxattemptwaittime[]
// end::replication-retry-config[]
// tag::basic-authentication[]
// Configure Client Authentication to Basic Authentication
// Providing username and password credentials
if(docs_example_ShowBasicAuth) {
CBLAuthenticator* basicAuth =
CBLAuth_CreatePassword(FLSTR("username"),
FLSTR("passwd"));
config.authenticator = basicAuth; (21)
}
// end::basic-authentication[]
// tag::session-authentication[]
if(docs_example_ShowSessionAuth) {
CBLAuthenticator* sessionAuth =
CBLAuth_CreateSession(FLSTR("904ac010862f37c8dd99015a33ab5a3565fd8447"),
FLSTR("optionalCookieName"));
config.authenticator = sessionAuth; (22)
}
// end::session-authentication[]
// tag::replication-custom-header[]
// Optionally, add custom headers
FLMutableDict customHdrs = FLMutableDict_New();
FLMutableDict_SetString(customHdrs,
FLSTR("customHeaderName"),
FLSTR("customHeaderValue"));
config.headers = customHdrs;
// tag::certificate-pinning[]
char cert_buf[10000];
FILE* cert_file = fopen("cert.pem", "r");
size_t read = fread(cert_buf, 1, sizeof(cert_buf), cert_file);
config.pinnedServerCertificate = (FLSlice){cert_buf, read};
// end::certificate-pinning[]
// end::replication-custom-header[]
// FILTERS
// tag::replication-push-filter[]
// tag::replication-pull-filter[]
// Purpose - Illustrate use of push and-or pull filter functions
config.pushFilter = simpleReplicationFilter;
config.pullFilter = simpleReplicationFilter;
// end::replication-pull-filter[]
// end::replication-push-filter[]
// Auto-purge over-ride
// tag::autopurge-override[]
config.disableAutoPurge = true; (23)
// end::autopurge-override[]
// Initialize replicator with created config
CBLReplicator* replicator =
CBLReplicator_Create(&config, &err); (24)
CBLEndpoint_Free(target);
// Add optional change listener
CBLListenerToken* token_ReplChangeListener =
CBLReplicator_AddChangeListener(replicator,
simpleChangeListener,
NULL); (25)
}
// END replication.html >> configure section
// PAGE=Data Sync >> Initialize section
// URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#lbl-init-repl
static CBLReplicator* docs_act_replication_Intialize(
void* context,
CBLReplicatorConfiguration argConfig,
bool argResetRequired)
{
CBLError err;
bool docs_example_resetRequired = argResetRequired;
// tag::p2p-act-rep-start-full[]
CBLReplicator* thisRepl =
CBLReplicator_Create(&argConfig, &err); (26)
// end::p2p-act-rep-start-full[]
if(!docs_example_resetRequired) {
// tag::p2p-act-rep-start-full[]
CBLReplicator_Start(thisRepl, false); (27)
// end::p2p-act-rep-start-full[]
} else {
// tag::replication-reset-checkpoint[]
CBLReplicator_Start(thisRepl, true); (28)
// end::replication-reset-checkpoint[]
}
return thisRepl;
}
// END replication.html >> initialize section
// PAGE=Data Sync >> Monitor section
// URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#lbl-repl-mon
static void docs_act_replication_Monitor(
void* context,
CBLReplicator* argRepl) {
CBLError err;
CBLReplicator* thisRepl = argRepl;
// tag::p2p-act-rep-add-change-listener[]
// Purpose -- illustrate addition of a Replicator change listener
CBLListenerToken* token_ReplChangeListener =
CBLReplicator_AddChangeListener(thisRepl,
simpleChangeListener,
NULL);
// end::p2p-act-rep-add-change-listener[]
// tag::add-document-replication-listener[]
// Purpose -- illustrate addition of a Document Replicator listener
CBLListenerToken* token_ReplDocListener =
CBLReplicator_AddDocumentReplicationListener(
thisRepl,
SimpleReplicationDocumentListener,
context);
// end::add-document-replication-listener[]
// tag::remove-document-replication-listener[]
// Purpose -- illustrate removal of a listener
CBLListener_Remove(token_ReplDocListener);
CBLListener_Remove(token_ReplChangeListener);
// end::remove-document-replication-listener[]
// tag::p2p-act-rep-status[]
// Purpose -- illustrate use of CBLReplicator_Status()
CBLReplicatorStatus thisState = CBLReplicator_Status(thisRepl);
if(thisState.activity==kCBLReplicatorStopped) {
if(thisState.error.code==0) {
CBLReplicator_Start(thisRepl,false);
} else {
printf("Replicator stopped -- code %d", thisState.error.code);
// ... handle error ...
CBLReplicator_Release(thisRepl);
}
}
// end::p2p-act-rep-status[]
// tag::replication-pendingdocuments[]
FLDict thisPendingIdList =
CBLReplicator_PendingDocumentIDs(thisRepl, &err); (29)
if(!FLDict_IsEmpty(thisPendingIdList)) {
FLDictIterator item;
FLDictIterator_Begin(thisPendingIdList, &item);
FLValue itemValue;
FLString pendingId;
while(NULL != (itemValue = FLDictIterator_GetValue(&item))) {
pendingId = FLValue_AsString(itemValue);
if(CBLReplicator_IsDocumentPending(thisRepl,
pendingId,
&err)) {
// ... process the still pending docid as required (30)
} else {
// Doc Id no longer pending
if(err.code==0) {
// No fail so must have already been pushed
printf("Document already pushed");
} else {
// Error detected so handle it
printf("Error code %d checking for pendingId", err.code);
break;
}
}
FLDictIterator_Next(&item);
}
FLDictIterator_End(&item);
FLValue_Release(itemValue);
} else {
printf("No Pending Id Docs to process");
}
FLDict_Release(thisPendingIdList);
// end::replication-pendingdocuments[]
}
// END replication.html >> Monitor section
// BEGIN replication.html >> Stop section
// PAGE=Data Sync >> Stop
// URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#lbl-repl-stop
static void docs_act_replication_Stop(
void* context,
CBLReplicator* argRepl) {
// tag::p2p-act-rep-stop[]
// Purpose -- show how to stop a replication
if(CBLReplicator_Status(argRepl).activity!=kCBLReplicatorStopped) {
CBLReplicator_Stop(argRepl);
}
// end::p2p-act-rep-stop[]
}
// END replication.html >> Stop section
static void replication_error_handling() {
CBLDatabase* db = kDatabase;
CBLError err;
FLString url = FLSTR("ws://localhost:4984/db");
CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err);
CBLReplicatorConfiguration config;
memset(&config, 0, sizeof(CBLReplicatorConfiguration));
config.database = db;
config.endpoint = target;
CBLReplicator* replicator = CBLReplicator_Create(&config, &err);
CBLEndpoint_Free(target);
stop_replicator(replicator);
}
static void create_encryptable() {
#ifdef COUCHBASE_ENTERPRISE
// tag::encryptable[]
// NOTE: No error handling, for brevity (see getting started)
// Create with premitive type
CBLEncryptable* encNull = CBLEncryptable_CreateWithNull();
CBLEncryptable* encBool = CBLEncryptable_CreateWithBool(true);
CBLEncryptable* encInt = CBLEncryptable_CreateWithInt(256);
CBLEncryptable* encUInt = CBLEncryptable_CreateWithUInt(1024);
CBLEncryptable* encFloat = CBLEncryptable_CreateWithFloat(1.2);
CBLEncryptable* encDouble = CBLEncryptable_CreateWithDouble(100.50);
CBLEncryptable* encString = CBLEncryptable_CreateWithString(FLSTR("foo"));
// Create with dictionary
FLMutableDict dict = FLMutableDict_New();
FLSlot_SetString(FLMutableDict_Set(dict, FLSTR("greeting")), FLSTR("hello"));
CBLEncryptable* encDict = CBLEncryptable_CreateWithDict(dict);
// Create with array
FLMutableArray array = FLMutableArray_New();
FLSlot_SetString(FLMutableArray_Append(array), FLSTR("item1"));
CBLEncryptable* encArray = CBLEncryptable_CreateWithArray(array);
// Create with FLValue
FLMutableDict dict2 = FLMutableDict_New();
FLSlot_SetString(FLMutableDict_Set(dict2, FLSTR("greeting")), FLSTR("hello"));
CBLEncryptable* encValue = CBLEncryptable_CreateWithValue((FLValue)dict2);
// end::encryptable[]
// Release after using it
CBLEncryptable_Release(encNull);
CBLEncryptable_Release(encBool);
CBLEncryptable_Release(encInt);
CBLEncryptable_Release(encBool);
CBLEncryptable_Release(encUInt);
CBLEncryptable_Release(encFloat);
CBLEncryptable_Release(encDouble);
CBLEncryptable_Release(encString);
CBLEncryptable_Release(encDict);
CBLEncryptable_Release(encArray);
CBLEncryptable_Release(encValue);
FLMutableDict_Release(dict);
FLMutableDict_Release(dict2);
FLMutableArray_Release(array);
#endif
}
static void release_encryptable() {
#ifdef COUCHBASE_ENTERPRISE
CBLEncryptable* encValue = CBLEncryptable_CreateWithString(FLSTR("foo"));
// tag::release_encryptable[]
// Release the encryptable value after finish using it
CBLEncryptable_Release(encValue);
// end::release_encryptable[]
#endif
}
static void use_encryptable() {
#ifdef COUCHBASE_ENTERPRISE
CBLDatabase* db = kDatabase;
// tag::use_encryptable[]
// NOTE: No error handling, for brevity (see getting started)
CBLDocument* doc = CBLDocument_CreateWithID(FLSTR("doc1"));
// Set encryptable:
FLMutableDict props = CBLDocument_MutableProperties(doc);
CBLEncryptable* encryptable = CBLEncryptable_CreateWithString(FLSTR("My secret"));
FLSlot_SetEncryptableValue(FLMutableDict_Set(props, FLSTR("secret")), encryptable);
CBLError error;
CBLDatabase_SaveDocument(db, doc, &error);
// Release
CBLDocument_Release(doc);
CBLEncryptable_Release(encryptable);
// end::release_encryptable[]
#endif
}
static void query_encryptable() {
#ifdef COUCHBASE_ENTERPRISE
CBLDatabase* db = kDatabase;
// tag::use_encryptable[]
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage,
FLSTR("SELECT secret, secret.value as secretValue FROM _ WHERE type = \"profile\""), NULL, &err);
CBLResultSet* results = CBLQuery_Execute(query, &err);
while(CBLResultSet_Next(results)) {
// Get secret as CBLEncryptable value
FLValue value = CBLResultSet_ValueForKey(results, FLSTR("secret"));
const CBLEncryptable* encValue = FLValue_GetEncryptableValue(value);
FLString secretStr = FLValue_AsString(CBLEncryptable_Value(encValue));
printf("Secret :: %.*s\n", (int)secretStr.size, (const char *)secretStr.buf);
// Get secret value directly
value = CBLResultSet_ValueForKey(results, FLSTR("secretValue"));
secretStr = FLValue_AsString(value);
printf("Secret :: %.*s\n", (int)secretStr.size, (const char *)secretStr.buf);
}
CBLResultSet_Release(results);
CBLQuery_Release(query);
// end::release_encryptable[]
#endif
}
#ifdef COUCHBASE_ENTERPRISE
// tag::replicator_property_encryption[]
// tag::replicator_property_encryptor_decryptor_sample[]
// Purpose: Declare property-level encryptor callback functions
static FLSliceResult my_cipher_function(FLSlice input) {
FLSliceResult result = FLSliceResult_New(input.size);
for(int i = 0; i < input.size; ++i) {
((uint8_t*)(result.buf))[i] = ((uint8_t*)input.buf)[i] ^ 'K';}
return result;
}
static FLSliceResult property_encryptor(void* context, FLString docID, FLDict props, FLString path,
FLSlice input, FLStringResult* algorithm, FLStringResult* keyID, CBLError* error) {
*algorithm = FLSlice_Copy(FLSTR("MyEnc"));
return my_cipher_function(input);
}
static FLSliceResult property_decryptor(void* context, FLString documentID, FLDict properties, FLString keyPath,
FLSlice input, FLString algorithm, FLString keyID, CBLError* error) {
return my_cipher_function(input);
}
// end::replicator_property_encryptor_decryptor_sample[]
// end::replicator_property_encryption[]
#endif
// PAGE=Field Level Encryption
// URL=https://docs.couchbase.com/couchbase-lite/current/c/field-level-encryption.html
//
static void replicator_property_encryption() {
#ifdef COUCHBASE_ENTERPRISE
CBLDatabase* db = kDatabase;
// tag::replicator_property_encryption[]
// Purpose: Show how to declare en(de)cryptors in replicator config
// NOTE: No error handling, for brevity (see getting started)
CBLError err;
FLString url = FLSTR("ws://localhost:4984/db");
CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err);
CBLReplicatorConfiguration config;
memset(&config, 0, sizeof(CBLReplicatorConfiguration));
config.database = db;
config.endpoint = target;
config.propertyEncryptor = property_encryptor; (31)
config.propertyDecryptor = property_decryptor; (32)
CBLReplicator* replicator = CBLReplicator_Create(&config, &err);
CBLEndpoint_Free(target);
CBLReplicator_Start(replicator, false);
// end::replicator_property_encryption[]
stop_replicator(replicator);
#endif
}
int main(int argc, char** argv) {
create_new_database();
create_document();
update_document();
do_batch_operation();
// Disable use_blob() as no avatar.jpg to load and crash
// use_blob();
doc_json();
dict_json();
array_json();
load_prebuilt();
create_index();
select_all();
select_and_access_all();
select_props();
select_meta();
select_id();
select_where();
use_collection_contains();
select_like();
select_wildcard_like();
select_wildcard_character_like();
select_regex();
select_join();
group_by();
order_by();
query_result_json();
create_full_text_index();
full_text_search();
start_replication();
replicator_property_encryption();
CBLDatabase_Close(kDatabase, NULL);
return 0;
}
-
Use keychain identity in config
// CBL Version 3.0.0 BETA #include <cbl/CouchbaseLite.h> #include <time.h> #include <inttypes.h> #ifdef _MSC_VER #include <direct.h> #include <Shlwapi.h> void usleep(unsigned int us) { Sleep(us / 1000); } #else #include <unistd.h> #endif static CBLDatabase* kDatabase; // Helper for stop replicator in the code snippet static void stop_replicator(CBLReplicator* replicator) { CBLReplicator_Stop(replicator); while(CBLReplicator_Status(replicator).activity != kCBLReplicatorStopped) { printf("Waiting for replicator to stop..."); usleep(200000); } CBLReplicator_Release(replicator); } // BEGIN lower-level function declarations // DOCS NOTE -- // These functions are referred to in subsequent code samples. // Their tags will ensure they are shown alongide the usage examples. // Functions used in more than one place may hve multiple tags. // tag::p2p-act-rep-func[] // tag::p2p-act-rep-add-change-listener[] // tag::replication-error-handling[] // Purpose -- illustrate a simple change listener static void simpleChangeListener(void* context, CBLReplicator* repl, const CBLReplicatorStatus* status) { if(status->error.code != 0) { printf("Error %d / %d\n", status->error.domain, status->error.code); } } // end::replication-error-handling[] // end::p2p-act-rep-add-change-listener[] // end::p2p-act-rep-func[] static const CBLDocument* simpleConflictResolver_localWins( void* context, FLString documentID, const CBLDocument* localDocument, const CBLDocument* remoteDocument) { return localDocument; } // tag::replication-push-filter[] // tag::replication-pull-filter[] // Purpose -- illustrate a simple replication filter function static bool simpleReplicationFilter(void* context, CBLDocument* argDoc, CBLDocumentFlags argFlags) { bool result = (argFlags == kCBLDocumentFlagsDeleted); return result; } // end::replication-push-filter[] // end::replication-pull-filter[] // tag::SimpleReplicationDocumentListener[] // Purpose -- Illustrate a simple replication document listener static void SimpleReplicationDocumentListener( void *context, CBLReplicator *replicator, bool isPush, unsigned numDocuments, const CBLReplicatedDocument *documents) { if(isPush) { printf("We pushed %d documents",numDocuments); } } // END lower-level function declarations static void getting_started_change_listener(void* context, CBLReplicator* repl, const CBLReplicatorStatus* status) { if(status->error.code != 0) { printf("Error %d / %d\n", status->error.domain, status->error.code); } } // Page=build and run // url=https://docs-staging.couchbase.com/couchbase-lite/current/c/gs-build.html static void getting_started() { // tag::getting-started[] // Purpose-- provide an overview of available crud and sync functionality // // Get the database (and create it if it doesn't exist) CBLError err; CBLDatabase* database = CBLDatabase_Open(FLSTR("mydb"), NULL, &err); if(!database) { // Error handling. For brevity, this is truncated in the rest of the snippet // and omitted in other doc code snippets fprintf(stderr, "Error opening database (%d / %d)\n", err.domain, err.code); FLSliceResult msg = CBLError_Message(&err); fprintf(stderr, "%.*s\n", (int)msg.size, (const char *)msg.buf); FLSliceResult_Release(msg); return; } // The lack of 'const' indicates this document is mutable // Create a new document (i.e. a record) in the database CBLDocument* mutableDoc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(mutableDoc); FLMutableDict_SetFloat(properties, FLSTR("version"), 3.0f); // Save it to the database if(!CBLDatabase_SaveDocument(database, mutableDoc, &err)) { // Failed to save, do error handling as above return; } // Since we will release the document, make a copy of the ID since it // is an internal pointer. Whenever we create or get an FLSliceResult // or FLStringResult we will need to free it later too! FLStringResult id = FLSlice_Copy(CBLDocument_ID(mutableDoc)); CBLDocument_Release(mutableDoc); // Update a document mutableDoc = CBLDatabase_GetMutableDocument(database, FLSliceResult_AsSlice(id), &err); if(!mutableDoc) { // Failed to retrieve, do error handling as above. NOTE: error code 0 simply means // the document does not exist. return; } properties = CBLDocument_MutableProperties(mutableDoc); FLMutableDict_SetString(properties, FLSTR("language"), FLSTR("C")); if(!CBLDatabase_SaveDocument(database, mutableDoc, &err)) { // Failed to save, do error handling as above return; } // Note const here, means readonly const CBLDocument* docAgain = CBLDatabase_GetDocument(database, FLSliceResult_AsSlice(id), &err); if(!docAgain) { // Failed to retrieve, do error handling as above. NOTE: error code 0 simply means // the document does not exist. return; } // No copy this time, so no release later (notice it is not FLStringResult this time) FLString retrievedID = CBLDocument_ID(docAgain); FLDict retrievedProperties = CBLDocument_Properties(docAgain); FLString retrievedLanguage = FLValue_AsString(FLDict_Get(retrievedProperties, FLSTR("language"))); printf("Document ID :: %.*s\n", (int)retrievedID.size, (const char *)retrievedID.buf); printf("Learning %.*s\n", (int)retrievedLanguage.size, (const char *)retrievedLanguage.buf); CBLDocument_Release(mutableDoc); CBLDocument_Release(docAgain); FLSliceResult_Release(id); // tag::query-syntax-n1ql[] // Create a query to fetch documents of type SDK int errorPos; CBLQuery* query = CBLDatabase_CreateQuery(database, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE type = \"SDK\""), &errorPos, &err); if(!query) { // Failed to create query, do error handling as above // Note that errorPos will contain the position in the N1QL string // that the parse failed, if applicable return; } CBLResultSet* result = CBLQuery_Execute(query, &err); if(!result) { // Failed to run query, do error handling as above return; } // end::query-syntax-n1ql[] // TODO: Result set count? CBLResultSet_Release(result); CBLQuery_Release(query); // Create replicator to push and pull changes to and from the cloud CBLEndpoint* targetEndpoint = CBLEndpoint_CreateWithURL(FLSTR("ws://localhost:4984/getting-started-db"), &err); if(!targetEndpoint) { // Failed to create endpoint, do error handling as above return; } CBLReplicatorConfiguration replConfig; CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("john"), FLSTR("pass")); memset(&replConfig, 0, sizeof(replConfig)); replConfig.database = database; replConfig.endpoint = targetEndpoint; replConfig.authenticator = basicAuth; CBLReplicator* replicator = CBLReplicator_Create(&replConfig, &err); CBLAuth_Free(basicAuth); CBLEndpoint_Free(targetEndpoint); if(!replicator) { // Failed to create replicator, do error handling as above return; } // Assume a function like the simple following // // static void getting_started_change_listener(void* context, CBLReplicator* repl, const CBLReplicatorStatus* status) { // if(status->error.code != 0) { // printf("Error %d / %d\n", status->error.domain, status->error.code); // } // } CBLListenerToken* token = CBLReplicator_AddChangeListener(replicator, getting_started_change_listener, NULL); CBLReplicator_Start(replicator, false); // Later, stop and release the replicator // end::getting-started[] // When finished release resources ... eg CBLListener_Remove(token); stop_replicator(replicator); } // tag::local-win-conflict-resolver[] static const CBLDocument* local_win_conflict_resolver(void* context, FLString documentID, const CBLDocument* localDocument, const CBLDocument* remoteDocument) { return localDocument; } // end::local-win-conflict-resolver[] // tag::remote-win-conflict-resolver[] static const CBLDocument* remote_win_conflict_resolver(void* context, FLString documentID, const CBLDocument* localDocument, const CBLDocument* remoteDocument) { return remoteDocument; } // end::remote-win-conflict-resolver[] // tag::merge-conflict-resolver[] static const CBLDocument* merge_conflict_resolver(void* context, FLString documentID, const CBLDocument* localDocument, const CBLDocument* remoteDocument) { FLDict localProps = CBLDocument_Properties(localDocument); FLDict remoteProps = CBLDocument_Properties(remoteDocument); FLMutableDict mergeProps = FLDict_MutableCopy(localProps, kFLDefaultCopy); FLDictIterator d; FLDictIterator_Begin(localProps, &d); FLValue value; while((value = FLDictIterator_GetValue(&d))) { FLString key = FLDictIterator_GetKeyString(&d); if(FLDict_Get(mergeProps, key)) { continue; } FLMutableDict_SetValue(mergeProps, key, value); FLDictIterator_Next(&d); } CBLDocument* mergeDocument = CBLDocument_CreateWithID(documentID); CBLDocument_SetProperties(mergeDocument, mergeProps); FLMutableDict_Release(mergeProps); return mergeDocument; } // end::merge-conflict-resolver[] static void test_replicator_conflict_resolve() { CBLDatabase* database = kDatabase; // tag::replication-conflict-resolver[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLEndpoint* target = CBLEndpoint_CreateWithURL(FLSTR("ws://localhost:4984/mydatabase"), &err); CBLReplicatorConfiguration replConfig; memset(&replConfig, 0, sizeof(replConfig)); replConfig.database = database; replConfig.endpoint = target; replConfig.conflictResolver = local_win_conflict_resolver; CBLReplicator* replicator = CBLReplicator_Create(&replConfig, &err); CBLEndpoint_Free(target); CBLReplicator_Start(replicator, false); // end::replication-conflict-resolver[] stop_replicator(replicator); } static bool custom_conflict_handler(void* context, CBLDocument* documentBeingSaved, const CBLDocument* conflictingDocument) { FLDict currentProps = CBLDocument_Properties(conflictingDocument); FLDict updatedProps = CBLDocument_Properties(documentBeingSaved); FLMutableDict newProps = FLDict_MutableCopy(updatedProps, kFLDefaultCopy); FLDictIterator d; FLDictIterator_Begin(currentProps, &d); FLValue currentValue; while((currentValue = FLDictIterator_GetValue(&d))) { FLString currentKey = FLDictIterator_GetKeyString(&d); if(FLDict_Get(newProps, currentKey)) { continue; } FLMutableDict_SetValue(newProps, currentKey, currentValue); FLDictIterator_Next(&d); } return true; } static void test_save_with_conflict_handler() { CBLDatabase* database = kDatabase; // tag::update-document-with-conflict-handler[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDocument* mutableDocument = CBLDatabase_GetMutableDocument(database, FLSTR("xyz"), &err); FLMutableDict properties = CBLDocument_MutableProperties(mutableDocument); FLMutableDict_SetString(properties, FLSTR("name"), FLSTR("apples")); /* static bool custom_conflict_handler(void* context, CBLDocument* documentBeingSaved, const CBLDocument* conflictingDocument) { FLDict currentProps = CBLDocument_Properties(conflictingDocument); FLDict updatedProps = CBLDocument_Properties(documentBeingSaved); FLMutableDict newProps = FLDict_MutableCopy(updatedProps, kFLDefaultCopy); FLDictIterator d; FLDictIterator_Begin(currentProps, &d); FLSlice currentKey = FLDictIterator_GetKeyString(&d); for(; currentKey.buf; currentKey = FLDictIterator_GetKeyString(&d)) { if(FLDict_Get(newProps, currentKey)) { continue; } FLValue currentValue = FLDictIterator_GetValue(&d); FLMutableDict_SetValue(newProps, currentKey, currentValue); } return true; } */ CBLDatabase_SaveDocumentWithConflictHandler(database, mutableDocument, custom_conflict_handler, NULL, &err); // end::update-document-with-conflict-handler[] } static void use_encryption() { #ifdef COUCHBASE_ENTERPRISE // tag::database-encryption[] // NOTE: No error handling, for brevity (see getting started) CBLDatabaseConfiguration config = CBLDatabaseConfiguration_Default(); // This returns a boolean, so check it in production code CBLEncryptionKey_FromPassword(&config.encryptionKey, FLSTR("password")); CBLError err; CBLDatabase* db = CBLDatabase_Open(FLSTR("seekrit"), &config, &err); // Change the encryption key (or add encryption if the DB is unencrypted) CBLEncryptionKey betterKey; CBLEncryptionKey_FromPassword(&betterKey, FLSTR("betterpassw0rd")); CBLDatabase_ChangeEncryptionKey(db, &betterKey, &err); // Remove encryption CBLDatabase_ChangeEncryptionKey(db, NULL, &err); // end::database-encryption[] #endif } static void reset_replicator_checkpoint() { CBLEndpoint* target = CBLEndpoint_CreateWithURL(FLSTR("ws://localhost:4984/db"), NULL); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = kDatabase; config.endpoint = target; CBLReplicator* replicator = CBLReplicator_Create(&config, NULL); CBLEndpoint_Free(target); // tag::replication-reset-checkpoint-full[] // replicator is a CBLReplicator* instance CBLReplicator_Start(replicator, true); (1) // end::replication-reset-checkpoint-full[] stop_replicator(replicator); } static void read_1x_attachment() { CBLDocument* document = CBLDocument_Create(); // tag::1x-attachment[] // NOTE: No error handling, for brevity (see getting started) CBLError err; FLDict properties = CBLDocument_Properties(document); FLDict attachments = FLValue_AsDict(FLDict_Get(properties, FLSTR("_attachments"))); const CBLBlob* avatar = FLDict_GetBlob(FLValue_AsDict(FLDict_Get(attachments, FLSTR("avatar")))); FLSliceResult content = CBLBlob_Content(avatar, &err); FLSliceResult_Release(content); // end::1x-attachment[] CBLDocument_Release(document); } static void create_new_database() { // tag::new-database[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDatabase* db = CBLDatabase_Open(FLSTR("my-database"), NULL, &err); // end::new-database[] kDatabase = db; } static void close_database() { CBLDatabase* db = kDatabase; // tag::close-database[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDatabase_Close(db, &err); // end::close-database[] } static void change_logging() { // tag::logging[] // For output to stdout CBLLog_SetConsoleLevel(kCBLLogVerbose); // For output to custom logging CBLLog_SetCallbackLevel(kCBLLogVerbose); // end::logging[] } static void load_prebuilt() { CBL_DeleteDatabase(FLSTR("travel-sample.cblite2"), kFLSliceNull, NULL); // tag::prebuilt-database[] // Note: Getting the path to a database is platform-specific. For desktop (including RPi) // this can be a simple filesystem path. For iOS you need to get the path from the // main bundle. For Android you need to extract it from your assets to a temporary directory // and then pass that path. // NOTE: No error handling, for brevity (see getting started) CBLError err; const char* path = "/path/to/travel-sample.cblite2"; if(!CBL_DatabaseExists(FLSTR("travel-sample.cblite2"), kFLSliceNull)) { CBL_CopyDatabase(FLStr(path), FLSTR("travel-sample"), NULL, &err); } // end::prebuilt-database[] CBLDatabase_Close(kDatabase, NULL); CBLDatabase_Release(kDatabase); kDatabase = CBLDatabase_Open(FLSTR("travel-sample"), NULL, NULL); } static void query_deleted_document() { CBLDatabase* db = kDatabase; // tag::query-deleted-documents[] // Query documents that have been deleted // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id FROM _ WHERE meta().deleted"), NULL, &err); // end::query-deleted-documents[] CBLQuery_Release(query); } static void create_document() { CBLDatabase* db = kDatabase; // tag::initializer[] // NOTE: No error handling, for brevity (see getting started) CBLDocument* newTask = CBLDocument_CreateWithID(FLSTR("xyz")); FLMutableDict properties = CBLDocument_MutableProperties(newTask); FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("task")); FLMutableDict_SetString(properties, FLSTR("owner"), FLSTR("todo")); // Storing time in millisecond, bluntly FLMutableDict_SetUInt(properties, FLSTR("createdAt"), time(NULL) * 1000); CBLError err; CBLDatabase_SaveDocument(db, newTask, &err); CBLDocument_Release(newTask); // end::initializer[] } static void update_document() { CBLDatabase* db = kDatabase; // tag::update-document[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDocument* mutableDocument = CBLDatabase_GetMutableDocument(db, FLSTR("xyz"), &err); FLMutableDict properties = CBLDocument_MutableProperties(mutableDocument); FLMutableDict_SetString(properties, FLSTR("name"), FLSTR("apples")); CBLDatabase_SaveDocument(db, mutableDocument, &err); CBLDocument_Release(mutableDocument); // end::update-document[] } // Note use_typed_accessors not applicable static void do_batch_operation() { CBLDatabase* db = kDatabase; // tag::batch[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLDatabase_BeginTransaction(db, &err); char buffer[7]; for(int i = 0; i < 10; i++) { CBLDocument* doc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(doc); FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("user")); sprintf(buffer, "user %d", i); FLMutableDict_SetString(properties, FLSTR("name"), FLStr(buffer)); FLMutableDict_SetBool(properties, FLSTR("admin"), false); CBLDatabase_SaveDocument(db, doc, &err); CBLDocument_Release(doc); printf("Saved user document %s\n", buffer); } CBLDatabase_EndTransaction(db, true, &err); // end::batch[] } static void document_listener(void* context, const CBLDatabase* db, FLString id) { CBLError err; const CBLDocument* doc = CBLDatabase_GetDocument(db, id, &err); FLDict properties = CBLDocument_Properties(doc); FLString verified_account = FLValue_AsString(FLDict_Get(properties, FLSTR("verified_account"))); printf("Status :: %.*s\n", (int)verified_account.size, (const char *)verified_account.buf); CBLDocument_Release(doc); } static void database_change_listener() { CBLDatabase* db = kDatabase; // tag::document-listener[] /* static void document_listener(void* context, const CBLDatabase* db, FLString id) { CBLError err; const CBLDocument* doc = CBLDatabase_GetDocument(db, id, &err); FLDict properties = CBLDocument_Properties(doc); FLString verified_account = FLValue_AsString(FLDict_Get(properties, FLSTR("verified_account"))); printf("Status :: %.*s\n", (int)verified_account.size, (const char *)verified_account.buf); CBLDocument_Release(doc); } */ CBLListenerToken* token = CBLDatabase_AddDocumentChangeListener(db, FLSTR("user.john"), document_listener, NULL); // end::document-listener[] CBLListener_Remove(token); } static void document_expiration() { CBLDatabase* db = kDatabase; // tag::document-expiration[] // Purge the document one day from now // Overly simplistic for example purposes // NOTE: API takes milliseconds // NOTE: No error handling, for brevity (see getting started) time_t ttl = time(NULL) + 24 * 60 * 60; ttl *= 1000; CBLError err; CBLDatabase_SetDocumentExpiration(db, FLSTR("doc123"), ttl, &err); // Reset expiration CBLDatabase_SetDocumentExpiration(db, FLSTR("doc1"), 0, &err); // Query documents that will be expired in less than five minutes time_t fiveMinutesFromNow = time(NULL) + 5 * 60; fiveMinutesFromNow *= 1000; FLMutableDict parameters = FLMutableDict_New(); FLMutableDict_SetInt(parameters, FLSTR("five_minutes"), fiveMinutesFromNow); CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id FROM _ WHERE meta().expiration < $five_minutes"), NULL, &err); CBLQuery_SetParameters(query, parameters); FLMutableDict_Release(parameters); // end::document-expiration[] } static void use_blob() { CBLDatabase* db = kDatabase; CBLDocument* newTask = CBLDocument_Create(); // tag::blob[] // Note: Reading the data is implementation dependent, as with prebuilt databases // NOTE: No error handling, for brevity (see getting started) uint8_t buffer[128000]; FILE* avatar_file = fopen("avatar.jpg", "rb"); size_t read = fread(buffer, 1, 128000, avatar_file); (2) FLSliceResult avatar = FLSliceResult_CreateWith(buffer, read); CBLBlob* blob = CBLBlob_CreateWithData(FLSTR("image/jpeg"), FLSliceResult_AsSlice(avatar)); (3) FLSliceResult_Release(avatar); // TODO: Create shortcut blob method CBLError err; FLMutableDict properties = CBLDocument_MutableProperties(newTask); FLSlot_SetBlob(FLMutableDict_Set(properties, FLSTR("avatar")), blob); CBLDatabase_SaveDocument(db, newTask, &err); (4) // end::blob[] CBLDocument_Release(newTask); CBLBlob_Release(blob); } static void doc_json() { CBLDatabase* db = kDatabase; // tag::tojson-document[] FLString json = FLSTR("{\"id\":\"1002\",\"type\":\"hotel\",\"name\":\"Hotel Ned\",\"city\":\"Balmain\",\"country\":\"Australia\"}"); // Create a document and set the JSON data to the document CBLError err; CBLDocument* newDoc = CBLDocument_CreateWithID(FLSTR("hotel_1002")); CBLDocument_SetJSON(newDoc, json, &err); // Save the document to the database CBLDatabase_SaveDocument(db, newDoc, &err); // Release created doc after using it CBLDocument_Release(newDoc); // Get the document from the database const CBLDocument* doc = CBLDatabase_GetDocument(db, FLSTR("hotel_1002"), &err); // Get document body as JSON FLSliceResult docJson = CBLDocument_CreateJSON(doc); printf("Document in JSON :: %.*s\n", (int)docJson.size, (const char *)docJson.buf); // Release JSON data after using it FLSliceResult_Release(docJson); // Release doc read from the database after using it CBLDocument_Release(doc); // end::tojson-document[] } static void dict_json() { // tag::tojson-dictionary[] FLString json = FLSTR("{\"id\":\"1002\",\"type\":\"hotel\",\"name\":\"Hotel Ned\",\"city\":\"Balmain\",\"country\":\"Australia\"}"); // Create a dictionary from the JSON string FLError err; FLSliceResult jsonData1 = FLData_ConvertJSON(json, &err); FLDict hotel = FLValue_AsDict(FLValue_FromData(FLSliceResult_AsSlice(jsonData1), kFLTrusted)); // Iterate through the dictionary FLDictIterator iter; FLDictIterator_Begin(hotel, &iter); FLValue value; while (NULL != (value = FLDictIterator_GetValue(&iter))) { FLString key = FLDictIterator_GetKeyString(&iter); FLString strValue = FLValue_AsString(value); printf("%.*s :: %.*s\n", (int)key.size, (const char*)key.buf, (int)strValue.size, (const char*)strValue.buf); FLDictIterator_Next(&iter); } // Convert the dictionary to JSON FLSliceResult jsonData2 = FLValue_ToJSON((FLValue)hotel); printf("Hotel in JSON :: %.*s\n", (int)jsonData2.size, (const char *)jsonData2.buf); // Release JSON data after finish using it FLSliceResult_Release(jsonData1); FLSliceResult_Release(jsonData2); // end::tojson-dictionary[] } static void array_json() { // tag::tojson-array[] FLString json = FLSTR("[\"Hotel Ned\", \"Hotel Ted\"]"); // Create an array from the JSON string FLError err; FLSliceResult jsonData1 = FLData_ConvertJSON(json, &err); FLArray hotels = FLValue_AsArray(FLValue_FromData(FLSliceResult_AsSlice(jsonData1), kFLTrusted)); // Iterate through the array FLArrayIterator iter; FLArrayIterator_Begin(hotels, &iter); FLValue value; while (NULL != (value = FLArrayIterator_GetValue(&iter))) { FLString hotel = FLValue_AsString(value); printf("Hotel :: %.*s\n", (int)hotel.size, (const char *)hotel.buf); FLArrayIterator_Next(&iter); } // Convert the array to JSON FLSliceResult jsonData2 = FLValue_ToJSON((FLValue)hotels); printf("Hotels in JSON :: %.*s\n", (int)jsonData2.size, (const char *)jsonData2.buf); // Release JSON data after finish using it FLSliceResult_Release(jsonData1); FLSliceResult_Release(jsonData2); // end::tojson-array[] } static void datatype_dictionary() { CBLDatabase *db = kDatabase; // tag::datatype_dictionary[] CBLError err; const CBLDocument *doc = CBLDatabase_GetDocument(db, FLSTR("doc1"), &err); FLDict properties = CBLDocument_Properties(doc); // Getting a dictionary from the document's properties FLValue dictValue = FLDict_Get(properties, FLSTR("address")); FLDict dict = FLValue_AsDict(dictValue); // Access a value with a key from the dictionary FLValue streetVal = FLDict_Get(dict, FLSTR("street")); FLString street = FLValue_AsString(streetVal); // Iterate dictionary FLDictIterator iter; FLDictIterator_Begin(dict, &iter); FLValue value; while (NULL != (value = FLDictIterator_GetValue(&iter))) { FLString key = FLDictIterator_GetKeyString(&iter); FLString strValue = FLValue_AsString(value); printf("Key :: %.*s\n", (int)key.size, (const char *)key.buf); printf("Value :: %.*s\n", (int)strValue.size, (const char *)strValue.buf); // ... FLDictIterator_Next(&iter); } // Create a mutable copy. // kFLDefaultCopy is shallow which means the nested dictionaries and arrays will be // referenced but not copied. Use kFLDeepCopyImmutables for the deep copy. FLMutableDict mutableDict = FLDict_MutableCopy(dict, kFLDefaultCopy); // Release when finish using it FLMutableDict_Release(mutableDict); // end::datatype_dictionary[] } static void datatype_mutable_dictionary() { CBLDatabase *db = kDatabase; // tag::datatype_mutable_dictionary[] // tag::datatype_mutable_dictionary-create[] // Create a new mutable dictionary and populate some keys/values FLMutableDict dict = FLMutableDict_New(); FLMutableDict_SetString(dict, FLSTR("street"), FLSTR("1 Main st.")); FLMutableDict_SetString(dict, FLSTR("city"), FLSTR("San Francisco")); // end::datatype_mutable_dictionary-create[] // tag::datatype_mutable_dictionary-add-to-doc[] // Set the dictionary to document's properties and save the document CBLDocument *doc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(doc); FLMutableDict_SetDict(properties, FLSTR("address"), dict); // end::datatype_mutable_dictionary-add-to-doc[] CBLError err; CBLDatabase_SaveDocument(db, doc, &err); CBLDocument_Release(doc); // Release when finish using it FLMutableDict_Release(dict); // end::datatype_mutable_dictionary[] } static void datatype_array() { CBLDatabase *db = kDatabase; // tag::datatype_array[] CBLError err; const CBLDocument *doc = CBLDatabase_GetDocument(db, FLSTR("doc1"), &err); FLDict properties = CBLDocument_Properties(doc); // Getting a phones array from the document's properties FLValue arrayValue = FLDict_Get(properties, FLSTR("phones")); FLArray array = FLValue_AsArray(arrayValue); // Get element count int count = FLArray_Count(array); printf("Count :: %d\n", count); // Access an array element by index if (!FLArray_IsEmpty(array)) { FLValue phoneVal = FLArray_Get(array, 0); FLString phone = FLValue_AsString(phoneVal); printf("Value :: %.*s\n", (int)phone.size, (const char *)phone.buf); } // Iterate array FLArrayIterator iter; FLArrayIterator_Begin(array, &iter); FLValue val; while (NULL != (val = FLArrayIterator_GetValue(&iter))) { FLString str = FLValue_AsString(val); printf("Value :: %.*s\n", (int)str.size, (const char *)str.buf); FLArrayIterator_Next(&iter); } // end::datatype_array[] // Create a mutable copy. // kFLDefaultCopy is shallow which means the nested dictionaries and arrays will be // referenced but not copied. Use kFLDeepCopyImmutables for the deep copy. FLMutableArray mutableArray = FLArray_MutableCopy(array, kFLDefaultCopy); // Release when finish using it FLMutableArray_Release(mutableArray); } static void datatype_mutable_array() { CBLDatabase *db = kDatabase; // tag::datatype_mutable_array[] // tag::datatype_mutable_array-create[] // Create a new mutable array and populate data into the array FLMutableArray phones = FLMutableArray_New(); FLMutableArray_AppendString(phones, FLSTR("650-000-0000")); FLMutableArray_AppendString(phones, FLSTR("650-000-0001")); // end::datatype_mutable_array-create[] // tag::datatype_mutable_array-add-to-doc[] // Set the array to document's properties and save the document CBLDocument *doc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(doc); FLMutableDict_SetArray(properties, FLSTR("phones"), phones); // end::datatype_mutable_array-add-to-doc[] CBLError err; CBLDatabase_SaveDocument(db, doc, &err); CBLDocument_Release(doc); // Release the created dictionary FLMutableArray_Release(phones); // end::datatype_mutable_array[] } static void datatype_usage() { // tag::datatype_usage[] // tag::datatype_usage_createdb[] // Open or create DB if it doesn't exist CBLError err; CBLDatabase* database = CBLDatabase_Open(FLSTR("mydb"), NULL, &err); if(!database) { // Error handling. For brevity, this is truncated in the rest of the snippet // and omitted in other doc code snippets fprintf(stderr, "Error opening database (%d / %d)\n", err.domain, err.code); FLSliceResult msg = CBLError_Message(&err); fprintf(stderr, "%.*s\n", (int)msg.size, (const char *)msg.buf); FLSliceResult_Release(msg); return; } // end::datatype_usage_createdb[] // tag::datatype_usage_createdoc[] // Create your new document // The lack of 'const' indicates this document is mutable CBLDocument* mutableDoc = CBLDocument_Create(); FLMutableDict properties = CBLDocument_MutableProperties(mutableDoc); // end::datatype_usage_createdoc[] // tag::datatype_usage_mutdict[] // Create and populate mutable dictionary FLMutableDict address = FLMutableDict_New(); FLMutableDict_SetString(address, FLSTR("street"), FLSTR("1 Main st.")); FLMutableDict_SetString(address, FLSTR("city"), FLSTR("San Francisco")); FLMutableDict_SetString(address, FLSTR("state"), FLSTR("CA")); FLMutableDict_SetString(address, FLSTR("country"), FLSTR("USA")); FLMutableDict_SetString(address, FLSTR("code"), FLSTR("90210")); // end::datatype_usage_mutdict[] // tag::datatype_usage_mutarray[] // Create and populate mutable array FLMutableArray phones = FLMutableArray_New(); FLMutableArray_AppendString(phones, FLSTR("650-000-0000")); FLMutableArray_AppendString(phones, FLSTR("650-000-0001")); // end::datatype_usage_mutarray[] // tag::datatype_usage_populate[] // Initialize and populate the document // Add document type and hotel name as string FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("hotel")); FLMutableDict_SetString(properties, FLSTR("hotel"), FLSTR("")); // Add average room rate (float) FLMutableDict_SetFloat(properties, FLSTR("room_rate"), 121.75f); // Add address (dictionary) FLMutableDict_SetDict(properties, FLSTR("address"), dict); // Add phone numbers(array) FLMutableDict_SetArray(properties, FLSTR("phones"), phones); // end::datatype_usage_populate[] // tag::datatype_usage_persist[] CBLError err; CBLDatabase_SaveDocument(database, doc, &err); // end::datatype_usage_persist[] // tag::datatype_usage_closedb[] CBLError err; CBLDatabase_Close(database, &err); // end::datatype_usage_closedb[] // tag::datatype_usage_release[] CBLDatabase_Release(database); CBLDocument_Release(doc); FLMutableDict_Release(dict); FLMutableArray_Release(phones); // end::datatype_usage_release[] // end::datatype_usage[] } // end datatype_usage() static void create_index() { CBLDatabase* db = kDatabase; // tag::query-index[] // For value types, this is optional but provides performance enhancements // NOTE: No error handling, for brevity (see getting started) // Syntax for second argument is the same as taking from a N1QL SELECT // i.e. SELECT (type, name) FROM _; CBLValueIndexConfiguration config = { kCBLN1QLLanguage, FLSTR("type, name") }; CBLError err; CBLDatabase_CreateValueIndex(db, FLSTR("TypeNameIndex"), config, &err); // end::query-index[] } static void select_meta() { CBLDatabase* db = kDatabase; // tag::query-select-meta[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, type, name FROM _"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString id = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("id"))); FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Document ID :: %.*s\n", (int)id.size, (const char *)id.buf); printf("Document Name :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-select-meta[] } static void select_id() { CBLDatabase* db = kDatabase; CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id FROM _"), NULL, &err); // tag::query-access-id[] // NOTE: No error handling, for brevity (see getting started) CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString id = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("id"))); printf("Document ID :: %.*s\n", (int)id.size, (const char *)id.buf); } // end::query-access-id[] CBLResultSet_Release(results); CBLQuery_Release(query); } static void query_change_listener(void* context, CBLQuery* query, CBLListenerToken* token) { CBLError err; CBLResultSet* results = CBLQuery_CopyCurrentResults(query, token, &err); while(CBLResultSet_Next(results)) { // Update UI } } static void select_all() { CBLDatabase* db = kDatabase; // tag::query-select-all[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _"), NULL, &err); // All results will be available from the above query CBLQuery_Release(query); // end::query-select-all[] // tag::live-query[] /* static void query_change_listener(void* context, CBLQuery* query, CBLListenerToken* token) { CBLError err; CBLResultSet* results = CBLQuery_CopyCurrentResults(query, token, &err); while(CBLResultSet_Next(results)) { // Update UI } } */ query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _"), NULL, &err); (5) CBLListenerToken* token = CBLQuery_AddChangeListener(query, query_change_listener, NULL); (6) // end::live-query[] // tag::stop-live-query[] CBLListener_Remove(token); // The token received from AddChangeListener CBLQuery_Release(query); // end::stop-live-query[] } static void select_and_access_all() { CBLDatabase* db = kDatabase; CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _"), NULL, &err); // tag::query-access-all[] CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLDict dict = FLValue_AsDict(CBLResultSet_ValueForKey(results, FLSTR("_"))); FLString id = FLValue_AsString(FLDict_Get(dict, FLSTR("id"))); FLString type = FLValue_AsString(FLDict_Get(dict, FLSTR("type"))); FLString name = FLValue_AsString(FLDict_Get(dict, FLSTR("name"))); FLString city = FLValue_AsString(FLDict_Get(dict, FLSTR("city"))); printf("ID :: %.*s\n", (int)id.size, (const char *)id.buf); printf("Type :: %.*s\n", (int)type.size, (const char *)type.buf); printf("Name :: %.*s\n", (int)name.size, (const char *)name.buf); printf("City :: %.*s\n", (int)city.size, (const char *)city.buf); } // All results will be available from the above query CBLResultSet_Release(results); // end::query-access-all[] CBLQuery_Release(query); } static void select_props() { CBLDatabase* db = kDatabase; // tag::query-access-props[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT type, name, city FROM _"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString type = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("type"))); FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); FLString city = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("city"))); printf("Type :: %.*s\n", (int)type.size, (const char *)type.buf); printf("Name :: %.*s\n", (int)name.size, (const char *)name.buf); printf("City :: %.*s\n", (int)city.size, (const char *)city.buf); } // end::query-access-props[] // All results will be available from the above query CBLResultSet_Release(results); CBLQuery_Release(query); } static void select_where() { CBLDatabase* db = kDatabase; // tag::query-where[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE type = \"hotel\" LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLDict dict = FLValue_AsDict(CBLResultSet_ValueForKey(results, FLSTR("_"))); FLString name = FLValue_AsString(FLDict_Get(dict, FLSTR("name"))); printf("Document Name :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-where[] } static void use_collection_contains() { CBLDatabase* db = kDatabase; // tag::query-collection-operator-contains[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name, public_likes FROM _ WHERE type = \"hotel\" " "AND ARRAY_CONTAINS(public_likes, \"Armani Langworth\")"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLArray publicLikes = FLValue_AsArray(CBLResultSet_ValueForKey(results, FLSTR("public_likes"))); FLStringResult json = FLValue_ToJSON((FLValue)publicLikes); printf("Public Likes :: %.*s\n", (int)json.size, (const char *)json.buf); FLSliceResult_Release(json); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-collection-operator-contains[] } static void use_collection_in() { CBLDatabase* db = kDatabase; // tag::query-collection-operator-in[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE \"Armani\" IN (first, last, username)"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLDict body = FLValue_AsDict(CBLResultSet_ValueAtIndex(results, 0)); FLStringResult json = FLValue_ToJSON((FLValue)body); printf("In results :: %.*s\n", (int)json.size, (const char *)json.buf); FLSliceResult_Release(json); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-collection-operator-in[] } static void select_like() { CBLDatabase* db = kDatabase; // tag::query-like-operator[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" " "AND lower(name) LIKE \"Royal Engineers Museum\" LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-like-operator[] } static void select_wildcard_like() { CBLDatabase* db = kDatabase; // tag::query-like-operator-wildcard-match[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" " "AND lower(name) LIKE \"Eng%e%\" LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-like-operator-wildcard-match[] } static void select_wildcard_character_like() { CBLDatabase* db = kDatabase; // tag::query-like-operator-wildcard-character-match[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" " "AND lower(name) LIKE \"Royal Eng____rs Museum\" LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-like-operator-wildcard-character-match[] } static void select_regex() { CBLDatabase* db = kDatabase; // tag::query-regex-operator[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, name FROM _ WHERE type = \"landmark\" " "AND regexp_like(name, \"\\bEng.*e\\b\") LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-regex-operator[] } static void select_join() { CBLDatabase* db = kDatabase; // tag::query-join[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT airline.name, airline.callsign, route.destinationairport, route.stops, route.airline " "FROM _ AS airline INNER JOIN _ AS route ON meta(airline).id = route.airlineid " "WHERE route.type = \"route\" AND airline.type = \"airline\" AND route.sourceairport = \"RIX\""), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString name = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("name"))); printf("Name Property :: %.*s\n", (int)name.size, (const char *)name.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-join[] } static void group_by() { CBLDatabase* db = kDatabase; // tag::query-groupby[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT count(*), country, tz FROM _ WHERE type = \"airport\" AND geo.alt >= 300 " "GROUP BY country, tz"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { int64_t count = FLValue_AsInt(CBLResultSet_ValueForKey(results, FLSTR("$1"))); FLString tz = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("tz"))); FLString country = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("country"))); printf("There are %" PRIi64 " airports in the %.*s timezone located in %.*s and above 300 ft\n", count, (int)tz.size, (const char *)tz.buf, (int)country.size, (const char *)country.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-groupby[] } static void order_by() { CBLDatabase* db = kDatabase; // tag::query-orderby[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id, title FROM _ WHERE type = \"hotel\" ORDER BY title ASC LIMIT 10"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString title = FLValue_AsString(CBLResultSet_ValueForKey(results, FLSTR("title"))); printf("Title :: %.*s\n", (int)title.size, (const char *)title.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::query-orderby[] } static void test_explain_statement() { CBLDatabase* db = kDatabase; { // tag::query-explain-all[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT * FROM _ WHERE type = \"hotel\" GROUP BY country ORDER BY title ASC LIMIT 10"), NULL, &err); FLSliceResult explanation = CBLQuery_Explain(query); printf("%.*s", (int)explanation.size, (const char *)explanation.buf); FLSliceResult_Release(explanation); // end::query-explain-all[] } // DOCS NOTE: Others omitted for now } static void query_result_json() { CBLDatabase* db = kDatabase; CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id as id, name, city, type FROM _ LIMIT 10"), NULL, &err); // tag::query-access-json[] CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLDict result = CBLResultSet_ResultDict(results); FLStringResult json = FLValue_ToJSON((FLValue)result); printf("JSON Result :: %.*s\n", (int)json.size, (const char *)json.buf); FLSliceResult_Release(json); } CBLResultSet_Release(results); // end::query-access-json[] CBLQuery_Release(query); } static void create_full_text_index() { CBLDatabase* db = kDatabase; const char* tasks[] = { "buy groceries", "play chess", "book travels", "buy museum tickets" }; char idBuffer[7]; for(int i = 0; i < 4; i++) { const char* task = tasks[i]; sprintf(idBuffer, "extra%d", i); const CBLDocument* doc = CBLDatabase_GetDocument(db, FLStr(idBuffer), NULL); if(doc) { CBLDocument_Release(doc); continue; } CBLDocument* mutableDoc = CBLDocument_CreateWithID(FLStr(idBuffer)); FLMutableDict properties = CBLDocument_MutableProperties(mutableDoc); FLMutableDict_SetString(properties, FLSTR("type"), FLSTR("task")); FLMutableDict_SetString(properties, FLSTR("task"), FLStr(task)); CBLDatabase_SaveDocument(db, mutableDoc, NULL); CBLDocument_Release(mutableDoc); } // tag::fts-index[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLFullTextIndexConfiguration config = { kCBLN1QLLanguage, FLSTR("name"), false }; CBLDatabase_CreateFullTextIndex(db, FLSTR("nameFTSIndex"), config, &err); // end::fts-index[] } static void full_text_search() { CBLDatabase* db = kDatabase; // tag::fts-query[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT meta().id FROM _ WHERE MATCH(nameFTSIndex, \"'buy'\")"), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { FLString id = FLValue_AsString(CBLResultSet_ValueAtIndex(results, 0)); printf("Document id :: %.*s\n", (int)id.size, (const char *)id.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::fts-query[] } static void start_replication() { CBLDatabase* db = kDatabase; /* * This requires Sync Gateway running with the following config, or equivalent: * * { * "log":["*"], * "databases": { * "db": { * "server":"walrus:", * "users": { * "GUEST": {"disabled": false, "admin_channels": ["*"] } * } * } * } * } */ // tag::replication[] // NOTE: No error handling, for brevity (see getting started) // Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion) CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; config.replicatorType = kCBLReplicatorTypePull; CBLReplicator* replicator = CBLReplicator_Create(&config, &err); CBLEndpoint_Free(target); CBLReplicator_Start(replicator, false); // end::replication[] stop_replicator(replicator); } // Console logging domain methods are not applicable to C static void file_logging() { // tag::file-logging[] // NOTE: No error handling, for brevity (see getting started) // NOTE: You will need to use a platform appropriate method for finding // a temporary directory FLString tempFolder = FLSTR("/tmp/cbllog"); CBLLogFileConfiguration config; // Don't bother zeroing, since we set all properties config.level = kCBLLogInfo; config.directory = tempFolder; config.maxRotateCount = 5; config.maxSize = 10240; config.usePlaintext = false; CBLError err; CBLLog_SetFileConfig(config, &err); // end::file-logging[] } // tag::custom-logging[] static void custom_log_callback(CBLLogDomain domain, CBLLogLevel level, FLString message) { // handle the message, for example piping it to // a third party framework } // end::custom-logging[] static void enable_custom_logging() { // tag::set-custom-logging[] CBLLog_SetCallback(custom_log_callback); // end::set-custom-logging[] } static void enable_basic_auth() { CBLDatabase* db = kDatabase; // tag::basic-authentication-full[] // NOTE: No error handling, for brevity (see getting started) CBLError err; FLString url = FLSTR("ws://localhost:4984/mydatabase"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("john"), FLSTR("pass")); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; config.authenticator = basicAuth; CBLReplicator* replicator = CBLReplicator_Create(&config, &err); CBLEndpoint_Free(target); CBLAuth_Free(basicAuth); CBLReplicator_Start(replicator, false); // end::basic-authentication-full[] stop_replicator(replicator); } static void docsonly_N1QL_Params(CBLDatabase* argDb) { CBLDatabase* database = argDb; // tag::query-syntax-n1ql-params[] int errorPos; CBLError err; FLString n1qlstr = FLSTR("SELECT * FROM _ WHERE type = $type"); FLMutableDict n1qlparams = FLMutableDict_New(); FLMutableDict_SetString(n1qlparams, FLSTR("type"), FLSTR("hotel")); CBLQuery* query = CBLDatabase_CreateQuery(database, kCBLN1QLLanguage, n1qlstr, &errorPos, &err); CBLQuery_SetParameters(query, n1qlparams); if(!query) { /* Do appropriate error handling ... Note that (where applicable) errorPos contains the position in the N1QL string that the parse failed */ FLMutableDict_Release(n1qlparams); CBLQuery_Release(query); return; } CBLResultSet* result = CBLQuery_Execute(query, &err); if(!result) { // Failed to run query, do error handling ... return; } // Release query when finished with FLMutableDict_Release(n1qlparams); CBLQuery_Release(query); // ... process results as required // Release result set then finished with CBLResultSet_Release(result); // end::query-syntax-n1ql-params[] } // tag::console-logging-db[] //Placeholder for code to increase level of console logging for kCBLLogDomainDatabase domain // end::console-logging-db[] // tag::console-logging[] //Placeholder for code to increase level of console logging for all domains // end::console-logging[] // tag::date-getter[] //Placeholder for Date accessors. // end::date-getter[] // tag::query-index[] // placeholder // end::query-index[] // DOCS NOTE // Page=Data Sync >> Configuration Summary // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#configuration-summary static void docs_act_replication(CBLDatabase* argDb) { CBLDatabase* database = argDb; /* * This requires Sync Gateway running with the following config, or equivalent: * * { * "log":["*"], * "databases": { * "db": { * "server":"walrus:", * "users": { * "GUEST": {"disabled": false, "admin_channels": ["*"] } * } * } * } * } */ // tag::p2p-act-rep-func[] // Purpose -- Show configuration , initialization and running of a replicator // NOTE: No error handling, for brevity (see getting started) // Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion) CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); (7) CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = database; config.endpoint = target; (8) // tag::p2p-act-rep-config-cont[] // Set replication direction and mode config.replicatorType = kCBLReplicatorTypePull; (9) config.continuous = true; // end::p2p-act-rep-config-cont[] // Optionally, set auto-purge behavior (here we override default) config.disableAutoPurge = true; (10) // Optionally, configure Client Authentication // Here we are using to Basic Authentication, // Providing username and password credentials CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("username"), FLSTR("passwd")); (11) config.authenticator = basicAuth; // Optionally, configure how we handle conflicts config.conflictResolver = simpleConflictResolver_localWins; (12) // Initialize replicator with created config CBLReplicator* replicator = CBLReplicator_Create(&config, &err); (13) CBLEndpoint_Free(target); // Optionally, add change listener CBLListenerToken* token = CBLReplicator_AddChangeListener(replicator, simpleChangeListener, NULL); (14) // Start replication CBLReplicator_Start(replicator, false); (15) // end::p2p-act-rep-func[] // ... other processing as required // When finished release resources e.g. CBLAuth_Free(basicAuth); stop_replicator(replicator); } // END configuration summary snippets // DOCS NOTE: // Page=Data Sync >> Configuration // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#configuration-summary // This function is not pulled into docs en-bloc // it is a slightly more in-depth than the configurationsummary above // and the snippets within it are used individually or in sets // to illustrate specific points as required // static void docs_act_replication_config_section_snippets() { CBLDatabase* db = kDatabase; bool docs_example_ShowBasicAuth = false; bool docs_example_ShowSessionAuth = false; /* * This requires Sync Gateway running with the following config, or equivalent: * * { * "log":["*"], * "databases": { * "db": { * "server":"walrus:", * "users": { * "GUEST": {"disabled": false, "admin_channels": ["*"] } * } * } * } * } */ // tag::p2p-act-rep-func-full[] // NOTE: No error handling, for brevity (see getting started) // Note: Android emulator needs to use 10.0.2.2 for localhost (10.0.3.2 for GenyMotion) // tag::sgw-act-rep-initialize[] // Initialize the configuration object and set db target CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); (16) CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; (17) // end::sgw-act-rep-initialize[] // tag::p2p-act-rep-config-type[] config.replicatorType = kCBLReplicatorTypePull; // end::p2p-act-rep-config-type[] // tag::p2p-act-rep-config-cont[] config.continuous = true; // end::p2p-act-rep-config-cont[] // tag::replication-retry-config[] // Configure replication retries // tag::replication-set-heartbeat[] config.heartbeat = 120; // (18) // end::replication-set-heartbeat[] // tag::replication-set-maxattempts[] config.maxAttempts = 20; // (19) // end::replication-set-maxattempts[] // tag::replication-set-maxattemptwaittime[] config.maxAttemptWaitTime = 600; // (20) // end::replication-set-maxattemptwaittime[] // end::replication-retry-config[] // tag::basic-authentication[] // Configure Client Authentication to Basic Authentication // Providing username and password credentials if(docs_example_ShowBasicAuth) { CBLAuthenticator* basicAuth = CBLAuth_CreatePassword(FLSTR("username"), FLSTR("passwd")); config.authenticator = basicAuth; (21) } // end::basic-authentication[] // tag::session-authentication[] if(docs_example_ShowSessionAuth) { CBLAuthenticator* sessionAuth = CBLAuth_CreateSession(FLSTR("904ac010862f37c8dd99015a33ab5a3565fd8447"), FLSTR("optionalCookieName")); config.authenticator = sessionAuth; (22) } // end::session-authentication[] // tag::replication-custom-header[] // Optionally, add custom headers FLMutableDict customHdrs = FLMutableDict_New(); FLMutableDict_SetString(customHdrs, FLSTR("customHeaderName"), FLSTR("customHeaderValue")); config.headers = customHdrs; // tag::certificate-pinning[] char cert_buf[10000]; FILE* cert_file = fopen("cert.pem", "r"); size_t read = fread(cert_buf, 1, sizeof(cert_buf), cert_file); config.pinnedServerCertificate = (FLSlice){cert_buf, read}; // end::certificate-pinning[] // end::replication-custom-header[] // FILTERS // tag::replication-push-filter[] // tag::replication-pull-filter[] // Purpose - Illustrate use of push and-or pull filter functions config.pushFilter = simpleReplicationFilter; config.pullFilter = simpleReplicationFilter; // end::replication-pull-filter[] // end::replication-push-filter[] // Auto-purge over-ride // tag::autopurge-override[] config.disableAutoPurge = true; (23) // end::autopurge-override[] // Initialize replicator with created config CBLReplicator* replicator = CBLReplicator_Create(&config, &err); (24) CBLEndpoint_Free(target); // Add optional change listener CBLListenerToken* token_ReplChangeListener = CBLReplicator_AddChangeListener(replicator, simpleChangeListener, NULL); (25) } // END replication.html >> configure section // PAGE=Data Sync >> Initialize section // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#lbl-init-repl static CBLReplicator* docs_act_replication_Intialize( void* context, CBLReplicatorConfiguration argConfig, bool argResetRequired) { CBLError err; bool docs_example_resetRequired = argResetRequired; // tag::p2p-act-rep-start-full[] CBLReplicator* thisRepl = CBLReplicator_Create(&argConfig, &err); (26) // end::p2p-act-rep-start-full[] if(!docs_example_resetRequired) { // tag::p2p-act-rep-start-full[] CBLReplicator_Start(thisRepl, false); (27) // end::p2p-act-rep-start-full[] } else { // tag::replication-reset-checkpoint[] CBLReplicator_Start(thisRepl, true); (28) // end::replication-reset-checkpoint[] } return thisRepl; } // END replication.html >> initialize section // PAGE=Data Sync >> Monitor section // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#lbl-repl-mon static void docs_act_replication_Monitor( void* context, CBLReplicator* argRepl) { CBLError err; CBLReplicator* thisRepl = argRepl; // tag::p2p-act-rep-add-change-listener[] // Purpose -- illustrate addition of a Replicator change listener CBLListenerToken* token_ReplChangeListener = CBLReplicator_AddChangeListener(thisRepl, simpleChangeListener, NULL); // end::p2p-act-rep-add-change-listener[] // tag::add-document-replication-listener[] // Purpose -- illustrate addition of a Document Replicator listener CBLListenerToken* token_ReplDocListener = CBLReplicator_AddDocumentReplicationListener( thisRepl, SimpleReplicationDocumentListener, context); // end::add-document-replication-listener[] // tag::remove-document-replication-listener[] // Purpose -- illustrate removal of a listener CBLListener_Remove(token_ReplDocListener); CBLListener_Remove(token_ReplChangeListener); // end::remove-document-replication-listener[] // tag::p2p-act-rep-status[] // Purpose -- illustrate use of CBLReplicator_Status() CBLReplicatorStatus thisState = CBLReplicator_Status(thisRepl); if(thisState.activity==kCBLReplicatorStopped) { if(thisState.error.code==0) { CBLReplicator_Start(thisRepl,false); } else { printf("Replicator stopped -- code %d", thisState.error.code); // ... handle error ... CBLReplicator_Release(thisRepl); } } // end::p2p-act-rep-status[] // tag::replication-pendingdocuments[] FLDict thisPendingIdList = CBLReplicator_PendingDocumentIDs(thisRepl, &err); (29) if(!FLDict_IsEmpty(thisPendingIdList)) { FLDictIterator item; FLDictIterator_Begin(thisPendingIdList, &item); FLValue itemValue; FLString pendingId; while(NULL != (itemValue = FLDictIterator_GetValue(&item))) { pendingId = FLValue_AsString(itemValue); if(CBLReplicator_IsDocumentPending(thisRepl, pendingId, &err)) { // ... process the still pending docid as required (30) } else { // Doc Id no longer pending if(err.code==0) { // No fail so must have already been pushed printf("Document already pushed"); } else { // Error detected so handle it printf("Error code %d checking for pendingId", err.code); break; } } FLDictIterator_Next(&item); } FLDictIterator_End(&item); FLValue_Release(itemValue); } else { printf("No Pending Id Docs to process"); } FLDict_Release(thisPendingIdList); // end::replication-pendingdocuments[] } // END replication.html >> Monitor section // BEGIN replication.html >> Stop section // PAGE=Data Sync >> Stop // URL=https://docs.couchbase.com/couchbase-lite/current/c/replication.html#lbl-repl-stop static void docs_act_replication_Stop( void* context, CBLReplicator* argRepl) { // tag::p2p-act-rep-stop[] // Purpose -- show how to stop a replication if(CBLReplicator_Status(argRepl).activity!=kCBLReplicatorStopped) { CBLReplicator_Stop(argRepl); } // end::p2p-act-rep-stop[] } // END replication.html >> Stop section static void replication_error_handling() { CBLDatabase* db = kDatabase; CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; CBLReplicator* replicator = CBLReplicator_Create(&config, &err); CBLEndpoint_Free(target); stop_replicator(replicator); } static void create_encryptable() { #ifdef COUCHBASE_ENTERPRISE // tag::encryptable[] // NOTE: No error handling, for brevity (see getting started) // Create with premitive type CBLEncryptable* encNull = CBLEncryptable_CreateWithNull(); CBLEncryptable* encBool = CBLEncryptable_CreateWithBool(true); CBLEncryptable* encInt = CBLEncryptable_CreateWithInt(256); CBLEncryptable* encUInt = CBLEncryptable_CreateWithUInt(1024); CBLEncryptable* encFloat = CBLEncryptable_CreateWithFloat(1.2); CBLEncryptable* encDouble = CBLEncryptable_CreateWithDouble(100.50); CBLEncryptable* encString = CBLEncryptable_CreateWithString(FLSTR("foo")); // Create with dictionary FLMutableDict dict = FLMutableDict_New(); FLSlot_SetString(FLMutableDict_Set(dict, FLSTR("greeting")), FLSTR("hello")); CBLEncryptable* encDict = CBLEncryptable_CreateWithDict(dict); // Create with array FLMutableArray array = FLMutableArray_New(); FLSlot_SetString(FLMutableArray_Append(array), FLSTR("item1")); CBLEncryptable* encArray = CBLEncryptable_CreateWithArray(array); // Create with FLValue FLMutableDict dict2 = FLMutableDict_New(); FLSlot_SetString(FLMutableDict_Set(dict2, FLSTR("greeting")), FLSTR("hello")); CBLEncryptable* encValue = CBLEncryptable_CreateWithValue((FLValue)dict2); // end::encryptable[] // Release after using it CBLEncryptable_Release(encNull); CBLEncryptable_Release(encBool); CBLEncryptable_Release(encInt); CBLEncryptable_Release(encBool); CBLEncryptable_Release(encUInt); CBLEncryptable_Release(encFloat); CBLEncryptable_Release(encDouble); CBLEncryptable_Release(encString); CBLEncryptable_Release(encDict); CBLEncryptable_Release(encArray); CBLEncryptable_Release(encValue); FLMutableDict_Release(dict); FLMutableDict_Release(dict2); FLMutableArray_Release(array); #endif } static void release_encryptable() { #ifdef COUCHBASE_ENTERPRISE CBLEncryptable* encValue = CBLEncryptable_CreateWithString(FLSTR("foo")); // tag::release_encryptable[] // Release the encryptable value after finish using it CBLEncryptable_Release(encValue); // end::release_encryptable[] #endif } static void use_encryptable() { #ifdef COUCHBASE_ENTERPRISE CBLDatabase* db = kDatabase; // tag::use_encryptable[] // NOTE: No error handling, for brevity (see getting started) CBLDocument* doc = CBLDocument_CreateWithID(FLSTR("doc1")); // Set encryptable: FLMutableDict props = CBLDocument_MutableProperties(doc); CBLEncryptable* encryptable = CBLEncryptable_CreateWithString(FLSTR("My secret")); FLSlot_SetEncryptableValue(FLMutableDict_Set(props, FLSTR("secret")), encryptable); CBLError error; CBLDatabase_SaveDocument(db, doc, &error); // Release CBLDocument_Release(doc); CBLEncryptable_Release(encryptable); // end::release_encryptable[] #endif } static void query_encryptable() { #ifdef COUCHBASE_ENTERPRISE CBLDatabase* db = kDatabase; // tag::use_encryptable[] // NOTE: No error handling, for brevity (see getting started) CBLError err; CBLQuery* query = CBLDatabase_CreateQuery(db, kCBLN1QLLanguage, FLSTR("SELECT secret, secret.value as secretValue FROM _ WHERE type = \"profile\""), NULL, &err); CBLResultSet* results = CBLQuery_Execute(query, &err); while(CBLResultSet_Next(results)) { // Get secret as CBLEncryptable value FLValue value = CBLResultSet_ValueForKey(results, FLSTR("secret")); const CBLEncryptable* encValue = FLValue_GetEncryptableValue(value); FLString secretStr = FLValue_AsString(CBLEncryptable_Value(encValue)); printf("Secret :: %.*s\n", (int)secretStr.size, (const char *)secretStr.buf); // Get secret value directly value = CBLResultSet_ValueForKey(results, FLSTR("secretValue")); secretStr = FLValue_AsString(value); printf("Secret :: %.*s\n", (int)secretStr.size, (const char *)secretStr.buf); } CBLResultSet_Release(results); CBLQuery_Release(query); // end::release_encryptable[] #endif } #ifdef COUCHBASE_ENTERPRISE // tag::replicator_property_encryption[] // tag::replicator_property_encryptor_decryptor_sample[] // Purpose: Declare property-level encryptor callback functions static FLSliceResult my_cipher_function(FLSlice input) { FLSliceResult result = FLSliceResult_New(input.size); for(int i = 0; i < input.size; ++i) { ((uint8_t*)(result.buf))[i] = ((uint8_t*)input.buf)[i] ^ 'K';} return result; } static FLSliceResult property_encryptor(void* context, FLString docID, FLDict props, FLString path, FLSlice input, FLStringResult* algorithm, FLStringResult* keyID, CBLError* error) { *algorithm = FLSlice_Copy(FLSTR("MyEnc")); return my_cipher_function(input); } static FLSliceResult property_decryptor(void* context, FLString documentID, FLDict properties, FLString keyPath, FLSlice input, FLString algorithm, FLString keyID, CBLError* error) { return my_cipher_function(input); } // end::replicator_property_encryptor_decryptor_sample[] // end::replicator_property_encryption[] #endif // PAGE=Field Level Encryption // URL=https://docs.couchbase.com/couchbase-lite/current/c/field-level-encryption.html // static void replicator_property_encryption() { #ifdef COUCHBASE_ENTERPRISE CBLDatabase* db = kDatabase; // tag::replicator_property_encryption[] // Purpose: Show how to declare en(de)cryptors in replicator config // NOTE: No error handling, for brevity (see getting started) CBLError err; FLString url = FLSTR("ws://localhost:4984/db"); CBLEndpoint* target = CBLEndpoint_CreateWithURL(url, &err); CBLReplicatorConfiguration config; memset(&config, 0, sizeof(CBLReplicatorConfiguration)); config.database = db; config.endpoint = target; config.propertyEncryptor = property_encryptor; (31) config.propertyDecryptor = property_decryptor; (32) CBLReplicator* replicator = CBLReplicator_Create(&config, &err); CBLEndpoint_Free(target); CBLReplicator_Start(replicator, false); // end::replicator_property_encryption[] stop_replicator(replicator); #endif } int main(int argc, char** argv) { create_new_database(); create_document(); update_document(); do_batch_operation(); // Disable use_blob() as no avatar.jpg to load and crash // use_blob(); doc_json(); dict_json(); array_json(); load_prebuilt(); create_index(); select_all(); select_and_access_all(); select_props(); select_meta(); select_id(); select_where(); use_collection_contains(); select_like(); select_wildcard_like(); select_wildcard_character_like(); select_regex(); select_join(); group_by(); order_by(); query_result_json(); create_full_text_index(); full_text_search(); start_replication(); replicator_property_encryption(); CBLDatabase_Close(kDatabase, NULL); return 0; }