A document is a JSON object that contains a number of key-value pairs. Entities, and relationships between entities, are managed within the document itself.
If you’re familiar with relational database, you’ll find that Couchbase Lite works differently and has its own database terminologies.
You can download Couchbase Lite library for Android at http://www.couchbase.com/nosql-databases/downloads#Couchbase_Mobile
Or add below line to graddle build file in Android Studio:
dependencies { compile 'com.couchbase.lite:couchbase-lite-android:1.0.3-4' }
Add the following lines to the top-level allprojects/repositories section:
allprojects { repositories { jcenter() mavenCentral() maven { url "http://files.couchbase.com/maven2/" } } }
Manager is the top-level object used to manage a collection of Database instances. You must create a Manager object in order to work with Couchbase Lite in your application.
To create a Manager object, we just need to call the constructor:
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); try { mManager = new Manager(new AndroidContext(getApplicationContext()), Manager.DEFAULT_OPTIONS); } catch (IOException ex) { Log.e("MainActivity", "Cannot create Manager instance", ex); return; } }
By default, the Manager will open a Database with read and write permission. If you want to modify the permission, you can pass a ManagerOptions object to the constructor:
ManagerOptions managerOptions = new ManagerOptions(); managerOptions.setReadOnly(true); mManager = new Manager(new AndroidContext(getApplicationContext()), managerOptions);
Database is a container for documents, a scope for queries and the source and target for replications.
Remember that Database is not a table! Couchbase Lite doesn’t have any term that equal to table in relational database.
A database has following elements:
_$()+-/
We will talk about each elements in more details later.
You can create an empty database by just open it:
try { mDatabase = mManager.getDatabase("icetea09-database"); } catch (CouchbaseLiteException e) { e.printStackTrace(); }
To delete your database and all its attachments permanently, you have to call delete method.
After deleted database, you should assign null value to the Database reference.
@Override protected void onDestroy() { try { mDatabase.delete(); } catch (CouchbaseLiteException e) { Log.e("MainActivity", "Cannot delete database", e); return; } super.onDestroy(); }
In document database, document is the primary entity stored in database - not row or record.
In Couchbase Lite, document has following elements:
To demonstrate for the CRUD operation in Couchbase Lite, I’ll create a simple form allow user to update their contact information:
The following code will show you how to create a document with assigned UUID. If you didn’t assign any UUID to the document, the ID will be generated randomly.
private void saveContactInfo(String firstName, String lastName, String phoneNumber) { //Create new document with random UUID //Document document = mDatabase.getDocument(CONTACT_INFO_DOCUMENT_ID); Document document = mDatabase.getDocument(CONTACT_INFO_DOCUMENT_ID); Map<String, Object> properties = new HashMap(); properties.put(FIELD_FIRST_NAME, firstName); properties.put(FIELD_LAST_NAME, lastName); properties.put(FIELD_PHONE_NUMBER, phoneNumber); try { document.putProperties(properties); } catch (CouchbaseLiteException e) { Log.e(TAG, "Cannot save document", e); } try { document.putProperties(properties); } catch (CouchbaseLiteException e) { Log.e(TAG, "Cannot save document", e); } }
In order to retrieve a document, you have to know its UUID:
private ContactInfo getContactInfo() { try{ Document doc = mDatabase.getDocument(CONTACT_INFO_DOCUMENT_ID); String firstName = doc.getProperty(FIELD_FIRST_NAME).toString(); String lastName = doc.getProperty(FIELD_LAST_NAME).toString(); String phoneNumber = doc.getProperty(FIELD_PHONE_NUMBER).toString(); return new ContactInfo(firstName, lastName, phoneNumber); } catch (Exception ex){ Log.e(TAG, "Cannot get contact info", ex); } return null; }
Please refer to the “Create document” section, cause Update document is almost the same code and you must have the document UUID to be able to update its content.
private boolean updateContactInfo(String firstName, String lastName, String phoneNumber) { Document document = mDatabase.getDocument(CONTACT_INFO_DOCUMENT_ID); Map<String, Object> properties = new HashMap(); properties.putAll(document.getProperties()); properties.put(FIELD_FIRST_NAME, firstName); properties.put(FIELD_LAST_NAME, lastName); properties.put(FIELD_PHONE_NUMBER, phoneNumber); try { document.putProperties(properties); return true; } catch (CouchbaseLiteException e) { Log.e(TAG, "Cannot save document", e); return false; } }
The only different is
properties.putAll(document.getProperties());
This line allow you to update the document with the same _rev. Or else, your updating will be rejected. Please find more details on 5. Revision.
private void deleteContactInfo() { Document document = mDatabase.getDocument(CONTACT_INFO_DOCUMENT_ID); try { document.delete(); } catch (CouchbaseLiteException e) { Log.e(TAG, "Cannot delete document", e); } }
To download the source code for this demo, please refer Download section at the bottom of this post.
Every Document has a property call _rev. It is the revision ID and will be generated automatically every time you update document value.
Every time you want to update document value, you have to pass the correct current _rev. Or else, the updating will be rejected.
That was demonstrated in the updating code:
properties.putAll(document.getProperties());
By putAll old properties values, you got the current _rev of the document.
As you know, Attachment is a part of Document but its not a part of document body (the JSON object).
The reason is to allow storing large binary data files in document and not affecting the JSON parsing performance. Cause attachment will be loaded on demand not when loading JSON body.
A document can have many attachments identified by different names. Each attachment has been tagged by its MIME type that helps the application interpret its content.
To demonstrate for creating, updating and retrieving attachments, we add one more ImageView into the screen to display user profile picture.
To create an Attachment, first create an UnsavedRevision, set Attachment content and finally save it. Update will be the same:
private void saveAvatar() { Document doc = mDatabase.getDocument(CONTACT_INFO_DOCUMENT_ID); ByteArrayOutputStream bos = new ByteArrayOutputStream(); Bitmap bitmap = ((BitmapDrawable) mImgAvatar.getDrawable()).getBitmap(); bitmap.compress(Bitmap.CompressFormat.PNG, 0 /*ignored for PNG*/, bos); byte[] bitmapdata = bos.toByteArray(); ByteArrayInputStream bs = new ByteArrayInputStream(bitmapdata); try { UnsavedRevision newRev = doc.getCurrentRevision().createRevision(); newRev.setAttachment("avatar.jpg", "image/jpeg", bs); newRev.save(); } catch (CouchbaseLiteException e) { Log.e(TAG, "Cannot save attachment", e); } }
Once you already had an attachment, you can access it via its name:
private Bitmap getAvatar() { Document doc = mDatabase.getDocument(CONTACT_INFO_DOCUMENT_ID); Revision rev = doc.getCurrentRevision(); Attachment att = rev.getAttachment("avatar.jpg"); if (att != null) { try { InputStream is = att.getContent(); BufferedInputStream bif = new BufferedInputStream(is); return BitmapFactory.decodeStream(bif); } catch (CouchbaseLiteException e) { Log.e(TAG, "Cannot load attachment", e); } return null; } return null; }
To delete an attachment, just call removeAttachment:
private void deleteAvatar(){ Document doc = mDatabase.getDocument(CONTACT_INFO_DOCUMENT_ID); try{ UnsavedRevision newRev = doc.getCurrentRevision().createRevision(); newRev.removeAttachment(AVATAR_JPG); newRev.save(); } catch (CouchbaseLiteException e){ Log.e(TAG, "Cannot delete attachment", e); } }
A View is a persistent index of documents in a database, generated using map/reduce, which you then query to find data.
To create a View object, we need to call the getView() method of Database object. In case not existing, the database will create new one with the input name.
And before query on the View, you have to register the mapping function for it:
View mPhoneView = mDatabase.getView(VIEW_PHONE); mPhoneView.setMap(new Mapper() { @Override public void map(Map<String, Object> document, Emitter emitter) { List<String> phones = (List) document.get(FirstDemoActivity.FIELD_PHONE_NUMBER); for (String phone : phones) { emitter.emit(phone, document.get(FirstDemoActivity.FIELD_FIRST_NAME) + " " + document.get(FirstDemoActivity.FIELD_LAST_NAME)); } } }, "1"); }
In the setMap() method, you can see that we have 2 parameter:
Reduce funtion recieves the indexed key/value pairs of mapping function and processse, aggregates them to one single object.
Reduce funtion is optional and not commonly used. It usually used to count, calulate the sum or average of the result.
mPhoneView.setMapReduce(new Mapper() { @Override public void map(Map<String, Object> document, Emitter emitter) { List<String> phones = (List) document.get(FirstDemoActivity.FIELD_PHONE_NUMBER); for (String phone : phones) { emitter.emit(phone, document.get(FirstDemoActivity.FIELD_FIRST_NAME) + " " + document.get(FirstDemoActivity.FIELD_LAST_NAME)); } } }, new Reducer() { @Override public Object reduce(List<Object> keys, List<Object> values, boolean rereduce) { return values.size(); } }, "1");
Now you have View already, and to retrieve result from View you must know about Query.
To create a Query object, you can call getQuery() method of a View. By default, that query will return all rows in view in increasing order by key.
But you can change this by some common properties like:
startKey
: the key to start at. The default value, null
, means to start from the beginning.endKey
: the last key to return. The default value, null
, means to continue to the end.descending
: If set to true
, the keys will be returned in reverse order. (This also reverses the meanings of thestartKey
and endKey
properties, since the query will now start at the highest keys and end at lower ones!)limit
: If nonzero, this is the maximum number of rows that will be returned.skip
: If nonzero, this many rows will be skipped (starting from the startKey
if any.)mQuery = mDatabase.getView(VIEW_PHONE).createQuery(); mQuery.setDescending(true); mQuery.setLimit(20);
After created a Query object and customized its properties, you can run it.
The result is a QueryEnumerator
, which provides a list of QueryRow
objects, each one describing one row from the view’s index.
try{ QueryEnumerator result = mQuery.run(); for (Iterator<QueryRow> it = result; it.hasNext(); ) { QueryRow row = it.next(); Log.w("MYAPP", "Widget named %s costs $%f", row.getKey(), ((Double) row.getValue()).doubleValue()); } } catch (CouchbaseLiteException e){ e.printStackTrace(); }
That’s it!
That’s all I want to share with you in this post about Couchbase Lite.
I hope that this post can give you the general view about Couchbase Lite and how to use it to store data in your application.
You can find more details about source code in the following link:
https://github.com/trinhlbk1991/DemoCouchbaseLite
Comments powered by Disqus.