Skip to content

Commit d8bb526

Browse files
committed
Merge branch 'main' into udpate-php-tutorial
2 parents efe3f4b + 4d03c3b commit d8bb526

File tree

55 files changed

+12390
-2560
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

55 files changed

+12390
-2560
lines changed

.github/workflows/test-md.yaml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,11 @@ jobs:
88
build:
99
runs-on: ubuntu-latest
1010
steps:
11-
- uses: actions/checkout@v2
12-
- name: Use Node.js 16
13-
uses: actions/setup-node@v2
11+
- uses: actions/checkout@v4
12+
- name: Use Node.js 22
13+
uses: actions/setup-node@v4
1414
with:
15-
node-version: 16
15+
node-version: 22
1616
cache: 'npm'
1717
- name: Install Dependencies
1818
run: npm ci

test/test-markdown-frontmatter.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@ const path = require('path')
33
const yaml = require('js-yaml')
44
const chalk = require('chalk')
55

6-
// accepted data field values
7-
const sdk_languages = ['nodejs', 'scala', 'python', 'swift', 'csharp', 'objective-c', 'android-java', 'any', 'java', 'kotlin', 'dart', 'golang', 'php']
6+
const sdk_languages = ['nodejs','scala','python','swift','csharp','objective-c','android-java','any','java','kotlin','dart','golang','php','c++'];
87

9-
const tags = ['Ottoman', 'Ktor', 'REST API', 'Express', 'Flask', 'TLS', 'Configuration', 'Next.js', 'iOS', 'Xcode', '.NET', 'Xamarin', 'Authentication', 'OpenID', 'Keycloak', 'Android', 'P2P', 'UIKit', 'Installation', 'Spring Boot', 'Spring Data', 'Transactions', 'SQL++ (N1QL)', 'Optimization', 'Community Edition', 'Docker', 'Data Modeling', 'Metadata', 'Best Practices', 'Data Ingestion', 'Kafka', 'Support', 'Customer', 'Prometheus', 'Monitoring', 'Observability', 'Metrics', 'Query Workbench', 'ASP.NET', 'linq', 'DBaaS', 'App Services', 'Flutter', 'Gin Gonic', 'FastAPI', 'Laravel', 'LangChain', 'OpenAI', 'Streamlit', 'Google Gemini', 'Nvidia NIM', 'LLama3', 'AWS', 'Artificial Intelligence', 'Cohere', 'Jina AI', 'Mistral AI', 'Ragas', 'Haystack', 'LangGraph', 'Amazon Bedrock', 'CrewAI']
8+
const tags = ['Ottoman','Ktor','REST API','Express','Flask','TLS','Configuration','Next.js','iOS','Xcode','.NET','Xamarin','Authentication','OpenID','Keycloak','Android','P2P','UIKit','Installation','Spring Boot','Spring Data','Transactions','SQL++ (N1QL)','Optimization','Community Edition','Docker','Data Modeling','Metadata','Best Practices','Data Ingestion','Kafka','Support','Customer','Prometheus','Monitoring','Observability','Metrics','Query Workbench','ASP.NET','linq','DBaaS','App Services','Flutter','Gin Gonic','FastAPI','Laravel','LangChain','OpenAI','Streamlit','Google Gemini','Nvidia NIM','LLama3','AWS','Artificial Intelligence','Cohere','Jina AI','Mistral AI','Ragas','Haystack','LangGraph','Amazon Bedrock','CrewAI','PydanticAI','C++','C++ SDK','smolagents','Ag2','Autogen','Couchbase Edge Server','Deepseek','OpenRouter','mastra'];
109

11-
const technologies = ['connectors', 'kv', 'query', 'capella', 'server', 'index', 'mobile', 'fts', 'sync gateway', 'eventing', 'analytics', 'udf', 'vector search']
10+
const technologies = ['connectors','kv','query','capella','server','index','mobile','fts','sync gateway','eventing','analytics','udf','vector search','react','edge-server','app-services'];
11+
12+
const content_types = ['quickstart','tutorial','learn'];
1213

13-
const content_types = ['quickstart', 'tutorial', 'learn']
1414

1515

1616
const test = (data, path) => {
704 KB
Loading
Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
---
2+
# frontmatter
3+
path: "/tutorial-cxx-quickstart"
4+
title: Quickstart in Couchbase with C++
5+
short_title: C++ Quickstart
6+
description:
7+
- Learn to to use Couchbase C++ SDK to interact with the database.
8+
- See how you can fetch data from Couchbase using SQL++ queries
9+
- Explore how you can perform search operations using Search indexes.
10+
- Explore CRUD operations in action with Couchbase
11+
content_type: quickstart
12+
filter: sdk
13+
technology:
14+
- kv
15+
- index
16+
- query
17+
- fts
18+
tags:
19+
- C++
20+
- C++ SDK
21+
sdk_language:
22+
- c++
23+
length: 30 Mins
24+
---
25+
26+
<!-- [abstract] -->
27+
# Quickstart for Couchbase in C++
28+
29+
In this tutorial, you will learn how to connect to a Couchbase Capella cluster to create, read, update, and delete documents, how to write simple parametrized SQL++ queries and how to create, and perform simple and complex text search using Search indexes.
30+
31+
## Prerequisites
32+
33+
To run this prebuilt project, you will need:
34+
35+
- [Couchbase Capella](https://www.couchbase.com/products/capella/) cluster or a self managed Couchbase cluster with [travel-sample](https://docs.couchbase.com/python-sdk/current/ref/travel-app-data-model.html) bucket loaded.
36+
37+
- [CMake](https://cmake.org/) 3.9 or higher installed
38+
- [C++17](https://en.cppreference.com/w/cpp/17) and a compatible compiler, [clang++](https://clang.llvm.org/) or [g++](https://gcc.gnu.org/)
39+
- Loading Travel Sample Bucket
40+
If travel-sample is not loaded in your cluster, you can load it by following the instructions:
41+
- [Load travel-sample bucket in Couchbase Capella](https://docs.couchbase.com/cloud/clusters/data-service/import-data-documents.html#import-sample-data)
42+
- [Load travel-sample bucket in self-managed cluster](https://docs.couchbase.com/server/current/getting-started/do-a-quick-install.html#load-the-sample-dataset)
43+
44+
## Application Setup
45+
46+
We will walk through the different steps required to get the application running.
47+
48+
### Cloning Repo
49+
50+
```sh
51+
git clone https://github.com/couchbase-examples/cxx-quickstart.git
52+
```
53+
54+
55+
### Setup Database Configuration
56+
57+
To know more about connecting to your Capella cluster, please follow the [instructions](https://docs.couchbase.com/cloud/get-started/connect.html).
58+
59+
Specifically, you need to do the following:
60+
61+
- Create the [database credentials](https://docs.couchbase.com/cloud/clusters/manage-database-users.html) to access the travel-sample bucket (Read and Write) used in the application.
62+
- [Allow access](https://docs.couchbase.com/cloud/clusters/allow-ip-address.html) to the Cluster from the IP on which the application is running.
63+
64+
All configuration for communication with the database is read from the environment variables. We have provided a convenience feature in this quickstart to setup the required environment variables using a shell script `setup_env_vars.sh`. Change the values of the following lines:
65+
66+
```sh
67+
export DB_CONN_STR=<connection_string>
68+
export DB_USERNAME=<username>
69+
export DB_PASSWORD=<password>
70+
export BUCKET_NAME=<bucket_name>
71+
export SCOPE_NAME=<inventory>
72+
export COL_NAME=<collection_name>
73+
```
74+
> Note: The connection string expects the `couchbases://` or `couchbase://` part.
75+
76+
Run the command:
77+
```sh
78+
source setup_env_vars.sh
79+
```
80+
This will set the environment variables for that session.
81+
82+
### Install Dependencies and Building
83+
84+
This project makes use of CMake and CPM to install dependencies.
85+
86+
87+
```sh
88+
mkdir build
89+
cd build
90+
cmake ..
91+
cmake --build .
92+
```
93+
94+
This will download and install all the dependencies required for the project to built. Along with that it will build the executable required to run the application.
95+
96+
## Running The Application
97+
98+
### Directly on Machine
99+
100+
At this point, we have installed the dependencies, loaded the travel-sample data and configured the application with the credentials. The application is now ready and you can run it by executing the following command from the build directory:
101+
102+
```sh
103+
cmake --build .
104+
./cxx_quickstart
105+
```
106+
> Note: Run this command from the build directory
107+
108+
### Verifying the Application
109+
110+
Once you run the executable, your terminal should fill up with the results of the executed statements written in the main function of the `main.cpp` and should look something like this:
111+
![image](./cli_output.png)
112+
113+
114+
### Running Tests
115+
116+
For running tests, a self-managed cluster is required with travel-sample bucket loaded or you need to update the connection details in the `tests/test.cpp` file. To run the tests, use the following command from the build directory:
117+
118+
```sh
119+
./tests/u_tests
120+
```
121+
122+
123+
### Schema and Usage Overview
124+
125+
This quickstart utilizes two collections: **airline** and **hotel**. The **airline** collection is used for CRUD operations, while the **hotel** collection is leveraged for Search indexes and Query execution. The schemas for both collections can be found in the `model` folder.
126+
127+
128+
## Code Review
129+
130+
To begin this tutorial, clone the repo and open it up in the IDE of your choice. Now you can explore about how to interact with Couchbase Server using the C++ SDK.
131+
132+
We have separated out the SDK code and the main function. The `db.h` and `db.cpp` contain the declaration and the implementation of utility functions we will use to parse environment variables and create a connection to the cluster. `operations.h` and `operations.cpp` contain all the functions that perform operations on the db. Both `db.cpp` and `operations.cpp` are combined to make a static library. The tests are similarly separated out in the `tests` folder which utilise the library created earlier. The `main.cpp` is the executable which is also built by linking the library and contains code that demonstrates the usage of the functions we defined earlier to initeract with the db.
133+
134+
### Connecting to the Cluster
135+
In `db.h`, we include the required header files to work with C++ SDK in order to for implement the functions required to initialize the DB. In the `db.cpp` we implement the functions that help us connect to the db. We begin by implementing few utility functions that will help us later. The `parseEnvironmentVariables` serves as a utility to get the values set for a list of environment variables. This enables us to get the connection parameters and credentials, set by running `source set_env_vars.sh`. Following this `checkScopeAndColExists` and `checkSearchEnabled` are implemented, used to check for existence of scope and collection of given name and to check if search service enabled respectively. Finally we have the InitCluster function which returns the connection objects as a tuple.
136+
137+
```c++
138+
// db.h
139+
140+
...
141+
std::vector<std::string> parseEnvironmentVariables(const std::vector<std::string>& keys);
142+
bool checkScopeAndColExists(couchbase::bucket& bucket, const std::string& scope_name, const std::string& col_name);
143+
bool checkSearchEnabled(couchbase::cluster& cluster, int min_nodes);
144+
...
145+
std::tuple<couchbase::cluster, couchbase::bucket, couchbase::scope, couchbase::collection> InitCluster();
146+
```
147+
We recommend creating a single Couchbase connection when your application starts up, and sharing this instance throughout your application. You should always set the default `BUCKET_NAME`, `SCOPE_NAME`, `COL_NAME` environment variables, and use the `InitCluster` function to get the instances. You should share and use these instances throughout your application.
148+
149+
150+
The Couchbase connection is established in the `connectCluster` method defined in `db.h` and implemented in `db.cpp`. There, we call the ["connect"](https://docs.couchbase.com/sdk-api/couchbase-cxx-client-1.0.5/classcouchbase_1_1cluster.html#a57285fbf793227e26d88de3808f9a707) method defined in the SDK to create the Database connection. If the connection is already established, we do not do anything. Following connection to the cluster, get the reference to the bucket, scope and collection and return all the objects as a tuple.
151+
152+
```c++
153+
// db.cpp
154+
...
155+
auto [connect_err, cluster] = couchbase::cluster::connect(DB_CONN_STRING, options).get();
156+
...
157+
auto bucket = cluster.bucket(BUCKET_NAME);
158+
...
159+
auto scope = bucket.scope(SCOPE_NAME);
160+
auto col = scope.collection(COL_NAME);
161+
return {cluster, bucket, scope, col};
162+
```
163+
164+
165+
166+
## Operations
167+
Operations for interacting with the db is defined and implemented in `operations.h` and `operations.cpp`
168+
### Insert Document
169+
Insert function is the equivalent of the POST request and can be used to insert new documents to the collection. We can pass the document to be inserted as a JSON string or as a JSON file path, the function takes in file_flag which is used to differentiate between the two.
170+
- The value gets converted to the type `tao::json::value` and inserts it to the collection if `file_flag=false`
171+
- If `file_flag=true`, it reads the content from the provided file and then converts it to `tao::json::value`.
172+
- Performs an upsert operation on the collection using the `doc_id` and the converted document content.
173+
- If successful, `return 1`. If an error occurs, prints an error message and `return 0`.
174+
```c++
175+
// operations.cpp
176+
auto [in_error, in_res] = col.insert(doc_id, v).get();
177+
178+
//main.cpp
179+
auto insert_res = Insert(col, "quickstart_test", "{ \"test\": \"hello\"}", false);
180+
auto insert_res2 = Insert(col, "quickstart_test2", "doc.json", true);
181+
```
182+
183+
### Upsert Document
184+
Upsert function is the equivalent of the PUT request. It can be used to update any existing document or to insert a new document to the collection if the `doc_id` doesn't exist already. Similar to Insert, We can pass the document to be inserted as a JSON string or as a JSON file path, the function takes in file_flag which is used to differentiate between the two.
185+
- The value gets converted to the type `tao::json::value` and inserts it to the collection if `file_flag=false`
186+
- If `file_flag=true`, it reads the content from the provided file and then converts it to `tao::json::value`.
187+
- Performs an insert operation on the collection using the `doc_id` and the converted document content.
188+
- If successful, `return 1`. If an error occurs, prints an error message and `return 0`.
189+
```c++
190+
// operations.cpp
191+
auto [up_error, up_res] = col.upsert(doc_id, v).get();
192+
193+
//main.cpp
194+
auto upsert_res = Upsert(col, "quickstart_test", "{ \"test\": \"hello\"}", false);
195+
auto upsert_res2 = Upsert(col, "quickstart_test2", "doc.json", true);
196+
```
197+
198+
### Read
199+
Read function is equivalent to GET requests and can be used fetch documents using the `doc_id`.
200+
- First checks if the document exists using `col.exists(doc_id)`.
201+
- If the document exists, it retrieves the document's content using `col.get(doc_id)` and returns it after converting it to `tao::json::value` for easier usage on return.
202+
- If an error occurs (e.g., document not found), it prints an error message and returns an empty tao::json::value object.
203+
```c++
204+
//operations.cpp
205+
auto [ex_err, ex_res] = col.exists(doc_id).get();
206+
...
207+
auto [get_err, get_res] = col.get(doc_id).get();
208+
...
209+
auto doc = get_res.content_as<tao::json::value>();
210+
return doc;
211+
212+
//main.cpp
213+
auto v = Read(col, "airline_10123");
214+
std::cout << tao::json::to_string(v) << std::endl;
215+
```
216+
217+
### Delete
218+
Delete function attempts to remove a document with the given `doc_id`
219+
- Attempts to remove the document with a given `doc_id` from the collection.
220+
- If the deletion is successful, `return 1` and if an error occurs, it prints an error message and `return 0`.
221+
222+
```c++
223+
//operations.cpp
224+
auto [delete_err, delete_res] = col.remove(doc_id).get();
225+
226+
//main.cpp
227+
auto res = Delete(col, doc_id);
228+
```
229+
230+
### Query
231+
We can use the `Query` function to execute any N1QL (SQL++) query on a scope.
232+
- Executes the N1QL query using the provided `scope.query(query, opts)`.
233+
- Returns the result of the query if successful. The result is added to a `std::vector<std::string>` object that contains the `id, country, avg_rating, title`.
234+
- We can pass `opts` parameter, which can be used to insert positonal parameters in the query.
235+
- If there is an error, it prints an error message and returns an empty result object.
236+
237+
```c++
238+
//operations.cpp
239+
std::string query{ R"(
240+
SELECT META(h).id, h AS doc,
241+
AVG(r.ratings.Overall) AS avg_rating
242+
FROM hotel h
243+
UNNEST h.reviews r
244+
WHERE h.country IN $1 AND h.description LIKE "%cheap%"
245+
GROUP BY META(h).id, h
246+
ORDER BY avg_rating DESC
247+
LIMIT 5;
248+
)" };
249+
auto [q_err, q_res] = scope.query(query, couchbase::query_options{}.positional_parameters(std::vector<std::string>{"United States", "United Kingdom"})).get();
250+
251+
//main.cpp
252+
for (auto& row : query_res) {
253+
std::cout << row << std::endl;
254+
}
255+
256+
```
257+
258+
### Create Search Index
259+
Search indexes in Couchbase are used for full-text search and efficient querying of documents based on specific fields or attributes. The `CreateSearchIndex` function helps in creating a new Search index which can then be used.
260+
- Reads the index configuration from the `index_file`.
261+
- Checks if an index with the same name already exists using the `searchIndexExists` function.
262+
- If the index with same name exists, it returns the index name.
263+
- If the index does not exist, it constructs a new search index object and upserts it into the Couchbase scope.
264+
- Returns the name of the newly created index or an empty string if there was an error.
265+
266+
```c++
267+
// operations.cpp
268+
auto err = scope_index_manager.upsert_index(i).get();
269+
270+
//main.cpp
271+
std::string index_name = CreateSearchIndex(scope, "hotel_search_index.json");
272+
```
273+
274+
### Search By Name
275+
The `SearchByName` function aims to demonstrates the usage of a search index to search for documents in a scope.
276+
Params:
277+
- `scope`: The Couchbase scope to search in.
278+
- `index`: The search index name to use for the query.
279+
- `name`: The name to search for in the documents.
280+
- `field`: The field where the name should be searched.
281+
- `limit`: The maximum number of results to return.
282+
283+
```c++
284+
//operations.cpp
285+
auto [s_err, s_res] = scope.search(index, searchQ, opts).get();
286+
...
287+
std::vector<std::string> rows_res{};
288+
// Reference is important since the copy constructor is deleted
289+
for(auto &row:s_res.rows()){
290+
rows_res.push_back(row.id());
291+
}
292+
return rows_res;
293+
294+
//main.cpp
295+
auto search_res = SearchByName(scope, index_name, "swanky", "name", 50);
296+
std::cout << "Search result contains:\t" << search_res.size() << std::endl;
297+
```
298+
299+
### Filter
300+
The `Filter` function aims to demo the construction and execution of a `conjuction_query` which can be described as an `AND` operation on two or more types of filters. This particular implementation performs a conjunction on `couchbase::match_query("United States").field("country")` and `couchbase::term_query("San Diego").field("city")`.
301+
302+
```c++
303+
//operations.cpp
304+
auto query = couchbase::conjunction_query{
305+
couchbase::match_query("United States").field("country"),
306+
couchbase::term_query("San Diego").field("city")
307+
};
308+
...
309+
auto [err,res] = scope.search(index_name, couchbase::search_request(query), opts).get();
310+
311+
for(auto &row:res.rows()){
312+
auto fields = row.fields_as<couchbase::codec::tao_json_serializer>();
313+
rows_res.push_back(fields["name"].as<std::string>());
314+
}
315+
return rows_res;
316+
317+
//main.cpp
318+
auto filter_res = Filter(scope, index_name, 50, 1);
319+
std::cout << "Filter result contains:\t" << filter_res.size() << std::endl;
320+
```

tutorial/markdown/couchbase-server/best-practices/json-data-modeling-guide/01-comparing-document-oriented-relational-data.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ description:
88
- Learn the different characteristics of each and explore the flexibility of Couchbase
99
- See an example dataset get mapped from relational to document-based
1010
content_type: tutorial
11-
filter: n1ql
11+
filter: sql++
1212
technology:
1313
- kv
1414
- capella

0 commit comments

Comments
 (0)