The Power of SQL and JSON’s Flexibility

Get sub-millisecond response from Couchbase’s in-memory JSON database without sacrificing the best capabilities of relational.

Are your legacy databases holding up application development and slowing down performance?
With Couchbase you can:

  • Use SQL for familiar query access
  • Store data as JSON and avoid a restrictive relational data model
  • SQL and JSON allow you to build new features using our built-in multi-model services, including full-text search, analytics, eventing, and key-value

Check out the code

Java - Upsert
                                      import com.couchbase.client.core.error.subdoc.PathNotFoundException;
import com.couchbase.client.java.*;
import com.couchbase.client.java.kv.*;
import com.couchbase.client.java.kv.MutationResult;
import com.couchbase.client.java.json.JsonObject;
import com.couchbase.client.java.kv.LookupInResult;
import static com.couchbase.client.java.kv.LookupInSpec.get;
import static com.couchbase.client.java.kv.MutateInSpec.upsert;
import java.util.Collections;

class Program {
  public static void main(String[] args) {
    var cluster = Cluster.connect(
      "couchbase://127.0.0.1", "username", "password"
    );
    var bucket = cluster.bucket("travel-sample");
    var collection = bucket.defaultCollection();

    JsonObject content = JsonObject.create()
      .put("country", "Iceland")
      .put("callsign", "ICEAIR")
      .put("iata", "FI")
      .put("icao", "ICE")
      .put("id", 123)
      .put("name", "Icelandair")
      .put("type", "airline");

    collection.upsert("airline_123", content);

    try {
      LookupInResult lookupResult = collection.lookupIn(
        "airline_123", Collections.singletonList(get("name"))
      );

      var str = lookupResult.contentAs(0, String.class);
      System.out.println("New Document name = " + str);

    } catch (PathNotFoundException ex) {
      System.out.println("Document not found!");
    }

  }
}
NodeJS - Upsert
                                      const couchbase = require('couchbase')

const main = async () => {
  const cluster = await couchbase.connect('couchbase://127.0.0.1', {
    username: 'username', password: 'password'
  })

  const bucket = cluster.bucket('travel-sample')
  const collection = bucket.defaultCollection()

  const airline = {
    country: 'Iceland', callsign: 'ICEAIR',
    iata: 'FI', icao: 'ICE', id: 123,
    name: 'Icelandair', type: 'airline',
  }

  const upsertDocument = async (type, id, doc) => {
    try {
      const upsertResult = await collection.upsert(`${type}_${id}`, doc);
      console.log('Upsert Result: ')
      console.log(upsertResult)
    } catch (err) {
      console.error(err)
    }
  }

  const getSubDocument = async (key, field) => {
    try {
      var result = await collection.lookupIn(key, [
        couchbase.LookupInSpec.get(field),
      ])
      var fieldValue = result.content[0].value

      console.log('LookupIn Result: ')
      console.log(result)

      console.log('Field Value: ')
      console.log(fieldValue)
    } catch (error) {
      console.error(error)
    }
  }

  upsertDocument(airline.type, airline.id, airline)
    .then(
      getSubDocument('airline_123', 'name')
        .then(() => process.exit(0))
    )
}

main()
Python - Upsert
                                      #!/usr/bin/python3
import sys
import couchbase.collection
import couchbase.subdocument as SD
from couchbase.cluster import Cluster, ClusterOptions
from couchbase.auth import PasswordAuthenticator
from couchbase.durability import ServerDurability, Durability
from datetime import timedelta

pa = PasswordAuthenticator('username', 'password')
cluster = Cluster('couchbase://127.0.0.1', ClusterOptions(pa))
bucket = cluster.bucket('travel-sample')
collection = bucket.default_collection()

try:
  document = dict(
    country="Iceland", callsign="ICEAIR", iata="FI", icao="ICE",
    id=123, name="Icelandair", type="airline"
  )
  result = collection.upsert(
    'airline_123',
    document,
    expiry=timedelta(minutes=1)
  )
  print("UPSERT SUCCESS")
  print("cas result:", result.cas)
except:
  print("exception:", sys.exc_info())

try:
  result = collection.lookup_in('airline_123', [SD.get('name')])
  name = result.content_as[str](0) # "United Kingdom"
  print("name:", name)

except:
  print("exception:", sys.exc_info()[0])
.NET - Upsert
                                      using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Couchbase;
using Couchbase.Core.Exceptions.KeyValue;
using Couchbase.KeyValue;

namespace CouchbaseDotNetExample
{
  class Program
  {
    static async Task Main(string[] args)
    {
      var cluster = await Cluster.ConnectAsync(
        "couchbase://127.0.0.1", "username", "password"
      );

      var bucket = await cluster.BucketAsync("travel-sample");
      var scope = await bucket.ScopeAsync("_default");
      var collection = await scope.CollectionAsync("_default");

      var content = new
        {
          Country = "Iceland",
          Callsign = "ICEAIR",
          Iata = "FI",
          Icao = "ICE",
          Id = 123,
          Name = "Icelandair",
          Type = "airline"
        };

      await collection.UpsertAsync("airline_123", content);

      try
      {
        var lookupResult = await collection.LookupInAsync("airline_123",
          new List<LookupInSpec>
            {
              LookupInSpec.Get("name")
            });

        var name = lookupResult.ContentAs<string>(0);
        Console.WriteLine($"New Document name = {name}");
      }
      catch (PathNotFoundException)
      {
        Console.WriteLine("Document not found!");
      }

      await cluster.DisposeAsync();
    }
  }
}
PHP - Upsert
                                      <?php
  $connectionString = "couchbase://127.0.0.1";
  $options = new \Couchbase\ClusterOptions();
  $options->credentials("username", "password");
  $cluster = new \Couchbase\Cluster($connectionString, $options);

  $bucket = $cluster->bucket("travel-sample");
  $collection = $bucket->defaultCollection();

  $content = ["country" => "Iceland",
              "callsign" => "ICEAIR",
              "iata" => "FI",
              "icao" => "ICE",
              "id" => 123,
              "name" => "Icelandair",
              "type" => "airline"];

  $collection->upsert("airline_123", $content);
  try {
    $result = $collection->lookupIn("airline_123", [new \Couchbase\LookupGetSpec("name")]);
    $name = $result->content(0);
    print("New Document name = $name");
  } catch (\Couchbase\PathNotFoundException $pnfe) {
    print("Sub-doc path not found!");
  } catch (\Couchbase\BaseException $ex) {
    print("Exception $ex\n");
  }
?>
Ruby - Upsert
                                      require "couchbase"
include Couchbase

options = Cluster::ClusterOptions.new
options.authenticate("username", "password")
cluster = Cluster.connect("couchbase://127.0.0.1", options)

bucket = cluster.bucket("travel-sample")
collection = bucket.default_collection

begin
  content = {"country" => "Iceland",
             "callsign" => "ICEAIR",
             "iata" => "FI",
             "icao" => "ICE",
             "id" => 123,
             "name" => "Icelandair",
             "type" => "airline"}
  collection.upsert("airline_123", content)
  result = collection.lookup_in("airline_123", [ LookupInSpec.get("name")])
  puts "New Document name: #{result.content(0)}"
rescue Couchbase::Error::PathNotFound => pnfe
  puts "Sub-doc path not found!"
rescue Couchbase::Error::DocumentNotFound => ex
  puts "Document not found!"
end
Scala - Upsert
                                      package com.couchbase

import com.couchbase.client.core.error.{CouchbaseException, DocumentNotFoundException}
import com.couchbase.client.core.error.subdoc.{PathExistsException,PathNotFoundException}
import com.couchbase.client.scala.Cluster
import com.couchbase.client.scala.json.{JsonObject, JsonObjectSafe}
import com.couchbase.client.scala.kv.LookupInSpec._
import com.couchbase.client.scala.kv.{LookupInResult, _}
import scala.util.{Failure, Success, Try}

object Program extends App {
  val cluster = Cluster.connect("127.0.0.1", "username", "password").get
  var bucket = cluster.bucket("travel-sample");
  val collection = bucket.defaultCollection

  val content = JsonObject("country" -> "Iceland",
                "callsign"-> "ICEAIR",
                "iata" -> "FI",
                "icao" -> "ICE",
                "id" -> 123,
                "name" -> "Icelandair",
                "type" -> "airline")

  collection.upsert("airline_123", content) match {
    case Success(result)    =>
    case Failure(exception) => println("Error: " + exception)
  }

  val result = collection.lookupIn("airline_123", Array(get("name")))
  result match {
    case Success(r) =>
      val str: Try[String] = r.contentAs[String](0)
      str match {
        case Success(s)   => println(s"New document name: ${s}")
        case Failure(err) => println(s"Error: ${err}")
      }
    case Failure(err: DocumentNotFoundException) => println("Document not found")
    case Failure(err: PathNotFoundException) => println("Sub-doc path not found!")
    case Failure(err: CouchbaseException) => println("Couchbase error: " + err)
    case Failure(err) => println("Error getting document: " + err)
  }
}
Golang - Upsert
                                      package main

import (
  "fmt"
  "log"
  "time"

  "github.com/couchbase/gocb/v2"
)

func main() {
  cluster, err := gocb.Connect("couchbase://127.0.0.1", gocb.ClusterOptions{
    Authenticator: gocb.PasswordAuthenticator{
      Username: "username",
      Password: "password",
    },
  })
  if err != nil {
    log.Fatal(err)
  }

  bucket := cluster.Bucket("travel-sample")
  err = bucket.WaitUntilReady(5*time.Second, nil)
  if err != nil {
    log.Fatal(err)
  }

  collection := bucket.DefaultCollection()

  type Doc struct {
    Id int `json:"id"`
    Type string `json:"type"`
    Name string `json:"name"`
    Iata string `json:"iata"`
    Icao string `json:"icao"`
    Callsign string `json:"callsign"`
    Country string `json:"country"`
  }

  document := Doc {
    Country: "Iceland",
    Callsign: "ICEAIR",
    Iata: "FI",
    Icao: "ICE",
    Id: 123,
    Name: "Icelandair",
    Type: "airline",
  }

  _, err = collection.Upsert("airline_123", &document, nil)
  if err != nil {
    log.Fatal(err)
  }

  options := []gocb.LookupInSpec{
    gocb.GetSpec("name", &gocb.GetSpecOptions{}),
  }
  results, err := collection.LookupIn("airline_123", options, &gocb.LookupInOptions{})
  if err != nil {
    log.Fatal(err)
  }

  var country string
  err = results.ContentAt(0, &country)
  if err != nil {
    log.Fatal(err)
  }

  fmt.Printf("New document name: %s\n", country)
}
C++ - Upsert
                                      #include <vector>
#include <string>
#include <iostream>

#include <libcouchbase/couchbase.h>

static void
check(lcb_STATUS err, const char* msg)
{
    if (err != LCB_SUCCESS) {
        std::cerr << "[ERROR] " << msg << ": " << lcb_strerror_short(err) << "\n";
        exit(EXIT_FAILURE);
    }
}

struct Result {
    std::string value{};
    lcb_STATUS status{ LCB_SUCCESS };
};

struct SubdocResults {
    lcb_STATUS status{ LCB_SUCCESS };
    std::vector<Result> entries{};
};

static void
sdget_callback(lcb_INSTANCE*, int, const lcb_RESPSUBDOC* resp)
{
    SubdocResults* results = nullptr;
    lcb_respsubdoc_cookie(resp, reinterpret_cast<void**>(&results));
    results->status = lcb_respsubdoc_status(resp);

    if (results->status != LCB_SUCCESS) {
        return;
    }

    std::size_t number_of_results = lcb_respsubdoc_result_size(resp);
    results->entries.resize(number_of_results);
    for (size_t idx = 0; idx < number_of_results; ++idx) {
        results->entries[idx].status = lcb_respsubdoc_result_status(resp, idx);
        const char* buf = nullptr;
        std::size_t buf_len = 0;
        lcb_respsubdoc_result_value(resp, idx, &buf, &buf_len);
        if (buf_len > 0) {
            results->entries[idx].value.assign(buf, buf_len);
        }
    }
}

int
main()
{
    std::string username{ "username" };
    std::string password{ "password" };
    std::string connection_string{ "couchbase://127.0.0.1" };
    std::string bucket_name{ "travel-sample" };

    lcb_CREATEOPTS* create_options = nullptr;
    check(lcb_createopts_create(&create_options, LCB_TYPE_BUCKET), "build options object for lcb_create");
    check(lcb_createopts_credentials(create_options, username.c_str(), username.size(), password.c_str(), password.size()),
        "assign credentials");
    check(lcb_createopts_connstr(create_options, connection_string.c_str(), connection_string.size()), "assign connection string");
    check(lcb_createopts_bucket(create_options, bucket_name.c_str(), bucket_name.size()), "assign bucket name");

    lcb_INSTANCE* instance = nullptr;
    check(lcb_create(&instance, create_options), "create lcb_INSTANCE");
    check(lcb_createopts_destroy(create_options), "destroy options object");
    check(lcb_connect(instance), "schedule connection");
    check(lcb_wait(instance, LCB_WAIT_DEFAULT), "wait for connection");
    check(lcb_get_bootstrap_status(instance), "check bootstrap status");

    std::string key{ "airline_123" };
    {
      std::string value{ R"({"country":"Iceland", "callsign":"ICEAIR", "iata":"FI", "icao":"ICE", "id":123, "name":"Icelandair", "type":"airline"})" };

      lcb_CMDSTORE* cmd = nullptr;
      check(lcb_cmdstore_create(&cmd, LCB_STORE_UPSERT), "create UPSERT command");
      check(lcb_cmdstore_key(cmd, key.c_str(), key.size()), "assign ID for UPSERT command");
      check(lcb_cmdstore_value(cmd, value.c_str(), value.size()), "assign value for UPSERT command");
      check(lcb_store(instance, nullptr, cmd), "schedule UPSERT command");
      check(lcb_cmdstore_destroy(cmd), "destroy UPSERT command");
      lcb_wait(instance, LCB_WAIT_DEFAULT);
    }

    lcb_install_callback(instance, LCB_CALLBACK_SDLOOKUP, reinterpret_cast<lcb_RESPCALLBACK>(sdget_callback));

    {
        SubdocResults results;

        lcb_SUBDOCSPECS* specs = nullptr;
        check(lcb_subdocspecs_create(&specs, 1), "create SUBDOC operations container");

        std::vector<std::string> paths{
            "name",
        };

        check(lcb_subdocspecs_get(specs, 0, 0, paths[0].c_str(), paths[0].size()), "create SUBDOC-GET operation");

        lcb_CMDSUBDOC* cmd = nullptr;
        check(lcb_cmdsubdoc_create(&cmd), "create SUBDOC command");
        check(lcb_cmdsubdoc_key(cmd, key.c_str(), key.size()), "assign ID to SUBDOC command");
        check(lcb_cmdsubdoc_specs(cmd, specs), "assign operations to SUBDOC command");
        check(lcb_subdoc(instance, &results, cmd), "schedule SUBDOC command");
        check(lcb_cmdsubdoc_destroy(cmd), "destroy SUBDOC command");
        check(lcb_subdocspecs_destroy(specs), "destroy SUBDOC operations");

        lcb_wait(instance, LCB_WAIT_DEFAULT);

        check(results.status, "status of SUBDOC operation");
        std::size_t idx = 0;
        for (const auto& entry : results.entries) {
            if (entry.status == LCB_SUCCESS) {
                std::cout << "New Document name: " << (entry.value.empty() ? "(no value)" : entry.value) << "\n";
            } else {
                std::cout << "code=" << lcb_strerror_short(entry.status) << "\n";
            }
            ++idx;
        }
    }

    lcb_destroy(instance);
    return 0;
}

Stay ahead of tomorrow’s database challenges

Challenge 1

Rigid data models slow down dev cycles

Legacy databases

  • Rigid data models make agile development difficult
  • Application objects are split into tables and need to be reassembled

Couchbase

  • JSON data supports agile development through flexible data models
  • Application objects can be directly serialized or deserialized

Challenge 2

Apps perform poorly

Legacy databases

  • Relational modeling often requires a high number of writes and reads
  • No built-in managed cache available

Couchbase

  • Caching delivers highly responsive user experiences and lower latency
  • Built-in managed cache provides sub-millisecond latency

Challenge 3

New functionality requires new tools

Legacy databases

  • Bolted on tools and time-consuming integrations are required to handle caching, analytics, eventing, and mobile syncing
  • Data access is limited to only SQL
  • More bolt-ons equal more work and more maintenance

Couchbase

  • Delivers the familiarity of SQL (with SQL++)
  • Provides multi-model capabilities (full-text search, query, key/value, analytics, and more, all on the same data set)
  • Automatically syncs your mobile and edge data in real time

Challenge 4

It’s hard to scale deployments with HA

Legacy databases

  • Optimized for single server deployments and expensive vertical scaling
  • Horizontal scaling is expensive and difficult
  • Upgrades and maintenance equal downtime equal 2 a.m. weekend office hours

Couchbase

  • Core distributed data architecture
  • Masterless, shared-nothing automatic horizontal scaling
  • No shard or partition keys to manage
  • Automatic replication
  • Automatic failover recovery and rebalancing
  • Maintenance and upgrades don't require downtime