Firebase: Cloud Firestore


SQL – NoSQL

  • Table based ⇒ Document based
  • Schema clearly defined ⇒ Flexible properties
  • Altering schema / back-filling is required ⇒ Add properties on the fly
  • Vertical scaling ⇒ Horizontal scaling
  • In-line memory caching ⇒ integrated caching

NoSQL types

  • Key/values: Simplest key value pairs
  • Document: each key bear a complex data structure known as document.
  • Column: Optimized for queries for large datasets and stores columns of data together instead of rows.
  • Graph: Stores networks of data such as social connections.

Firestore vs Real-time database

Firestore complements existing Real-time database and there is quite a bit of overlap but Cloud Firestore is future.

Real-time database

  • Stores data as one large JSON tree.
  • Has scalability issues, like 100,000 connections or 1000 writes per second.
  • Charges for bandwidth and storage (MB).
  • Scaling requires sharding.

Firestore

  • Stores data in documents organized in collections.
  • Charges for operations performed (database read/write/delete etc).
  • Scaling will be automatic.

Differences to consider

  • Shallow queries offer more flexibility in hierarchical data structures.
  • Complex queries offer more granularity and reduce the need for duplicate data.
  • Query cursors offer more robust pagination.
  • Transactions no longer require a common root for all your data, and are more efficient.
  • Firestore might be more expensive than Realtime, particularly if you rely on many small operations.

Benefits

  • NoSQL
  • Realtime updates
  • Expressive querying
  • Offline support (even for web)
  • Designed to scale
  • Comprehensive security rules
  • Data browsing tool
  • Multi-region data replication

Ways of storing data

  • Key-value store
  • A single big nested JSON tree
  • Collection of JSON objects

Caveats

  • Schema-less approach requires defensive coding
  • Cannot rename a collection or any fields inside a document
  • Once you select the location for your project, you cannot change it.
  • Local writes in your app will invoke snapshopt listeners immediately. You can add a param to determine whether the data is local.

Things to note

  • Deleting document does NOT delete the nested data of the document.
  • Document can not be larger than 1MB.
  • Collection/Document/Collection/Document … up to 100 levels deep.

Approach

  • Keep all the data in a document to populate one screen (no need to join/fetch)
  • Put relevant data in a single place (e.g User info inside Review)

Rationale

  • Reading data is outnumbered by writing data in most cases (like, 7000: 1)
  • Reading data is super easy as all the required data in one place
  • Horizontally scalable.

Structure

  • Collection/Document/Collection/Document….
  • Document can not contain other document
  • Queries are shallow, won’t fetch the subcollections

How to work

  • Store the path of the document you want: user/user_123/reviews/review_1
  • Duplicate only necessary info, like only user name and profile pic in each review

Query

  • Call a method or set a listener
  • You can set event listeners to single document, multiple documents, or collections
  • Event listeners can be for “added”, “edited”, “deleted” or “any”
  • Simple and compound query both are supported.
  • orderBy supported
  • Limit data supported for pagination

Use cases#1

Suppose our database structure is like,
Restaurants/Restaurant_1/Reviews/Review_1

Query can be done only in collection or in sub-collections, not across the collections

  • All restaurants in a zip code (can be done)
  • All 4 star reviews of a restaurant (can be done)
  • All 4 star reviews of all restaurants (can not be done)

The reason is, it spans across multiple sub-collections and it’s called Collection Group Query (not supported right now)

Use cases#2

Greater than, less than, equal queries are there, but calculated value can’t be queried, like Avg etc.

Use cases#3

The time it takes to run a query is proportional to number of documents returned, not the number of documents you are searching through.
For example, if you are running a query to get 5 restaurants, it will same time if the database contains 60 restaurants or 600k restaurants

Use case#4

You can’t do, “Find name like %Tar%”, cause it’s not indexed
No pattern searching
No regex searching
No OR queries, like, Name = “Joy” or Name = “Bil”

Data types

Integer
String
Boolean
Date & time
Double
Array
Bytes
Geographical point
Map
Null
Reference

Create, update, delete

To replace a document, call set method
Add merge: true if you want to append, instead of replace in set method.
Transaction and Batch write are supported.

Transaction

Transaction is a set of read and write operations on or more documents.
Keep in mind,

  • Read operations must come before write operations
  • Transaction functions should not directly modify application state
  • Transactions will fail when the client is offline.

Batch write

A Batched write is set of up to 500 write operations on one or more documents.

Security

  • Firebase provides authentication and Firestore security rules provides authorization
  • Use regex like security rules started with match
  • You will also get a simulator to try out rules
match /{collectionName}/{document} {
allow read: if [condition];
allow write: if [condition];

// granular rules
allow get: if [condition]; // applies to single document read requests
allow list: if [condition] // applies to queries and collection read requests

	// nested rules
	match/{subCollection}/{document} {
	}
}

Use case#1

Able to read if you created it,

match /projects/{project}/issues/{issue}/attachments/{attachment} {
	allow read: if request.auth.uid == resource.data.user_id; // user_id is a field
}

Use case#2

Able to read by all but updated or deleted by the creator

match /projects/{project}/issues/{issue}{
	allow read: if request.auth != null;
	allow write: if request.auth.uid == resource.data.reporter; // reproter is a field
}

Use case#3

Allow user to read data if document visibility is set to public

match /cities/{city} {
	allow read: if resource.data.visibility == 'public';
}

Use case#4

Query the user document and get the current user id and if the admin property is true then s/he is able to write,

match /projects/{project}/issues/{issue}{
	allow write: if get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
}

Index

  • Firestore indexes every field in the document and even for maps.
  • It takes time to write / update each document cause it requires to update a lot of indexes.
  • But query or read will be super fast, it’s impossible to run a slow query in Firestore.
  • If you run compound query, it will fail and Firebase provides you a link in console to create compound index.

For example,

citiesRef.whereEqualTo("state", "CO").whereEqualTo("name", "Denver");

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.