{"id":21656983,"url":"https://github.com/cotag/libcouchbase","last_synced_at":"2025-04-11T22:12:37.361Z","repository":{"id":34312308,"uuid":"38229547","full_name":"cotag/libcouchbase","owner":"cotag","description":"libcouchbase ruby FFI with libuv integration","archived":false,"fork":false,"pushed_at":"2024-08-07T09:26:23.000Z","size":349,"stargazers_count":11,"open_issues_count":12,"forks_count":8,"subscribers_count":4,"default_branch":"master","last_synced_at":"2025-04-11T22:12:32.748Z","etag":null,"topics":["adapter","couchbase","database","libcouchbase","ruby"],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"other","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/cotag.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-06-29T05:34:36.000Z","updated_at":"2021-01-04T10:11:13.000Z","dependencies_parsed_at":"2022-09-14T03:41:09.489Z","dependency_job_id":null,"html_url":"https://github.com/cotag/libcouchbase","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cotag%2Flibcouchbase","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cotag%2Flibcouchbase/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cotag%2Flibcouchbase/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/cotag%2Flibcouchbase/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/cotag","download_url":"https://codeload.github.com/cotag/libcouchbase/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248487683,"owners_count":21112190,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["adapter","couchbase","database","libcouchbase","ruby"],"created_at":"2024-11-25T09:18:35.962Z","updated_at":"2025-04-11T22:12:37.329Z","avatar_url":"https://github.com/cotag.png","language":"Ruby","readme":"# libcouchbase FFI bindings for Ruby\n\n[![Build Status](https://secure.travis-ci.org/cotag/libcouchbase.svg)](http://travis-ci.org/cotag/libcouchbase)\n\nAn alternative to the official [couchbase-client](https://github.com/couchbase/couchbase-ruby-client)\n\n* This client is non-blocking where possible using Fibers, which makes it simple to write performant code in Frameworks like [Rails](http://rubyonrails.org/).\n* Client is threadsafe and reentrant\n\nThis is a low level wrapper around libcouchbase. For a more friendly ActiveModel interface see [couchbase-orm](https://github.com/acaprojects/couchbase-orm)\n\n## Couchbase 5 Changes\n\nThe Couchbase 5 Admin Console blows away flags on documents if you edit them in the interface.\nFlags were being used to store document formats, however these were mainly implemented for compatibility with the defunct official client.\n\n\nTo prevent this being an issue we've made the following changes from version 1.2 of this library:\n\n1. All writes will result in valid JSON being saved to the database\n   * No more `raw strings` they will be saved as `\"raw strings\"`\n   * Existing raw strings will still be read correctly\n2. Since there are no more raw strings, append / prepend are no longer needed (not that we ever used them)\n\n\n## Runtime Support:\n\n* Native Ruby\n  * Blocks the current thread while performing operations\n  * Multiple operations can occur simultaneously on different threads\n  * For [Rails](http://rubyonrails.org/) and similar, this has optimal performance when running on [Puma](http://puma.io/)\n* [EventMachine](https://github.com/eventmachine/eventmachine)\n  * Requires the use of [em-synchrony](https://github.com/igrigorik/em-synchrony) or for the EM run block to be [wrapped by a fiber](https://github.com/igrigorik/em-http-request/blob/master/examples/fibered-http.rb#L27)\n  * When running [Rails](http://rubyonrails.org/) you'll have best results with [Thin](https://github.com/macournoyer/thin) and [Rack Fiber Pool](https://github.com/alebsack/rack-fiber_pool)\n  * Requests block the current Fiber, yielding so the reactor loop is not blocked\n* [Libuv](https://github.com/cotag/libuv)\n  * When running [Rails](http://rubyonrails.org/) you'll have best results with [SpiderGazelle](https://github.com/cotag/spider-gazelle)\n  * Requests block the current Fiber, yielding so the reactor loop is not blocked\n\nSyntax is the same across all runtimes and you can perform multiple operations simultaneously then wait for the results of those operations.\n\nOperations are also aware of the context they are being executed in.\nFor instance if you perform a request in an EventMachine thread pool, it will execute as Native Ruby and on the event loop it'll be non-blocking.\n\n\n## Installation\n\nThis GEM includes the [libcouchbase c-library](https://github.com/couchbase/libcouchbase) with requires [cmake](https://cmake.org/) for the build process.\nThe library is built on installation.\n\n* Ensure [cmake](https://cmake.org/install/) is installed\n* Run `gem install libcouchbase`\n\n\nThe library is designed to run anywhere [Rails](http://rubyonrails.org/) runs:\n\n* Ruby 2.2+\n* JRuby 9.1+\n* Rubinius 3.76+\n\n\nTested on the following Operating Systems:\n\n* OSX / MacOS\n* Linux\n* Windows\n  * Ruby x64 2.4+ with MSYS2 DevKit\n\n\n## Usage\n\nFirst, you need to load the library:\n\n```ruby\nrequire 'libcouchbase'\n```\n\nThe client will automatically adjust configuration when the cluster rebalances its nodes when nodes are added or deleted therefore this client is \"smart\".\nBy default the client will connect to the default bucket on localhost.\n\n```ruby\nbucket = Libcouchbase::Bucket.new\n```\n\nTo connect to other buckets, other than the default\n\n```ruby\n# Same as Libcouchbase::Bucket.new\nbucket = Libcouchbase::Bucket.new(hosts: '127.0.0.1', bucket: 'default', password: nil)\n\n# To connect to other buckets, you can also specify multiple hosts:\nbucket = Libcouchbase::Bucket.new(hosts: ['cb1.org', 'cb2.org'], bucket: 'app_data', password: 'goodluck')\n```\n\nConnections can be configured to use `:quiet` mode. This mean it won't raise\nexceptions when the given key does not exist:\n\n```ruby\nbucket.quiet = true\nbucket.get(:missing_key)            #=\u003e nil\n```\n\nIt could be useful avoiding exception handling. (See `#add` and `#replace` operations).\nYou can turn off these exceptions by passing `:quiet =\u003e true` when you\nare instantiating the connection or change corresponding attribute:\n\n```ruby\nbucket.quiet = false\nbucket.get(\"missing-key\")                    #=\u003e raise Libcouchbase::Error::KeyNotFound\nbucket.get(\"missing-key\", :quiet =\u003e true)    #=\u003e nil\n```\n\n\nThe library supports both synchronous and asynchronous operations.\nIn asynchronous mode all operations will return control to caller\nwithout blocking current thread. By default all operations are\nsynchronous, using Fibers on event loops to prevent blocking the\nreactor. Use asynchronous operations if you want mulitple operations\nto execute in parallel.\n\n\n```ruby\n# Perform operations in Async and then wait for the results\nresults = []\nresults \u003c\u003c bucket.get(:key1, async: true)\nresults \u003c\u003c bucket.get(:key2, async: true)\nbucket.wait_results(results)          #=\u003e ['key1_val', 'key2_val']\n\n# Is equivalent to:\nbucket.get(:key1, :key2)              #=\u003e ['key1_val', 'key2_val']\n\n# Process result without waiting or blocking the thread at all\n# This will execute on the couchbase reactor loop so it is\n# recommended not to block in the callback - spin up a new thread\n# or schedule the work to occur next_tick etc\npromise = bucket.get(:key1, async: true)\npromise.then  { |result| puts result }\npromise.catch { |error|  puts error  }\npromise.finally { puts 'operation complete' }\n```\n\n\n### Get\n\n```ruby\nval = bucket.get(\"foo\")\n\n# Get extended details\nresult = bucket.get(\"foo\", extended: true)\nresult.key      #=\u003e \"foo\"\nresult.value    #=\u003e {some: \"value\"}\nresult.cas      #=\u003e 123445\nresult.metadata #=\u003e {flags: 0}\n```\n\n\nGet multiple values. In quiet mode will put `nil` values on missing\npositions:\n\n```ruby\nvals = bucket.get(:foo, :bar, \"baz\")\n```\n\nHash-like syntax\n\n```ruby\nval = bucket[:foo]\n```\n\nReturn a key-value hash\n\n```ruby\nval = bucket.get(:foo, :bar, \"baz\", assemble_hash: true)\nval #=\u003e {:foo =\u003e val1, :bar =\u003e val2, \"baz\" =\u003e val3}\n```\n\n\n### Touch\n\n```ruby\n# Expire in 30 seconds\nbucket.touch(:foo, expire_in: 30\nbucket.touch(:foo, ttl: 30)\nbucket.touch(:foo, expire_at: (Time.now + 30))\n```\n\n\n### Set\n\nThe set command will unconditionally store an object in couchbase.\n\n```ruby\nbucket.add(\"foo\", \"bar\")\nbucket.add(\"foo\", \"bar\", ttl: 30)\n```\n\n\n### Add\n\nThe add command will fail if the key already exists.\n\n```ruby\nbucket.add(\"foo\", \"bar\")\nbucket.add(\"foo\", \"bar\", ttl: 30)\n```\n\n\n### Replace\n\nThe replace command will fail if the key doesn't already exist.\n\n```ruby\nbucket.replace(\"foo\", \"bar\")\n```\n\n\n### Increment/Decrement\n\nThese commands increment the value assigned to the key.\nA Couchbase increment is atomic on a distributed system.\n\n```ruby\nbucket.set(:foo, 1)\nbucket.incr(:foo)           #=\u003e 2\nbucket.incr(:foo, delta: 2) #=\u003e 4\nbucket.incr(:foo, 2)        #=\u003e 6\nbucket.incr(:foo, -1)       #=\u003e 5\n\nbucket.decr(:foo)           #=\u003e 4\nbucket.decr(:foo, 2)        #=\u003e 2\n\nbucket.incr(:missing1, initial: 10)      #=\u003e 10\nbucket.incr(:missing1, initial: 10)      #=\u003e 11\nbucket.incr(:missing2, create: true)     #=\u003e 0\nbucket.incr(:missing2, create: true)     #=\u003e 1\n```\n\n\n### Delete\n\n```ruby\nbucket.delete(:foo)\nbucket.delete(:foo, cas: 8835713818674332672)\n```\n\n\n### Flush\n\nDelete all items in the bucket. This must be enabled on the cluster to work\n\n```ruby\nbucket.flush\n```\n\n### Subdocument queries\n\nThese allow you to modify keys within documents. There is a block form.\n\n```ruby\nc.subdoc(:foo) { |subdoc|\n    subdoc.get('sub.key')\n    subdoc.exists?('other.key')\n    subdoc.get_count('some.array')\n} # =\u003e [\"sub key val\", true, 23]\n```\n\nThere is an inline form\n\n```ruby\nc.subdoc(:foo).get(:bob).execute! # =\u003e { age: 13, working: false }\nc.subdoc(:foo)\n    .get(:bob)\n    .get(:jane)\n    .execute! # =\u003e [{ age: 13, working: false }, { age: 47, working: true }]\n```\n\nYou can't perform lookups and mutations in the same request.\n\n```ruby\n# multi-mutation example\nc.subdoc(:foo)\n    .counter('bob.age', 1)\n    .dict_upsert('bob.address', {\n        number: 23\n        street: 'Daily Ave'\n        suburb: 'Some Town'\n    }).execute! # =\u003e 14 (the new counter value)\n```\n\nBy default, subkeys are created if they don't exist\n\n```ruby\nc.put(:some_key, {name: 'bob'})\nc.subdoc(:some_key).dict_add('non.existant.key', {\n  random: 123,\n  hash: 'values'\n}).execute! \n```\n\nPossible lookup operations are:\n\n* `get`\n* `exists?`\n* `get_count`\n\nPossible mutation operations\n\n* `counter` increments the subkey by integer value passed\n* `dict_upsert` replaces the subkey with value passed\n* `dict_add`\n* `array_add_first`\n* `array_add_last`\n* `array_add_unique`\n* `array_insert`\n* `replace`\n\nYou can see additional docs here: https://developer.couchbase.com/documentation/server/current/sdk/subdocument-operations.html\n\n\n### Views (Map/Reduce queries)\n\nIf you store structured data, they will be treated as documents and you\ncan handle them in map/reduce function from Couchbase Views. For example,\nstore a couple of posts using memcached API:\n\n```ruby\n    c['biking'] = {:title =\u003e 'Biking',\n                   :body =\u003e 'My biggest hobby is mountainbiking. The other day...',\n                   :date =\u003e '2009/01/30 18:04:11'}\n    c['bought-a-cat'] = {:title =\u003e 'Bought a Cat',\n                         :body =\u003e 'I went to the the pet store earlier and brought home a little kitty...',\n                         :date =\u003e '2009/01/30 20:04:11'}\n    c['hello-world'] = {:title =\u003e 'Hello World',\n                        :body =\u003e 'Well hello and welcome to my new blog...',\n                        :date =\u003e '2009/01/15 15:52:20'}\n```\n\nNow let's create design doc with sample view and save it in file\n'blog.json':\n\n```JSON\n    {\n      \"_id\": \"_design/blog\",\n      \"language\": \"javascript\",\n      \"views\": {\n        \"recent_posts\": {\n          \"map\": \"function(doc){if(doc.date \u0026\u0026 doc.title){emit(doc.date, doc.title);}}\"\n        }\n      }\n    }\n```\n\nThis design document could be loaded into the database like this (also you can\npass the ruby Hash or String with JSON encoded document):\n\n```ruby\n    c.save_design_doc(File.open('blog.json'))\n```\n\nTo execute view you need to fetch it from design document `_design/blog`:\n\n```ruby\n    blog = c.design_docs['blog']\n    blog.views                       #=\u003e [\"recent_posts\"]\n\n    # Returns an Enumerator\n    res = blog.view('recent_posts')  #=\u003e #\u003cLibcouchbase::Results:0x007fbaed12c988\u003e\n\n    # Results are lazily loaded by the enumerator\n    # Results are stored for re-use until `res` goes out of scope\n    # Actual database query happens here, by default documents are included\n    res.each do |row|\n        # Returns extended results by default\n        row.key\n        row.value\n        row.cas\n        row.metadata #=\u003e {emitted: val, geometry: spatial_val, format: :document, flags: 0}\n    end\n\n    # You can however stream results to save memory and the results are not saved\n    res.stream do |row|\n        # Row is cleaned up as soon as possible\n    end\n\n    # For IDs only:\n    res = blog.view(:recent_posts, include_docs: false)\n```\n\n\n### N1QL Queries\n\nIf N1QL indexes have been created, then you can query them\n\n```ruby\nresults = bucket.n1ql\n  .select('*')\n  .from(:default)\n  .where('port == 10001')\n  .results\n\n# Results are lazily loaded by the enumerator\n# Results are stored for re-use until `results` goes out of scope\n# Actual database query happens here\nresults.each do |row|\n    # Each row is a Hash of the data requested\nend\n\n# You can however stream results to save memory and the results are not saved\nresults.stream do |row|\n    # Row is cleaned up as soon as possible\nend\n```\n\n\n### Full Text Search\n\nIf Full Text Search indexes have been created, then you can query them\n\n```ruby\nresults = bucket.full_text_search(:index_name, 'query')\n\n# Results are lazily loaded by the enumerator\n# Results are stored for re-use until `res` goes out of scope\n# Actual database query happens here\nresults.each do |row|\n    # Each row is a Hash of the data requested\nend\n\n# You can however stream results to save memory and the results are not saved\nresults.stream do |row|\n    # Row is cleaned up as soon as possible\nend\n```\n\nFull text search supports more complex queries, you can pass in a Hash as the query\nand provide any other options supported by FTS: http://developer.couchbase.com/documentation/server/current/fts/fts-queries.html\n\n```ruby\nbucket.full_text_search(:index_name, {\n    boost: 1,\n    query: \"geo.accuracy:rooftop\"\n}, size: 10, from: 0, explain: true, fields: ['*'])\n```\n\n\n","funding_links":[],"categories":[],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcotag%2Flibcouchbase","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcotag%2Flibcouchbase","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcotag%2Flibcouchbase/lists"}