From 9e3fb512ebfbf89073888bf89e4ccee69ee88832 Mon Sep 17 00:00:00 2001 From: Dhiraj Kumar Azad Date: Mon, 20 May 2024 16:59:48 +0530 Subject: [PATCH 1/5] Update docs for database, collection and scopes, indexing, documents, blobs, sql++ --- docs/Queries/sqlplusplus.md | 1361 ++++++++++++++++++++++++++++++++++- docs/blobs.md | 8 +- docs/database-prebuilt.md | 18 +- docs/databases.md | 33 +- docs/documents.md | 43 +- docs/indexes.md | 58 +- docs/scopes-collections.md | 94 ++- 7 files changed, 1560 insertions(+), 55 deletions(-) diff --git a/docs/Queries/sqlplusplus.md b/docs/Queries/sqlplusplus.md index 0c38b78..b156f7f 100644 --- a/docs/Queries/sqlplusplus.md +++ b/docs/Queries/sqlplusplus.md @@ -3,4 +3,1363 @@ id: sqlplusplus sidebar_position: 1 --- -# SQL++ for Typscript \ No newline at end of file +# SQL++ for Mobile + +> Description - _How to use SQL++ Query Strings to build effective queries with Couchbase Lite for Ionic_ +> Related Content - [Live Queries](../live-queries.md) | [Indexes](../indexes.md) + +:::info +N1QL is Couchbase's implementation of the developing SQL++ standard. As such the terms N1QL and SQL++ are used interchangeably in all Couchbase documentation unless explicitly stated otherwise. +::: + +## Introduction + +Developers using Couchbase Lite for Ionic can provide SQL++ query strings using the SQL++ Query API. This API uses query statements of the form shown in [Example 1](#example-1-running-a-sql-query). The structure and semantics of the query format are based on that of Couchbase Server's SQL++ query language — see [SQL++ Reference Guide](https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/index.html) and [SQL++ Data Model](https://docs.couchbase.com/server/current/learn/data/n1ql-versus-sql.html). + +## Running + +Use `Database.createQuery` to define a query through an SQL++ string. Then run the query using the `Query.execute()` method. + +#### Example 1. Running a SQL++ Query + +```typescript +const query = database.createQuery("SELECT META().id AS thisId FROM _ WHERE type = \"hotel\""); +const resultSet = await query.execute(); +``` + +Here we are accessing the default collection using the shorthand notation (`_`) — see the FROM clause for more on data source selection and Query Parameters for more on parameterized queries. + +## Query Format + +The API uses query statements of the form shown in [Example 2](#example-2-query-format). + +#### Example 2. Query Format + +```SQL +SELECT ____ +FROM ____ +JOIN ____ +WHERE ____ +GROUP BY ____ +ORDER BY ____ +LIMIT ____ +OFFSET ____ +``` + +### Query Components + +1. The `SELECT` clause specifies the data to be returned in the result set. +2. The `FROM` clause specifies the collection to query the documents from. +3. The `JOIN` clause specifies the criteria for joining multiple documents. +4. The `WHERE` clause specifies the query criteria. The `SELECT`ed properties of documents matching this criteria will be returned in the result set. +5. The `GROUP BY` clause specifies the criteria used to group returned items in the result set. +6. The `ORDER BY` clause specifies the criteria used to order the items in the result set. +7. The `LIMIT` clause specifies the maximum number of results to be returned. +8. The `OFFSET` clause specifies the number of results to be skipped before starting to return results. + +:::tip +We recommend working through the [SQL++ Tutorials](https://query-tutorial.couchbase.com/tutorial/#1) as a good way to build your SQL++ skills. +::: + +## SELECT Clause + +### Purpose + +Projects the result returned by the query, identifying the columns it will contain. + +### Syntax + +#### Example 3. SQL++ Select Syntax + +```SQL +select = SELECT _ ( ( DISTINCT | ALL ) _ )? selectResults +selectResults = selectResult ( _? ',' _? selectResult )* +selectResult = expression ( ( _ AS )? _ columnAlias )? +columnAlias = IDENTIFIER +``` + +### Arguments + +1. The select clause begins with the `SELECT` keyword. + * The optional `ALL` argument is used to specify that the query should return `ALL` results (the default). + * The optional `DISTINCT` argument is used to specify that the query should return distinct results. + +2. `selectResults` is a list of columns projected in the query result. Each column is an expression which could be a property expression or any expression or function. You can use the `*` expression, to select all columns. + +3. Use the optional `AS` argument to provides an alias for a column. Each column can be aliased by putting the alias name after the column name. + +#### SELECT Wildcard + +When using the `*` expression, the column name is one of: + + * The alias name, if one was specified. + * The data source name(or its alias if provided) as specified in the [FROM clause](https://cbl-dart.dev/queries/sqlplusplus-mobile/#from-clause). + +This behavior is inline with that of SQL++ for Server — see example in [Table 1](#table-1-example-column-names-for-select). + +#### Table 1. Example Column Names for SELECT * + +| Query | Column Name | +|-------------------------------|---------------------| +| `SELECT * AS data FROM _` | `data` | +| `SELECT * FROM _` | `_` | +| `SELECT * FROM _default` | `_default` | +| `SELECT * FROM users` | `users` | +| `SELECT * FROM users AS user` | `user` | + +### Example + +#### Example 4. SELECT Examples + +```SQL +SELECT * ...; +SELECT user.* AS data ...; +SELECT name fullName ...; +SELECT user.name ...; +SELECT DISTINCT address.city ...; +``` + +1. Use the `*` expression to select all columns. +2. Select all properties from the `user` data source. Give the object an alias of `data`. +3. Select a pair of properties. +4. Select a specific property from the `user` data source. +5. Select the property `city` from the `address` data source. + +## FROM Clause + +### Purpose + +Specifies the data source and optionally applies an alias (`AS`). It is mandatory. + +### Syntax + +#### Example 5. FROM Syntax + +```SQL +from = FROM _ dataSource +dataSource = collectionName ( ( _ AS )? _ collectionAlias )? +collectionName = IDENTIFIER +collectionAlias = IDENTIFIER +``` + +Here `dataSource` is the collection name against which the query is to run. Use `AS` to give the collection an alias you can use within the query. To use the default collection, without specifying a name, use `_` as the data source. + +### Example + +#### Example 6. FROM Examples + +```SQL +SELECT name FROM user; +SELECT user.name FROM users AS user; +SELECT user.name FROM users user; +SELECT name FROM _; +SELECT user.name FROM _ AS user; +SELECT user.name FROM _ user; +``` + +## JOIN Clause + +### Purpose + +The `JOIN` clause enables you to select data from multiple data sources linked by criteria specified in the ON constraint. Currently only self-joins are supported. For example to combine airline details with route details, linked by the airline id — see Example 7. + +### Syntax + +#### Example 7. JOIN Syntax + +```SQL +join = joinOperator _ dataSource ( _ constraint )? +joinOperator = ( ( LEFT ( _ OUTER )? | INNER | CROSS ) _ )? JOIN +dataSource = collectionName ( ( _ AS )? _ collectionAlias )? +constraint = ON _ expression +collectionName = IDENTIFIER +collectionAlias = IDENTIFIER +``` + +### Arguments + +1. The `JOIN` clause starts with a `JOIN` operator followed by the data source. +2. Five `JOIN` operators are supported: + * `JOIN`, `LEFT JOIN`, `LEFT OUTER JOIN`, `INNER JOIN`, and `CROSS JOIN`. + * Note: `JOIN` and `INNER JOIN` are the same, and `LEFT JOIN` and `LEFT OUTER JOIN` are the same. +3. The `JOIN` constraint starts with the `ON` keyword followed by the expression that defines the joining constraints. + +### Example + +#### Example 8. JOIN Examples + +```SQL +SELECT users.prop1, other.prop2 +FROM users +JOIN users AS other ON users.key = other.key; + +SELECT users.prop1, other.prop2 +FROM users +LEFT JOIN users AS other ON users.key = other.key; +``` + +#### Example 9. Using JOIN to Combine Document Details + +This example joins the documents from the `routes` collections with documents from the `airlines` collection using the document ID (`id`) of the *airline* document and the `airlineId` property of the *route* document. + +```SQL +SELECT * +FROM routes r +JOIN airlines a ON r.airlineId = META(a).id +WHERE a.country = "France"; +``` + +## WHERE Clause + +### Purpose + +Specifies the selection criteria used to filter results. As with SQL, use the `WHERE` clause to choose which results are returned by your query. + +### Syntax + +#### Example 10. WHERE Syntax + +```SQL +where = WHERE _ expression +``` + +### Arguments + +1. `WHERE` evalates the expression to a `BOOLEAN` value. You can combine any number of expressions through logical operators, in order to implement sophisticated filtering capabilities. + +### Example + +#### Example 11. WHERE Examples + +```SQL +SELECT name +FROM employees +WHERE department = "engineer" AND group = "mobile" +``` + +## GROUP BY Clause + +### Purpose + +Use `GROUP BY` to group results for aggreation, based on one or more expressions. + +### Syntax + +#### Example 12. GROUP BY Syntax + +```SQL +groupBy = grouping ( _ having )? +grouping = GROUP BY _ expression ( _? ',' _? expression )* +having = HAVING _ expression +``` + +### Arguments + +1. The `GROUP BY` clause starts with the `GROUP BY` keyword followed by one or more expressions. +2. The `GROUP BY` clause is normally used together with aggregate functions (e.g. `COUNT`, `MAX`, `MIN`, `SUM`, `AVG`). +3. The `HAVING` clause allows you to filter the results based on aggregate functions — for example, `HAVING COUNT(airlineId) > 100`. + +### Example + +#### Example 13. GROUP BY Examples + +```SQL +SELECT COUNT(airlineId), destination +FROM routes +GROUP BY destination; + +SELECT COUNT(airlineId), destination +FROM routes +GROUP BY destination +HAVING COUNT(airlineId) > 100; + +SELECT COUNT(airlineId), destination +FROM routes +WHERE destinationState = "CA" +GROUP BY destination +HAVING COUNT(airlineId) > 100; +``` + +## ORDER BY Clause + +### Purpose + +Sort query results based on a expression. + +### Syntax + +#### Example 14. ORDER BY Syntax + +```SQL +orderBy = ORDER BY _ ordering ( _? ',' _? ordering )* +ordering = expression ( _ order )? +order = ( ASC | DESC ) +``` + +### Arguments + +1. The `ORDER BY` clause starts with the `ORDER BY` keyword followed by one or more ordering expressions. +2. An ordering expression specifies an expressions to use for ordering the results. +3. For each ordering expression, the sorting direction can be specified using the optional `ASC` (ascending) or `DESC` (descending) directives. Default is `ASC`. + +### Example + +#### Example 15. ORDER BY Examples + +```SQL +SELECT name +FROM users +ORDER BY name; + +SELECT name +FROM users +ORDER BY name DESC; + +SELECT name, score +FROM users +ORDER BY name ASC, score DESC; +``` + +## LIMIT Clause + +### Purpose + +Specifies the maximum number of results to be returned by the query. + +### Syntax + +#### Example 16. LINIT Syntax + +```SQL +limit = LIMIT _ expression +``` + +### Arguments + +1. The `LIMIT` clause starts with the `LIMIT` keyword followed by an expression that will be evaluated as a number. + +### Example + +#### Example 17. LIMIT Examples + +```SQL +SELECT name +FROM users +LIMIT 10; +``` + +## OFFSET Clause {#offset-clause} + +### Purpose + +Specifies the number of results to be skipped by the query. + +### Syntax + + +#### Example 18. OFFSET syntax + +``` +offset = OFFSET _ expression +``` + +### Arguments + +1. The offset clause starts with the `OFFSET` keyword followed by an expression + that will be evaluated as a number that represents the number of results to + be skipped before the query begins returning results. + +### Example + +#### Example 19. OFFSET Examples + +```sql +SELECT name +FROM db +OFFSET 10; + +SELECT name +FROM db +LIMIT 10 +OFFSET 10; +``` + +## Expressions {#expressions} + +An expression is a specification for a value that is resolved when executing a +query. This section, together with [Operators](#operators) and +[Functions](#functions), which are covered in their own sections, covers all the +available types of expressions. + +### Literals + +#### Boolean + +##### Purpose + +Represents a true or false value. + +##### Syntax + +#### Example 20. Boolean Syntax + +``` +boolean = ( TRUE | FALSE ) +``` + +##### Example + +#### Example 21. Boolean Examples + +```sql +SELECT value +FROM db +WHERE value = true; + +SELECT value +FROM db +WHERE value = false; +``` + +#### Numeric + +##### Purpose + +Represents a numeric value. Numbers may be signed or unsigned digits. They have +optional fractional and exponent components. + +##### Syntax + +#### Example 22. Numeric Syntax + +``` +numeric = -? ( ( . digit+ ) | ( digit+ ( . digit* )? ) ) ( ( E | e ) ( - | + )? digit+ )? +digit = /[0-9]/ +``` + + +##### Example + +#### Example 23. Numeric Examples + +```sql +SELECT + 10, + 0, + -10, + 10.25, + 10.25e2, + 10.25E2, + 10.25E+2, + 10.25E-2 +FROM db; +``` + +#### String + +##### Purpose + +The string literal represents a string or sequence of characters. + +##### Syntax + +#### Example 24. String Syntax + +``` +string = ( " character* " | ' character* ' ) +character = ( escapeSequence | any codepoint except ", ' or control characters ) +escapeSequence = \ ( " | ' | \ | / | b | f | n | r | t | u hex hex hex hex ) +hex = hexDigit hexDigit +hexDigit = /[0-9a-fA-F]/ +``` + +:::note + +The string literal can be double-quoted as well as single-quoted. + +::: + +##### Example + +#### Example 25. String Examples + +```sql +SELECT firstName, lastName +FROM db +WHERE middleName = "middle" AND lastName = 'last'; +``` + +#### NULL + +##### Purpose + +Represents the absence of a value. + +##### Syntax + +#### Example 26. NULL Syntax + +``` +null = NULL +``` + +##### Example + +#### Example 27. NULL Examples + +```sql +SELECT firstName, lastName +FROM db +WHERE middleName IS NULL; +``` + +#### MISSING + +##### Purpose + +Represents a missing name-value pair in a dictionary. + +##### Syntax + +#### Example 28. MISSING Syntax + +``` +missing = MISSING +``` + +##### Example + +#### Example 29. MISSING Examples + +```sql +SELECT firstName, lastName +FROM db +WHERE middleName IS MISSING; +``` + +#### Array + +##### Purpose + +Represents an array. + +##### Syntax + +#### Example 30. ARRAY Syntax + +``` +array = [ ( _? expression ( _? ',' _? expression )* _? )? ] +``` + +##### Example + +#### Example 31. ARRAY examples + +```sql +SELECT ["a", "b", "c"] +FROM db; + +SELECT [property1, property2, property3] +FROM db; +``` + +#### Dictionary + +##### Purpose + +Represents a dictionary. + +##### Syntax + +#### Example 32. Dictionary Syntax + +``` +dictionary = { ( _? string _? : _? expression ( _? , _? string _? : _? expression )* _? )? } +``` + + +##### Example + +#### Example 33. Dictionary Examples + +```sql +SELECT { 'name': 'James', 'department': 10 } +FROM db; + +SELECT { 'name': 'James', 'department': dept } +FROM db; + +SELECT { 'name': 'James', 'phones': ['650-100-1000', '650-100-2000'] } +FROM db; +``` + +### Identifier + +#### Purpose + +An identifier references an entity by its symbolic name. Use an identifier for +example to identify: + +- Column alias names +- Database names +- Database alias names +- Property names +- Parameter names +- Function names +- FTS index names + +#### Syntax + +#### Example 34. Identifier Syntax + +``` +identifier = ( plainIdentifier | quotedIdentifier ) +plainIdentifier = /[a-zA-Z_][a-zA-Z0-9_$]*/ +quotedIdentifier = /`[^`]+`/ +``` + +:::tip + +To use other than basic characters in the identifier, surround the identifier +with the backticks ` character. For example, to use a hyphen (-) in an +identifier, use backticks to surround the identifier. + +::: + +#### Example + +#### Example 35. Identifier Examples + +```sql +SELECT * +FROM _; + +SELECT * +FROM `db-1`; + +SELECT key +FROM db; + +SELECT key$1 +FROM db_1; + +SELECT `key-1` +FROM db; +``` + +### Property Expression + +#### Purpose + +The property expression is used to reference a property of a dictionary. + +#### Syntax + +#### Example 36. Property Expression Syntax + +``` +property = ( * | dataSourceName . _? * | propertyPath ) +propertyPath = propertyName ( ( . _? propertyName ) | ( [ _? numeric _? ] _? ) )* +propertyName = IDENTIFIER +``` + +1. Prefix the property expression with the data source name or alias to indicate + its origin. +2. Use dot syntax to refer to nested properties in the propertyPath. +3. Use bracket (`[index]`) syntax to refer to an item in an array. +4. Use the asterisk (`*`) character to represents all properties. This can only + be used in the result list of the `SELECT` clause. + + +#### Example + +#### Example 37. Property Expressions Examples + +```sql +SELECT * +FROM db +WHERE contact.name = 'daniel'; + +SELECT db.* +FROM db +WHERE contact.name = 'daniel'; + +SELECT db.contact.address.city +FROM db +WHERE contact.name = 'daniel'; + +SELECT contact.address.city, contact.phones[0] +FROM db +WHERE contact.name = 'daniel'; +``` + +### Any and Every Expression + +#### Purpose + +Evaluates expressions over items in an array. + +#### Syntax + +#### Example 38. Any and Every Expression Syntax + + +```sql +arrayExpression = anyEvery _ variableName _ IN _ expression _ SATISFIES _ expression _ END +anyEvery = ( anyOrSome AND EVERY | anyOrSome | EVERY ) +anyOrSome = ( ANY | SOME ) +variableName = IDENTIFIER +``` + +1. The array expression starts with `anyEvery`, where each possible combination + has a different function as described below, and is terminated by `END`. + + - `ANY` or `SOME`: Returns `TRUE` if at least one item in the array satisfies + the expression, otherwise returns `FALSE`. + + :::note + + `ANY` and `SOME` are interchangeable. + + ::: + + - `EVERY`: Returns `TRUE` if all items in the array satisfies the expression, + otherwise returns `FALSE`. If the array is empty, returns `TRUE`. + - `( ANY | SOME ) AND EVERY`: Same as `EVERY` but returns `FALSE` if the + array is empty. + +2. The `variableName` represents each item in the array. +3. The `IN` keyword is used to specify the array to be evaluated. +4. The `SATISFIES` keyword is used to specify the expression to evaluate for + each item in the array. +5. `END` terminates the array expression. + + +#### Example + +#### Example 39. Any and Every Expression Examples + + +```sql +SELECT name +FROM db +WHERE + ANY contact IN contacts + SATISFIES contact.city = 'San Mateo' + END; +``` + +### Parameter Expression + +#### Purpose + +A parameter expression references a value from the `api|Parameters` assigned to +the query before execution. + +:::note + +If a parameter is specified in the query string, but no value has been provided, +an error will be thrown when executing the query. + +::: + +#### Syntax + +#### Example 40. Parameter Expression Syntax + + +``` +parameter = $ IDENTIFIER +``` + +#### Example + +#### Example 41. Parameter Expression Examples + + +```sql +SELECT name +FROM db +WHERE department = $department; +``` + +#### Example 42. Using a Parameter + + +```dart +final query = await Query.fromN1ql( + db, + r''' + SELECT name + WHERE department = $department + ''', +); +query.parameters = Parameters({'department': 'E001'}); +final results = query.execute(); +``` + +### Parenthesis Expression + +#### Purpose + +Use parentheses to group expressions together to make them more readable or to +establish operator precedence. + +#### Example + +#### Example 43. Parenthesis Expression Examples + + +```sql +SELECT (value1 + value2) * value 3 +FROM db; + +SELECT * +FROM db; +WHERE ((value1 + value2) * value3) + value4 = 10; + +SELECT * +FROM db +WHERE (value1 = value2) + OR (value3 = value4); +``` + +## Operators {#operators} + +### Binary Operators + +#### Maths + +#### Table 2. Maths Operators + +| Op | Description | Example | +| --- | -------------- | --------------------- | +| `+` | Add | `WHERE v1 + v2 = 10` | +| `-` | Subtract | `WHERE v1 - v2 = 10` | +| `*` | Multiply | `WHERE v1 \* v2 = 10` | +| `/` | Divide - see 1 | `WHERE v1 / v2 = 10` | +| `%` | Modulus | `WHERE v1 % v2 = 0` | + +1. If both operands are integers, integer division is used, but if one is a + floating number, then float division is used. This differs from SQL++ for + Server, which performs float division regardless. Use `DIV(x, y)` to force + float division in SQL++ for Mobile. + +#### Comparison Operators + +##### Purpose + +The comparison operators can for example be used in the `WHERE` clause to +specify the condition on which to match documents. + +#### Table 3. Comparison Operators + + +| Op | Description | Example | +| ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `=` or `==` | Equals | `WHERE v1 = v2
WHERE v1 == v2` | +| `!=` or <> | Not Equal to | `WHERE v1 != v2
WHERE v1 <> v2` | +| `>` | Greater than | `WHERE v1 > v2` | +| `>=` | Greater than or equal to | `WHERE v1 >= v2` | +| `<` | Less than | `WHERE v1 < v2` | +| `<=` | Less than or equal to | `WHERE v1 <= v2` | +| `IN` | Returns `TRUE` if the value is in the list or array of values specified by the right hand side expression; Otherwise returns `FALSE`. | `WHERE 'James' IN contactsList` | +| `LIKE` | String wildcard pattern matching, comparison - see 2. Two wildchards are supported:
• `%` Matches zero or more characters.
• \_` Matches a single character. | `WHERE name LIKE 'a%'`
`WHERE name LIKE '%a'`
`WHERE name LIKE '%or%'`
`WHERE name LIKE 'a%o%'`
`WHERE name LIKE '%_r%'`
`WHERE name LIKE '%a_%'`
`WHERE name LIKE '%a__%'`
`WHERE name LIKE 'aldo'`
| +| `MATCH` | String matching using FTS | `WHERE v1-index MATCH "value"` | +| `BETWEEN` | Logically equivalent to `v1 >= start AND v1 <= end` | `WHERE v1 BETWEEN 10 AND 100` | +| `IS NULL` - see 3 | Equal to `null` | `WHERE v1 IS NULL` | +| `IS NOT NULL` | Not equal to `null` | `WHERE v1 IS NOT NULL` | +| `IS MISSING` | Equal to MISSING | `WHERE v1 IS MISSING` | +| `IS NOT MISSING` | Not equal to MISSING | `WHERE v1 IS NOT MISSING` | +| `IS VALUED` | Logically equivalent to `IS NOT NULL AND MISSING` | `WHERE v1 IS VALUED` | +| `IS NOT VALUED` | Logically equivalent to `IS NULL OR MISSING` | `WHERE v1 IS NOT VALUED` | + +2. Matching is case-insensitive for ASCII characters, case-sensitive for + non-ASCII. +3. Use of `IS` and `IS NOT` is limited to comparing `NULL` and `MISSING` values + (this encompasses `VALUED`). This is different from `api|QueryBuilder`, in + which they operate as equivalents of `==` and `!=`. + + +#### Table 4. Comparing NULL and MISSING values using IS + + +| Op | Non-`NULL` Value | `NULL` | `MISSING` | +| ---------------- | ---------------- | ------- | --------- | +| `IS NULL` | `FALSE` | `TRUE` | `MISSING` | +| `IS NOT NULL` | `TRUE` | `FALSE` | `MISSING` | +| `IS MISSING` | `FALSE` | `FALSE` | `TRUE` | +| `IS NOT MISSING` | `TRUE` | `TRUE` | `FALSE` | +| `IS VALUED` | `TRUE` | `FALSE` | `FALSE` | +| `IS NOT VALUED` | `FALSE` | `TRUE` | `TRUE` | + + +#### Logical Operators + +##### Purpose + +Logical operators combine expressions using the following boolean logic rules: + +- `TRUE` is `TRUE`, and `FALSE` is `FALSE`. +- Numbers `0` or `0.0` are `FALSE`. +- Arrays and dictionaries are `FALSE`. +- Strings and Blobs are `TRUE` if the values are casted as a non-zero or `FALSE` + if the values are casted as `0` or `0.0`. +- `NULL` is `FALSE`. +- `MISSING` is `MISSING`. + +:::note + +This is different from SQL++ for Server, where: + +- `MISSING`, `NULL` and `FALSE` are `FALSE`. +- Numbers `0` is `FALSE`. +- Empty strings, arrays, and objects are `FALSE`. +- All other values are `TRUE`. + +::: + +:::tip + +Use the `TOBOOLEAN(expr)` function to convert a value based on SQL++ for Server +boolean value rules. + +::: + +#### Table 5. Logical Operators + + +| Op | Description | Example | +| ----- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------ | +| `AND` | Returns `TRUE` if the operand expressions evaluate to `TRUE`; otherwise `FALSE`.

If an operand is `MISSING` and the other is `TRUE` returns `MISSING`, if the other operand is `FALSE` it returns `FALSE`.

If an operand is `NULL` and the other is `TRUE` returns `NULL`, if the other operand is `FALSE` it returns `FALSE`. | `WHERE city = 'San Francisco' AND status = TRUE` | +| `OR` | Returns `TRUE` if one of the operand expressions is evaluated to `TRUE`; otherwise returns `FALSE`

If an operand is `MISSING`, the operation will result in `MISSING` if the other operand is `FALSE` or `TRUE` if the other operand is `TRUE`.

If an operand is `NULL`, the operation will result in `NULL` if the other operand is `FALSE` or `TRUE` if the other operand is `TRUE`. | `WHERE city = 'San Francisco' OR city = 'Santa Clara'` | + + +#### Table 6. Logical Operators Table + + +| `a` | `b` | `a AND b` | `a OR b` | +| --------- | --------- | -------------- | ---------------- | +| `TRUE` | `TRUE` | `TRUE` | `TRUE` | +| | `FALSE` | `FALSE` | `TRUE` | +| | `NULL` | `FALSE`, see 5 | `TRUE` | +| | `MISSING` | `MISSING` | `TRUE` | +| `FALSE` | `TRUE` | `FALSE` | `TRUE` | +| | `FALSE` | `FALSE` | `FALSE` | +| | `NULL` | `FALSE` | `FALSE`, see 5 | +| | `MISSING` | `FALSE` | `MISSING` | +| `NULL` | `TRUE` | `FALSE`, see 5 | `TRUE` | +| | `FALSE` | `FALSE` | `FALSE`, see 5 | +| | `NULL` | `FALSE`, see 5 | `FALSE`, see 5 | +| | `MISSING` | `FALSE`, see 6 | `MISSING`, see 7 | +| `MISSING` | `TRUE` | `MISSING` | `TRUE` | +| | `FALSE` | `FALSE` | `MISSING` | +| | `NULL` | `FALSE`, see 6 | `FALSE`, see 7 | +| | `MISSING` | `MISSING` | `MISSING` | + +This differs from SQL++ for Server in the following instances: + +- 5. Server will return: `NULL` instead of `FALSE`. +- 6. Server will return: `MISSING` instead of `FALSE`. +- 7. Server will return: `NULL` instead of `MISSING`. + +#### String Operators + +##### Purpose + +A single string operator is provided. It enables string concatenation. + +#### Table 7. String Operators + + +| Op | Description | Example | +| ------------ | ------------- | ---------------------------------------------------------- | +| `||` | Concatenating | `SELECT firstName || lastName AS fullName FROM db` | + + +### Unary Operators + +#### Purpose + +Three unary operators are provided. They operate by modifying an expression, +making it numerically positive or negative, or by logically negating its value +(`TRUE` becomes `FALSE`). + +#### Syntax + +#### Example 44. Unary Operators Syntax + + +```sql +unaryOperator = ( + | - | NOT ) _ expression +``` + + +#### Table 8. Unary Operators + +| Op | Description | Example | +| ----- | ------------------------------ | ----------------------------------- | +| `+` | Positive value | `WHERE v1 = +10` | +| `-` | Negative value | `WHERE v1 = -10` | +| `NOT` | Logical Negate operator, see 8 | `WHERE "James" NOT IN contactsList` | + +8. The `NOT` operator is often used in conjunction with operators such as `IN`, + `LIKE`, `MATCH`, and `BETWEEN` operators. + - `NOT` operation on `NULL` value returns `NULL`. + - `NOT` operation on `MISSING` value returns `MISSING`. + + +#### Table 9. NOT Operators + +| `a` | `NOT a` | +| --------- | --------- | +| `TRUE` | `FALSE` | +| `FALSE` | `TRUE` | +| `NULL` | `FALSE` | +| `MISSING` | `MISSING` | + + +### `COLLATE` Operator + +#### Purpose + +Collate operators specify how a string comparison is conducted. + +#### Usage + +The collate operator is used in conjunction with string comparison expressions +and `ORDER BY` clauses. It allows for one or more collations. If multiple +collations are used, the collations need to be specified in a parenthesis. When +only one collation is used, the parenthesis is optional. + +:::note + +Collation is not supported by SQL++ for Server. + +::: + +#### Syntax + +#### Example 45. COLLATE Operator Syntax + +```sql +collate = COLLATE _ ( collation | '(' collation ( _ collation )+ ')' ) +collation = NO? (UNICODE | CASE | DIACRITICS) +``` + +#### Arguments + +1. The available collation options are: + - `UNICODE`: Conduct a Unicode comparison; the default is to do ASCII + comparison. + - `CASE`: Conduct case-sensitive comparison + - `DIACRITIC`: Take accents and diacritics into account in the comparison; On + by default. + - `NO`: This can be used as a prefix to the other collations, to disable + them. For example, use `NOCASE` to enable case-insensitive comparison. + +#### Example + +#### Example 46. COLLATE Operator Example + +```sql +SELECT department +FROM db +WHERE name = "fred" COLLATE UNICODE; + +SELECT department +FROM db +WHERE name = "fred" COLLATE (UNICODE CASE); + +SELECT name +FROM db +ORDER BY name COLLATE (UNICODE DIACRITIC); +``` + +### Conditional Operator + +#### Purpose + +The conditional (or `CASE`) operator evaluates conditional logic in a similar +way to the `IF`/`ELSE` operator. + +#### Syntax + +#### Example 47. Conditional Operators Syntax + +``` +case = CASE _ ( expression _ )? + ( WHEN _ expression _ THEN _ expression _ )+ + ( ELSE _ expression _)? + END +``` + +Both _Simple Case_ and _Searched Case_ expressions are supported. The syntactic +difference being that the _Simple Case_ expression has an expression after the +`CASE` keyword. + +1. Simple Case Expression + - If the `CASE` expression is equal to the first `WHEN` expression, the + result is the `THEN` expression. + - Otherwise, any subsequent `WHEN` clauses are evaluated in the same way. + - If no match is found, the result of the `CASE` expression is the `ELSE` + expression, or `NULL` if no `ELSE` expression was provided. +2. Searched Case Expression + - If the first `WHEN` expression is `TRUE`, the result of this expression is + its `THEN` expression. + - Otherwise, subsequent `WHEN` clauses are evaluated in the same way. + - If no `WHEN` clause evaluate to `TRUE`, then the result of the expression + is the `ELSE` expression, or `NULL` if no `ELSE` expression was provided. + + +#### Examples + +#### Example 48. Simple Case + +```sql +SELECT + CASE state + WHEN 'CA' + THEN 'Local' + ELSE 'Non-Local' + END +FROM db; +``` + +#### Examples + +#### Example 49. Searched Case + + +```sql +SELECT + CASE + WHEN shippedOn IS NOT NULL + THEN 'SHIPPED' + ELSE 'NOT-SHIPPED' + END +FROM db; +``` + + +## Functions {#functions} + +### Purpose + +Functions provide specialised operations through a generalized syntax. + +### Syntax + +#### Example 50. Functions Syntax + + +The function syntax is the same as C-style language function syntax. It starts +with the function name, followed by optional arguments inside parentheses. + +``` +function = functionName _? '(' ( _? expression ( _? ',' _? expression )* _? )? ')' +functionName = IDENTIFIER +``` + +### Aggregation Functions + +#### Table 10. Aggregation Functions + +| Function | Description | +| -------------- | ------------------------------------------------------- | +| `AVG(value)` | Returns the average of all numeric values in the group. | +| `COUNT(value)` | Returns the count of all values in the group. | +| `MIN(value)` | Returns the minimum numeric value in the group. | +| `MAX(value)` | Returns the maximum numeric value in the group. | +| `SUM(value)` | Returns the sum of all numeric values in the group. | + + +### Array Functions + +#### Table 11. Array Functions + +| Function | Description | +| ----------------------- | ---------------------------------------------------------------------------------------------------- | +| `ARRAY_AGG(value)` | Returns an array of the non-`MISSING` group values in the input expression, including `NULL` values. | +| `ARRAY_AVG(value)` | Returns the average of all non-`NULL` number values in the array; or `NULL` if there are none. | +| `ARRAY_CONTAINS(value)` | Returns `TRUE` if the value exists in the array; otherwise `FALSE`. | +| `ARRAY_COUNT(value)` | Returns the number of non-`NULL` values in the array. | +| `ARRAY_IFNULL(value)` | Returns the first non-`NULL` value in the array. | +| `ARRAY_MAX(value)` | Returns the largest non-`NULL`, non_MISSING value in the array. | +| `ARRAY_MIN(value)` | Returns the smallest non-`NULL`, non_MISSING value in the array. | +| `ARRAY_LENGTH(value)` | Returns the length of the array. | +| `ARRAY_SUM(value)` | Returns the sum of all non-`NULL` numeric value in the array. | + + +### Conditional Functions + +#### Table 12. Conditional Functions + +| Function | Description | +| ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `IFMISSING(value, ...)` | Returns the first non-`MISSING` value, or `NULL` if all values are `MISSING`. | +| `IFMISSINGORNULL(value, ...)` | Returns the first non-`NULL` and non-`MISSING` value, or `NULL` if all values are `NULL` or `MISSING`. | +| `IFNULL(value, ...)` | Returns the first non-`NULL`, or `NULL` if all values are `NULL`. | +| `MISSINGIF(value, other)` | Returns `MISSING` when `value = other`; otherwise returns `value`.
Returns `MISSING` if either or both expressions are `MISSING`.
Returns `NULL` if either or both expressions are `NULL`. | +| `NULLIF(value, other)` | Returns `NULL` when `value = other`; otherwise returns `value`.
Returns `MISSING` if either or both expressions are `MISSING`.
Returns `NULL` if either or both expressions are `NULL`. | + + +### Date and Time Functions + +#### Table 13. Date and Time Functions + +| Function | Description | +| ---------------------- | --------------------------------------------------------------------------------------------------------------------------------- | +| `STR_TO_MILLIS(value)` | Returns the number of milliseconds since the unix epoch of the given ISO 8601 date input string. | +| `STR_TO_UTC(value)` | Returns the ISO 8601 UTC date time string of the given ISO 8601 date input string. | +| `MILLIS_TO_STR(value)` | Returns a ISO 8601 date time string in device local timezone of the given number of milliseconds since the unix epoch expression. | +| `MILLIS_TO_UTC(value)` | Returns the UTC ISO 8601 date time string of the given number of milliseconds since the unix epoch expression. | + + +### Full Text Search Functions + +#### Table 14. FTS Functions + +| Function | Description | Example | +| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------ | +| `MATCH(indexName, term`)` | Returns `TRUE` if `term` expression matches the FTS indexed document. `indexName` identifies the FTS index to search for matches. | `WHERE MATCH(description, 'couchbase')` | +| `RANK(indexName)` | Returns a numeric value indicating how well the current query result matches the full-text query when performing the MATCH. indexName is an IDENTIFIER for the FTS index. | `WHERE MATCH(description, 'couchbase') ORDER BY RANK(description)` | + + +### Maths Functions + +#### Table 15. Maths Functions + +| Function | Description | +| ------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ABS(value)` | Returns the absolute value of a number. | +| `ACOS(value)` | Returns the arc cosine in radians. | +| `ASIN(value)` | Returns the arcsine in radians. | +| `ATAN(value)` | Returns the arctangent in radians. | +| `ATAN2(a, b)` | Returns the arctangent of `a` / `b`. | +| `CEIL(value)` | Returns the smallest integer not less than the number. | +| `COS(value)` | Returns the cosine of an angle in radians. | +| `DIV(a, b)` | Returns float division of `a` and `b`. Both `a` and `b` are cast to a double number before division. The returned result is always a double. | +| `DEGREES(value)` | Converts radians to degrees. | +| `E()` | Returns the e constant, which is the base of natural logarithms. | +| `EXP(value)` | Returns the natural exponential of a number. | +| `FLOOR(value)` | Returns largest integer not greater than the number. | +| `IDIV(a, b)` | Returns integer division of `a` and `b`. | +| `LN(value)` | Returns log base e. | +| `LOG(value)` | Returns log base 10. | +| `PI()` | Returns the pi constant. | +| `POWER(a, b)` | Returns `a` to the `b`th power. | +| `RADIANS(value)` | Converts degrees to radians. | +| `ROUND(value (, digits)?)` | Returns the rounded value to the given number of integer digits to the right of the decimal point (left if digits is negative). Digits are 0 if not given.

The function uses Rounding Away From Zero convention to round midpoint values to the next number away from zero (so, for example, `ROUND(1.75)` returns 1.8 but `ROUND(1.85)` returns 1.9. | +| `ROUND_EVEN(value (, digits)?)` | Returns rounded value to the given number of integer digits to the right of the decimal point (left if digits is negative). Digits are 0 if not given.

The function uses Rounding to Nearest Even (Banker's Rounding) convention which rounds midpoint values to the nearest even number (for example, both `ROUND_EVEN(1.75)` and `ROUND_EVEN(1.85)` return 1.8). | +| `SIGN(value)` | Returns -1 for negative, 0 for zero, and 1 for positive numbers. | +| `SIN(value)` | Returns sine of an angle in radians. | +| `SQRT(value)` | Returns the square root. | +| `TAN(value)` | Returns tangent of an angle in radians. | +| `TRUNC(value (, digits)?)` | Returns a truncated number to the given number of integer `digits` to the right of the decimal point (left if digits is negative). Digits are 0 if not given. | + +:::note + +The behavior of the `ROUND()` function is different from SQL++ for Server +`ROUND()`, which rounds the midpoint values using Rounding to Nearest Even +convention. + +::: + +### Pattern Searching Functions + +#### Table 16. Pattern Searching Functions + +| Function | Description | +| -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `REGEXP_CONTAINS(value, pattern)` | Returns `TRUE` if the string value contains any sequence that matches the regular expression pattern. | +| `REGEXP_LIKE(value, pattern)` | Return `TRUE` if the string value exactly matches the regular expression pattern. | +| `REGEXP_POSITION(value, pattern)` | Returns the first position of the occurrence of the regular expression pattern within the input string expression. Returns `-1` if no match is found. Position counting starts from zero. | +| `REGEXP_REPLACE(value, pattern, repl [, n])` | Returns a new string with occurrences of `pattern` replaced with `repl`. If `n` is given, at the most `n` replacements are performed. If `n` is not given, all matching occurrences are replaced. | + + +### String Functions + +#### Table 17. String Functions + +| Function | Description | +| ---------------------------- | ---------------------------------------------------------------------------------------------------- | +| `CONTAINS(value, substring)` | Returns `TRUE` if the substring exists within the input string, otherwise returns `FALSE`. | +| `LENGTH(value)` | Returns the length of a string. The length is defined as the number of characters within the string. | +| `LOWER(value)` | Returns the lower-case string of the input string. | +| `LTRIM(value)` | Returns the string with all leading whitespace characters removed. | +| `RTRIM(value)` | Returns the string with all trailing whitespace characters removed. | +| `TRIM(value)` | Returns the string with all leading and trailing whitespace characters removed. | +| `UPPER(value)` | Returns the upper-case string of the input string. | + + +### Type Checking Functions + +#### Table 18. Type Checking Functions + +| Function | Description | +| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `ISARRAY(value)` | Returns `TRUE` if `value` is an array, otherwise returns `MISSING`, `NULL` or `FALSE`. | +| `ISATOM(value)` | Returns `TRUE` if `value` is a boolean, number, or string, otherwise returns `MISSING`, `NULL` or `FALSE`. | +| `ISBOOLEAN(value)` | Returns `TRUE` if `value` is a boolean, otherwise returns `MISSING`, `NULL` or `FALSE`. | +| `ISNUMBER(value)` | Returns `TRUE` if `value` is a number, otherwise returns `MISSING`, `NULL` or `FALSE`. | +| `ISOBJECT(value)` | Returns `TRUE` if `value` is an object (dictionary), otherwise returns `MISSING`, `NULL` or `FALSE`. | +| `ISSTRING(value)` | Returns `TRUE` if `value` is a string, otherwise returns `MISSING`, `NULL` or `FALSE`. | +| `TYPE(value)` | Returns one of the following strings, based on the value of `value`:
• "missing"
• "null"
• "boolean"
• "number"
• "string"
• "array"
• "object"
• "binary" | + + +### Type Conversion Functionsunctions + +#### Table 19. Type Conversion Functions + +| Function | Description | +| ------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `TOARRAY(value)` | Returns `MISSING` if the value is `MISSING`.
Returns `NULL` if the value is `NULL`.
Returns an array value as is.
Returns all other values wrapped in an array. | +| `TOATOM(value)` | Returns `MISSING` if the value is `MISSING`.
Returns `NULL` if the value is `NULL`.
Returns an array of a single item if the value is an array.
Returns an object of a single key/value pair if the value is an object.
Returns a boolean, number, or string value as is.
Returns `NULL` for all other values. | +| `TOBOOLEAN(value)` | Returns `MISSING` if the value is `MISSING`.
Returns `NULL` if the value is `NULL`.
Returns `FALSE` if the value is `FALSE`.
Returns `FALSE` if the value is `0` or `NaN`.
Returns `FALSE` if the value is an empty string, array, and object.
Return `TRUE` for all other values. | +| `TONUMBER(value)` | Returns `MISSING` if the value is `MISSING`.
Returns `NULL` if the value is `NULL`.
Returns `0` if the value is `FALSE`.
Returns `1` if the value is `TRUE`.
Returns a number value as is.
Parses a string value in to a number.
Returns `NULL` for all other values. | +| `TOOBJECT(value)` | Returns `MISSING` if the value is `MISSING`.
Returns `NULL` if the value is `NULL`.
Returns an object value as is.
Returns an empty object for all other values. | +| `TOSTRING(value)` | Returns `MISSING` if the value is `MISSING`.
Returns `NULL` if the value is `NULL`.
Returns "false" if the value is `FALSE`.
Returns "true" if the value is `TRUE`.
Returns a string representation of a number value.
Returns a string value as is.
Returns `NULL` for all other values. | + + +## QueryBuilder Differences {#querybuilder-differences} + +SQL++ for Mobile queries support all `QueryBuilder` features. See [Table 20](#) +for the features supported by SQL++ for Mobile but not by `QueryBuilder`. + +#### Table 20. QueryBuilder Differences + +| Category | Components | +| -------------------------- | ------------------------------------------------------------------------------------------------------------- | +| Conditional Operator | `CASE(WHEN ... THEN ... ELSE ...)` | +| Array Functions | `ARRAY_AGG`, `ARRAY_AVG`, `ARRAY_COUNT`, `ARRAY_IFNULL`, `ARRAY_MAX`, `ARRAY_MIN`, `ARRAY_SUM` | +| Conditional Functions | `IFMISSING`, `IFMISSINGORNULL`, `IFNULL`, `MISSINGIF`, `NULLIF`, `MATCH`, `RANK`, `DIV`, `IDIV`, `ROUND_EVEN` | +| Pattern Matching Functions | `REGEXP_CONTAINS`, `REGEXP_LIKE`, `REGEXP_POSITION`, `REGEXP_REPLACE` | +| Type Checking Functions | `ISARRAY`, `ISATOM`, `ISBOOLEAN`, `ISNUMBER`, `ISOBJECT`, `ISSTRING`, `TYPE` | +| Type Conversion Functions | `TOARRAY`, `TOATOM`, `TOBOOLEAN`, `TONUMBER`, `TOOBJECT`, `TOSTRING` | + + +## Query Parameters {#query-parameters} + +You can provide runtime parameters to your SQL++ query to make it more flexible. +To specify substitutable parameters within your query string prefix the name +with `$` — see: [Example 51](#). + +#### Example 51. Running a SQL++ Query + +```dart +final db = await Database.openAsync('hotel'); +final query = await Query.fromN1ql( + db, + r''' + SELECT META().id AS docId + FROM _ + WHERE type = $type + ''', +); +query.parameters = Parameters({'type': 'hotel'}); +final resultSet = query.execute(); +``` + +1. Define a parameter placeholder `$type`. +2. Set the value of the `type` parameter. \ No newline at end of file diff --git a/docs/blobs.md b/docs/blobs.md index dc8494d..994e282 100644 --- a/docs/blobs.md +++ b/docs/blobs.md @@ -22,7 +22,7 @@ The blob as an object appears in a document as dictionary property — see, fo Other properties include `length` (the length in bytes), and optionally `content_type` (typically, its MIME type). -The blob's data (an image, audio or video content) is not stored in the document, but in a separate content-addressable store, indexed by the `digest` property — see [Using Blobs]. +The blob's data (an image, audio or video content) is not stored in the document, but in a separate content-addressable store, indexed by the `digest` property — see [Using Blobs](#using-blobs). ### Constraints @@ -50,20 +50,20 @@ const imageData = await fetchAvatarImageData(); const avatarBlob = new Blob('image/jpeg', imageData); // Retrieve an existing document -let document = await database.getDocument(documentId); +let document = await collection.document(documentId); // Assign the Blob to the document under the 'avatar' key document.setBlob('avatar', avatarBlob); // Save the updated document back to the database -await database.save(document); +await collection.save(document); ``` ## Syncing When a document containing a blob object is synchronized, the Couchbase Lite replicator generates an `_attachments` dictionary with an auto-generated name for each blob attachment. This is different to the `avatar` key and is used internally to access the blob content. -If you view a sync'd blob document in either Capella's Admin Interface or Couchbase Server's Admin Console, you will see something similar to [Figure 1], which shows the document with its generated `_attachments` dictionary, including the digest. +If you view a sync'd blob document in either Capella's Admin Interface or Couchbase Server's Admin Console, you will see something similar to [Figure 1](#figure-1-sample-blob-document), which shows the document with its generated `_attachments` dictionary, including the digest. #### Figure 1. Sample Blob Document diff --git a/docs/database-prebuilt.md b/docs/database-prebuilt.md index fe15277..d373d04 100644 --- a/docs/database-prebuilt.md +++ b/docs/database-prebuilt.md @@ -108,10 +108,20 @@ Start your normal application logic immediately, unless it is essential to have #### Example 1. Decompress and Copy Database using API ```typescript -// Check if the database already exists -if (!await Database.exists(dbName, null)) { - // Copy the database from the sourcePath to the app's directory - await Database.copy(sourcePath, dbName, new DatabaseConfiguration()); +// Get the default path for database storage from the file system +const pd = new FileSystem(); +const result = await pd.getDefaultPath(); +const path = result; // Assuming you want to use this path further in your logic + +// Set database name, source path, and configuration +const dbName = 'my_prebuilt_db'; +const sourcePath = await database.getPath(); +const config = new DatabaseConfiguration(); + +const databaseExists = await database.exists(dbName, sourcePath); +if (!databaseExists) { + // Copy the database from the sourcePath to the app's directory if it doesn't already exist + await database.copy(sourcePath, dbName, config); } ``` diff --git a/docs/databases.md b/docs/databases.md index cdad2ea..874f3b5 100644 --- a/docs/databases.md +++ b/docs/databases.md @@ -57,7 +57,7 @@ In an Ionic application using Couchbase Lite, begin by initializing the Capacito **Example: Initializing Capacitor Engine and Database Context** ```javascript -import { CapacitorEngine } from 'couchbase-lite-ee-ionic'; +import { CapacitorEngine } from 'cbl-ionic'; const engine = new CapacitorEngine(); // Initialize once, early in your app ``` @@ -66,13 +66,15 @@ This configuration ensures seamless interaction between your Ionic app and the u ### Create or Open a Database -To create or open a database, use the Database class from the cblite-core package, specifying the database name and optionally, a DatabaseConfiguration for custom settings like the database directory or encryption. +To create or open a database, use the Database class from the cbl-cblite package, specifying the database name and optionally, a DatabaseConfiguration for custom settings like the database directory or encryption. **Example 1. Creating/Opening a Database** ```javascript -import { Database, DatabaseConfiguration } from 'cblite-core'; +import { Database, DatabaseConfiguration } from 'cbl-cblite'; //import the package +``` +```javascript const config = new DatabaseConfiguration(); config.setDirectory('path/to/database'); // Optional const myDatabase = new Database('myDatabaseName', config); @@ -102,9 +104,13 @@ To enable database encryption in Ionic, use the `DatabaseConfiguration` class to ```typescript const dbName = 'my_secure_db'; const encryptionKey = 'my_secret_key'; -const config = new DatabaseConfiguration().setEncryptionKey(encryptionKey); + +const config = new DatabaseConfiguration(); +config.setEncryptionKey(encryptionKey); + const db = new Database(dbName, config); -await db.open(); + +await db.open();dar ``` ### Persisting @@ -119,9 +125,9 @@ An encrypted database can only be opened with the same language package that was From time to time it may be necessary to perform certain maintenance activities on your database, for example to compact the database file, removing unused documents and blobs no longer referenced by any documents. -```typescript -await db.compact(); -``` +Couchbase Lite's API provides the Database.performMaintenance method. The available maintenance operations, including compact are as shown in the enum MaintenanceType to accomplish this. + +This is a resource intensive operation and is not performed automatically. It should be run on-demand using the API. If in doubt, consult Couchbase support. ## Command Line Tool @@ -145,11 +151,12 @@ You should use console logs as your first source of diagnostic information. If t ```typescript -import { Database, LogDomain, LogLevel } from 'cblite-core'; - -db.setLogLevel(LogDomain.DATABASE, LogLevel.VERBOSE) - .then(() => console.log('Database log level set to VERBOSE.')) - .catch(error => console.error('Setting log level failed:', error)); +try { + await db.setLogLevel(LogDomain.DATABASE, LogLevel.VERBOSE); + console.log('Database log level set to VERBOSE.'); +} catch (error) { + console.error('Setting log level failed:', error); +} ``` diff --git a/docs/documents.md b/docs/documents.md index 39e6064..9234767 100644 --- a/docs/documents.md +++ b/docs/documents.md @@ -180,7 +180,7 @@ Couchbase recommend using a type attribute to define each logical document type. With the document now populated, we can persist to our Couchbase Lite database. ```typescript -db.save(hotelInfo); +await collection.save(document); ``` ### Close the Database @@ -188,13 +188,11 @@ db.save(hotelInfo); With your document saved, you can now close our Couchbase Lite database. ```typescript -db.close(); +database.close(); ``` ## Working with Data -### Checking a Document's Properties - ### Date accessors Couchbase Lite offers Date accessors as a convenience. Dates are a common data type, but JSON doesn’t natively support them, so the convention is to store them as strings in ISO-8601 format. @@ -216,14 +214,11 @@ const date = document.getDate("createdAt"); ### Using Dictionaries -#### API References - - #### Example 2. Read Only ```typescript // Get a document by ID -const doc: Document = database.getDocument('doc1'); +const doc: Document = collection.document('doc1'); // Getting a dictionary from the document's properties const addressDict = doc.getDictionary('address'); @@ -254,14 +249,11 @@ const mutableDoc = new MutableDocument("doc1"); mutableDoc.setDictionary("address", addressDict); // Simulate saving the document -mutableDoc.save(); +await collection.save(mutableDoc); ``` ### Using Arrays -#### API References - - #### Example 4. Read Only ```typescript @@ -299,7 +291,7 @@ const phones = ["650-000-0000", "650-000-0001"]; doc.setArray("phones", phones); // Save the document with the new array to the database -await database.save(doc); +await collection.save(doc); ``` ### Using Blobs @@ -308,9 +300,11 @@ For more on working with blobs, see [Blobs](blobs.md) ## Document Initializers -The `MutableDocument` constructor is utilized to create a new document instance. If no document ID is specified, the database generates a unique ID for the document automatically. +The `MutableDocument` constructor can be used to create a new document where the document ID is randomly generated by the database. + +Use the `MutableDocument('specific_id')` initializer to create a new document with a specific ID. -The `Database.getDocument` method can be used to get a document. If it doesn't exist in the database, it will return null. This method can be used to check if a document with a given ID already exists in the database. +The `Collection.document` method can be used to get a document. If it doesn't exist in the collection, it will return null. This method can be used to check if a document with a given ID already exists in the collection. #### Example 6. Persist a document @@ -326,32 +320,22 @@ document.setString('owner', 'todo'); document.setDate('createdAt', new Date()); // Persist the document to the database -await database.save(document); +await collection.save(document); ``` -## Mutability - -## Batch Operations - -If you’re making multiple changes to a database at once, it’s faster to group them together. The following example persists a few documents in batch. - -#### Example 8. Batch Operations - ## Document change events It is possible to register for document changes. The following example registers for changes to the document with ID user.john and prints the verified_account property when a change is detected. ```typescript -const token = database.addDocumentChangeListener('user.john', async (change) => { - const document = await database.getDocument(change.documentID); +const token = collection.addDocumentChangeListener('user.john', async (change) => { + const document = await collection.document(change.documentID); if (document !== null) { console.log(`Status: ${document.getString('verified_account')}`); } }); ``` -## Document Expiration - ## Document Constraints Couchbase Lite APIs do not explicitly disallow the use of attributes with the underscore prefix at the top level of document. This is to facilitate the creation of documents for use either in local only mode where documents are not synced. @@ -369,9 +353,6 @@ Couchbase Lite APIs do not explicitly disallow the use of attributes with the un * `_rev` * `_sequence` -## Working with JSON Data - - diff --git a/docs/indexes.md b/docs/indexes.md index 96ff01b..aab3209 100644 --- a/docs/indexes.md +++ b/docs/indexes.md @@ -3,4 +3,60 @@ id: indexes sidebar_position: 11 --- -# Indexing \ No newline at end of file +# Indexing + +> Description — _Couchbase mobile database indexes and indexing concepts_ +> Related Content — [Databases](databases.md) | [Documents](documents.md) | [Indexing](indexes.md) + +## Introduction + +Before we begin querying documents, let's briefly mention the importance of having an appropriate and balanced approach to indexes. + +Creating indexes can speed up the performance of queries. A query will typically return results more quickly if it can take advantage of an existing database index to search, narrowing down the set of documents to be examined. + +:::note CONSTRAINTS +Couchbase Lite does not currently support partial value indexes; indexes with non-property expressions. You should only index with properties that you plan to use in the query. +::: + +## Creating a new index + +You can use SQL++ to create an index + +[Example 2](#example-2-create-index) creates a new index for the type and name properties, shown in this data model: + +**Example 1. Data Model** + +```json +{ + "_id": "hotel123", + "type": "hotel", + "name": "The Michigander", + "overview": "Ideally situated for exploration of the Motor City and the wider state of Michigan. Tripadvisor rated the hotel ...", + "state": "Michigan" +} +``` + +The code to create the index will look something like this: + +#### Example 2. Create Index + +```typescript + // Define a value index on 'name' and 'documentType' +const valueIndex = IndexBuilder.valueIndex( + ValueIndexItem.property('name'), + ValueIndexItem.property('documentType') +); + +// Create the value index +const valueIndexName = 'nameTypeIndex'; +await collection.createIndex(valueIndexName, valueIndex); +``` + +## Summary + +When planning the indexes you need for your database, remember that while indexes make queries faster, they may also: + +* Make writes slightly slower, because each index must be updated whenever a document is updated. +* Make your Couchbase Lite database slightly larger. + +So too many indexes may hurt performance. Optimal performance depends on designing and creating the right indexes to go along with your queries. \ No newline at end of file diff --git a/docs/scopes-collections.md b/docs/scopes-collections.md index 5ffc4ea..21431c3 100644 --- a/docs/scopes-collections.md +++ b/docs/scopes-collections.md @@ -3,4 +3,96 @@ id: scopes-collections sidebar_position: 6 --- -# Scopes and Collections \ No newline at end of file +# Scopes and Collections + +> Description — _Scopes and collections allow you to organize your documents within a database._ + +:::important AT A GLANCE +**Use collections to organize your content in a database** + +For example, if your database contains travel information, airport documents can be assigned to an airports collection, hotel documents can be assigned to a hotels collection, and so on. + +* Document names must be unique within their collection. + +**Use scopes to group multiple collections** + +Collections can be assigned to different scopes according to content-type or deployment-phase (for example, test versus production). + +* Collection names must be unique within their scope. +::: + +## Default Scopes and Collections + +Every database you create contains a default scope and a default collection named `_default`. + +If you create a document in the database and don't specify a specific scope or collection, it is saved in the default collection, in the default scope. + +If you upgrade from a version of Couchbase Lite prior to support for scops and collections, all existing data is automatically placed in the default scope and default collection. + +The default scope and collection cannot be dropped. + +## Create a Scope and Collection + +In addition to the default scope and collection, you can create your own scope and collection when you create a document. + +Naming conventions for collections and scopes: + +* Must be between 1 and 251 characters in length. +* Can only contain the characters `A-Z`, `a-z`, `0-9`, and the symbols `_`, `-`, and `%`. +* Cannot start with `_` or `%`. +* Scope names must be unique in databases. +* Collection names must be unique within a scope. + +:::note +Scope and collection names are case sensitive. +::: + +**Example 1. Create a scope and collection** + +```typescript +const collection = await database.createCollection(collectionName, scopeName); +``` +In the example above, you can see that `Database.createCollection` can take two parameters. The second is the scope assigned to the created collection, if this parameter is omitted then a collection of the given name will be assigned to the `_default` scope. + +The first parameter is the name of the collection you want to create, in this case `myCollectionName`. + +If a collection with the specified name already exists in the specified scope, `Database.createCollection` returns the existing collection. + +:::note +You cannot create an empty user-defined scope. A scope is implicitly created and removed by the `Database.createCollection` and `Database.deleteCollection` methods. +::: + +## Index a Collection + +**Example 2. Index a Collection** + +```typescript + +``` + +## Drop a Collection + +**Example 3. Drop a Collection** + +```typescript +await database.deleteCollection(collectionName, scopeName); +``` + +## List Scopes and Collections + +**Example 4. List Scopes and Collections** + +```typescript +// Get Scopes +const scopes = await database.scopes(); + +// Get Collections of a particular Scope +const collections = await database.collections(scopeName); +``` + + + + + + + From 33b3a0801ba5e28ec0ab16161a2369e2c96b30ff Mon Sep 17 00:00:00 2001 From: Dhiraj Kumar Azad Date: Wed, 22 May 2024 19:02:07 +0530 Subject: [PATCH 2/5] Updates based on comments --- docs/Queries/sqlplusplus.md | 164 ++++++++++++++++++------------------ docs/database-prebuilt.md | 7 +- docs/databases.md | 4 +- docs/documents.md | 3 + docs/scopes-collections.md | 10 ++- 5 files changed, 99 insertions(+), 89 deletions(-) diff --git a/docs/Queries/sqlplusplus.md b/docs/Queries/sqlplusplus.md index b156f7f..eb72e96 100644 --- a/docs/Queries/sqlplusplus.md +++ b/docs/Queries/sqlplusplus.md @@ -23,7 +23,7 @@ Use `Database.createQuery` to define a query through an SQL++ string. Then run t #### Example 1. Running a SQL++ Query ```typescript -const query = database.createQuery("SELECT META().id AS thisId FROM _ WHERE type = \"hotel\""); +const query = database.createQuery('SELECT META().id AS thisId FROM inventory.hotel WHERE city="Medway"'); const resultSet = await query.execute(); ``` @@ -104,7 +104,7 @@ This behavior is inline with that of SQL++ for Server — see example in [Table | `SELECT * AS data FROM _` | `data` | | `SELECT * FROM _` | `_` | | `SELECT * FROM _default` | `_default` | -| `SELECT * FROM users` | `users` | +| `SELECT * FROM users` | `users` | | `SELECT * FROM users AS user` | `user` | ### Example @@ -149,12 +149,13 @@ Here `dataSource` is the collection name against which the query is to run. Use #### Example 6. FROM Examples ```SQL -SELECT name FROM user; -SELECT user.name FROM users AS user; -SELECT user.name FROM users user; -SELECT name FROM _; -SELECT user.name FROM _ AS user; -SELECT user.name FROM _ user; +SELECT name FROM testScope.user; +SELECT user.name FROM testScope.users AS user; +SELECT user.name FROM testScope.users user; +-- These queries use the default scope and default collection (_default._default) in Couchbase. +SELECT name FROM _default._default; +SELECT user.name FROM _default._default AS user; +SELECT user.name FROM _default._default user; ``` ## JOIN Clause @@ -190,11 +191,11 @@ collectionAlias = IDENTIFIER ```SQL SELECT users.prop1, other.prop2 -FROM users +FROM testScope.users JOIN users AS other ON users.key = other.key; SELECT users.prop1, other.prop2 -FROM users +FROM testScope.users LEFT JOIN users AS other ON users.key = other.key; ``` @@ -204,7 +205,7 @@ This example joins the documents from the `routes` collections with documents fr ```SQL SELECT * -FROM routes r +FROM inventory.routes r JOIN airlines a ON r.airlineId = META(a).id WHERE a.country = "France"; ``` @@ -233,7 +234,7 @@ where = WHERE _ expression ```SQL SELECT name -FROM employees +FROM testScope.employees WHERE department = "engineer" AND group = "mobile" ``` @@ -265,16 +266,16 @@ having = HAVING _ expression ```SQL SELECT COUNT(airlineId), destination -FROM routes +FROM inventory.routes GROUP BY destination; SELECT COUNT(airlineId), destination -FROM routes +FROM inventory.routes GROUP BY destination HAVING COUNT(airlineId) > 100; SELECT COUNT(airlineId), destination -FROM routes +FROM inventory.routes WHERE destinationState = "CA" GROUP BY destination HAVING COUNT(airlineId) > 100; @@ -308,15 +309,15 @@ order = ( ASC | DESC ) ```SQL SELECT name -FROM users +FROM testScope.users ORDER BY name; SELECT name -FROM users +FROM testScope.users ORDER BY name DESC; SELECT name, score -FROM users +FROM testScope.users ORDER BY name ASC, score DESC; ``` @@ -344,7 +345,7 @@ limit = LIMIT _ expression ```SQL SELECT name -FROM users +FROM testScope.users LIMIT 10; ``` @@ -375,11 +376,11 @@ offset = OFFSET _ expression ```sql SELECT name -FROM db +FROM testScope.users OFFSET 10; SELECT name -FROM db +FROM testScope.users LIMIT 10 OFFSET 10; ``` @@ -413,11 +414,11 @@ boolean = ( TRUE | FALSE ) ```sql SELECT value -FROM db +FROM testScope.testCollection WHERE value = true; SELECT value -FROM db +FROM testScope.testCollection WHERE value = false; ``` @@ -452,7 +453,7 @@ SELECT 10.25E2, 10.25E+2, 10.25E-2 -FROM db; +FROM testScope.testCollection; ``` #### String @@ -485,8 +486,8 @@ The string literal can be double-quoted as well as single-quoted. ```sql SELECT firstName, lastName -FROM db -WHERE middleName = "middle" AND lastName = 'last'; +FROM crm.customer +WHERE contact.middleName = "middle" AND contact.lastName = 'last'; ``` #### NULL @@ -509,8 +510,8 @@ null = NULL ```sql SELECT firstName, lastName -FROM db -WHERE middleName IS NULL; +FROM crm.customer +WHERE contact.middleName IS NULL; ``` #### MISSING @@ -533,8 +534,8 @@ missing = MISSING ```sql SELECT firstName, lastName -FROM db -WHERE middleName IS MISSING; +FROM crm.customer +WHERE contact.middleName IS MISSING; ``` #### Array @@ -557,10 +558,10 @@ array = [ ( _? expression ( _? ',' _? expression )* _? )? ] ```sql SELECT ["a", "b", "c"] -FROM db; +FROM testScope.testCollection SELECT [property1, property2, property3] -FROM db; +FROM testScope.testCollection ``` #### Dictionary @@ -584,13 +585,13 @@ dictionary = { ( _? string _? : _? expression ( _? , _? string _? : _? expressio ```sql SELECT { 'name': 'James', 'department': 10 } -FROM db; +FROM testScope.testCollection; SELECT { 'name': 'James', 'department': dept } -FROM db; +FROM testScope.testCollection; SELECT { 'name': 'James', 'phones': ['650-100-1000', '650-100-2000'] } -FROM db; +FROM testScope.testCollection; ``` ### Identifier @@ -624,6 +625,8 @@ To use other than basic characters in the identifier, surround the identifier with the backticks ` character. For example, to use a hyphen (-) in an identifier, use backticks to surround the identifier. +Please note that backticks are commonly used for string literals/interpolation in TypeScript/JavaScript. Therefore, you should be aware that backticks need to be escaped properly to function correctly in TypeScript/JavaScript. For more information, refer to [Template Literal Types in TypeScript](https://www.typescriptlang.org/docs/handbook/2/template-literal-types.html). + ::: #### Example @@ -631,20 +634,21 @@ identifier, use backticks to surround the identifier. #### Example 35. Identifier Examples ```sql +-- This query uses the default scope and default collection (_default._default) in Couchbase. SELECT * -FROM _; +FROM _default._default; SELECT * -FROM `db-1`; +FROM test-scope.test-collection; SELECT key -FROM db; +FROM testScope.testCollection; SELECT key$1 -FROM db_1; +FROM test_Scope.test_Collection; SELECT `key-1` -FROM db; +FROM testScope.testCollection; ``` ### Property Expression @@ -677,20 +681,20 @@ propertyName = IDENTIFIER ```sql SELECT * -FROM db -WHERE contact.name = 'daniel'; +FROM crm.customer +WHERE contact.firstName = 'daniel'; -SELECT db.* -FROM db -WHERE contact.name = 'daniel'; +SELECT crm.customer.* +FROM crm.customer +WHERE contact.firstName = 'daniel'; -SELECT db.contact.address.city -FROM db -WHERE contact.name = 'daniel'; +SELECT crm.customer.contact.address.city +FROM crm.customer +WHERE contact.firstName = 'daniel'; SELECT contact.address.city, contact.phones[0] -FROM db -WHERE contact.name = 'daniel'; +FROM crm.customer +WHERE contact.firstName = 'daniel'; ``` ### Any and Every Expression @@ -741,8 +745,8 @@ variableName = IDENTIFIER ```sql -SELECT name -FROM db +SELECT firstName, lastName +FROM crm.customer WHERE ANY contact IN contacts SATISFIES contact.city = 'San Mateo' @@ -778,24 +782,20 @@ parameter = $ IDENTIFIER ```sql -SELECT name -FROM db -WHERE department = $department; +SELECT * +FROM crm.customer +WHERE contact.firstName = $firstName; ``` #### Example 42. Using a Parameter -```dart -final query = await Query.fromN1ql( - db, - r''' - SELECT name - WHERE department = $department - ''', -); -query.parameters = Parameters({'department': 'E001'}); -final results = query.execute(); +```typescript +const query = database.createQuery('SELECT * FROM crm.customer WHERE contact.firstName = $firstName'); +const params = new Parameters(); +params.setValue('firstName', 'daniel'); +query.parameters = params; +const resultSet = await query.execute(); ``` ### Parenthesis Expression @@ -812,14 +812,14 @@ establish operator precedence. ```sql SELECT (value1 + value2) * value 3 -FROM db; +FROM testScope.testCollection SELECT * -FROM db; +FROM testScope.testCollection WHERE ((value1 + value2) * value3) + value4 = 10; SELECT * -FROM db +FROM testScope.testCollection WHERE (value1 = value2) OR (value3 = value4); ``` @@ -972,9 +972,9 @@ A single string operator is provided. It enables string concatenation. #### Table 7. String Operators -| Op | Description | Example | -| ------------ | ------------- | ---------------------------------------------------------- | -| `||` | Concatenating | `SELECT firstName || lastName AS fullName FROM db` | +| Op | Description | Example | +| ------------ | ------------- | -------------------------------------------------------------------------------- | +| `||` | Concatenating | `SELECT firstName || lastName AS fullName FROM testScope.testCollection` | ### Unary Operators @@ -1063,17 +1063,17 @@ collation = NO? (UNICODE | CASE | DIACRITICS) #### Example 46. COLLATE Operator Example ```sql -SELECT department -FROM db -WHERE name = "fred" COLLATE UNICODE; +SELECT contact +FROM crm.customer +WHERE contact.firstName = "fred" COLLATE UNICODE; -SELECT department -FROM db -WHERE name = "fred" COLLATE (UNICODE CASE); +SELECT contact +FROM crm.customer +WHERE contact.firstName = "fred" COLLATE (UNICODE CASE); -SELECT name -FROM db -ORDER BY name COLLATE (UNICODE DIACRITIC); +SELECT firstName, lastName +FROM crm.customer +ORDER BY firstName COLLATE (UNICODE DIACRITIC), lastName COLLATE (UNICODE DIACRITIC); ``` ### Conditional Operator @@ -1123,7 +1123,7 @@ SELECT THEN 'Local' ELSE 'Non-Local' END -FROM db; +FROM testScope.testCollection; ``` #### Examples @@ -1138,7 +1138,7 @@ SELECT THEN 'SHIPPED' ELSE 'NOT-SHIPPED' END -FROM db; +FROM testScope.testCollection; ``` diff --git a/docs/database-prebuilt.md b/docs/database-prebuilt.md index d373d04..d477ea8 100644 --- a/docs/database-prebuilt.md +++ b/docs/database-prebuilt.md @@ -108,15 +108,14 @@ Start your normal application logic immediately, unless it is essential to have #### Example 1. Decompress and Copy Database using API ```typescript -// Get the default path for database storage from the file system -const pd = new FileSystem(); -const result = await pd.getDefaultPath(); -const path = result; // Assuming you want to use this path further in your logic +// Get the default path +const path = await pd.getDefaultPath(); // Set database name, source path, and configuration const dbName = 'my_prebuilt_db'; const sourcePath = await database.getPath(); const config = new DatabaseConfiguration(); +config.setDirectory(path); const databaseExists = await database.exists(dbName, sourcePath); if (!databaseExists) { diff --git a/docs/databases.md b/docs/databases.md index 874f3b5..10f7087 100644 --- a/docs/databases.md +++ b/docs/databases.md @@ -110,7 +110,7 @@ config.setEncryptionKey(encryptionKey); const db = new Database(dbName, config); -await db.open();dar +await db.open(); ``` ### Persisting @@ -127,7 +127,7 @@ From time to time it may be necessary to perform certain maintenance activities Couchbase Lite's API provides the Database.performMaintenance method. The available maintenance operations, including compact are as shown in the enum MaintenanceType to accomplish this. -This is a resource intensive operation and is not performed automatically. It should be run on-demand using the API. If in doubt, consult Couchbase support. +This is a resource intensive operation and is not performed automatically. It should be run on-demand using the API. For questions or issues, please visit the [Couchbase Forums](https://www.couchbase.com/forums/) where you can ask for help and discuss with the community. ## Command Line Tool diff --git a/docs/documents.md b/docs/documents.md index 9234767..bb54b9f 100644 --- a/docs/documents.md +++ b/docs/documents.md @@ -334,6 +334,9 @@ const token = collection.addDocumentChangeListener('user.john', async (change) = console.log(`Status: ${document.getString('verified_account')}`); } }); + +// Remove the change listener when it is no longer needed +await collection.removeDocumentChangeListener(token); ``` ## Document Constraints diff --git a/docs/scopes-collections.md b/docs/scopes-collections.md index 21431c3..7ad8f70 100644 --- a/docs/scopes-collections.md +++ b/docs/scopes-collections.md @@ -67,7 +67,15 @@ You cannot create an empty user-defined scope. A scope is implicitly created and **Example 2. Index a Collection** ```typescript - + // Define a value index on 'name' and 'documentType' +const valueIndex = IndexBuilder.valueIndex( + ValueIndexItem.property('name'), + ValueIndexItem.property('documentType') +); + +// Create the value index +const valueIndexName = 'nameTypeIndex'; +await collection.createIndex(valueIndexName, valueIndex); ``` ## Drop a Collection From a2a9fbed67d2f836803684adb99ad28b0b56827c Mon Sep 17 00:00:00 2001 From: Dhiraj Kumar Azad Date: Tue, 28 May 2024 15:23:31 +0530 Subject: [PATCH 3/5] added sqlpp and fts docs --- docs/Queries/live-queries.md | 49 ++- docs/Queries/query-builder.md | 6 - docs/Queries/query-result-set.md | 3 +- docs/Queries/query-troubleshooeting.md | 323 +++++++++++++++++- ...lplusplus-and-query-builder-differences.md | 6 - ...lplusplus-mobile-and-server-differences.md | 120 ++++++- docs/documents.md | 17 +- docs/full-text-search.md | 157 ++++++++- docs/scopes-collections.md | 9 +- 9 files changed, 649 insertions(+), 41 deletions(-) delete mode 100644 docs/Queries/query-builder.md delete mode 100644 docs/Queries/sqlplusplus-and-query-builder-differences.md diff --git a/docs/Queries/live-queries.md b/docs/Queries/live-queries.md index c113e41..af70cdf 100644 --- a/docs/Queries/live-queries.md +++ b/docs/Queries/live-queries.md @@ -3,4 +3,51 @@ id: live-queries sidebar_position: 6 --- -# Live Queries \ No newline at end of file +# Live Queries + +> Description - _Couchbase Lite Live Query Concepts_ +> Related Content - [SQL++ for Mobile](sqlplusplus.md) + +## Activating a Live Query + +A live query is a query that, once activated, remains active and monitors the database for changes; refreshing the result set whenever a change occurs. As such, it is a great way to build reactive user interfaces — especially table/list views — that keep themselves up to date. + +**So, a simple use case may be:** A replicator running and pulling new data from a server, whilst a live-query-driven UI automatically updates to show the data without the user having to manually refresh. This helps your app feel quick and responsive. + +With Couchbase Lite for Ionic, live queries can be watched through: + + * Listener callbacks: `Query.addChangeListener` + +Each time you start watching a live query, the query is executed and an initial change notification is dispatched. The query is then kept active and further change notifications are dispatched whenever a change occurs. + +## Watching with Change Listeners + +In the case of the synchronous API, all changes are delivered to the listeners as soon as they are registered. + +With the asynchronous API, changes are only guaranteed to be delivered once the `Promise` returned from the registration call is completed: + +#### Example 1. Starting a Live Query - Change Listener + +```typescript +// Register a change listener and await the Promise returned from the registration call. +await query.addChangeListener(async (change) => { + // Await the results of the change. + const results = await change.results; + + results.forEach(result => { + // Do something with the result... + }); +}); +``` + +To stop receiving notifications, call `Query.removeChangeListener` with the token that was returned from the registration call. Regardless of the whether the API is synchronous or asynchronous, listeners will stop receiving notifications immediately: + +#### Example 2. Stopping a Live Query - Change Listener + +```typescript +final token = await query.addChangeListener((change) async { ... }); + +// Some time goes by... + +await query.removeChangeListener(token); +``` \ No newline at end of file diff --git a/docs/Queries/query-builder.md b/docs/Queries/query-builder.md deleted file mode 100644 index e934b0b..0000000 --- a/docs/Queries/query-builder.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -id: query-builder -sidebar_position: 4 ---- - -# Query Builder \ No newline at end of file diff --git a/docs/Queries/query-result-set.md b/docs/Queries/query-result-set.md index ef70d5c..fa93ea4 100644 --- a/docs/Queries/query-result-set.md +++ b/docs/Queries/query-result-set.md @@ -3,4 +3,5 @@ id: query-result-set sidebar_position: 5 --- -# Query Result Sets \ No newline at end of file +# Query Result Sets + diff --git a/docs/Queries/query-troubleshooeting.md b/docs/Queries/query-troubleshooeting.md index 4612d25..88f77bf 100644 --- a/docs/Queries/query-troubleshooeting.md +++ b/docs/Queries/query-troubleshooeting.md @@ -3,4 +3,325 @@ id: query-troubleshooting sidebar_position: 7 --- -# Query Troubleshoooting \ No newline at end of file +# Query Troubleshoooting + +> Description - _Couchbase Lite Queries - Troubleshooting_ +> Abstract - _This content describes how to use the Couchbase Lite for Ionic Query API's exlplain method to examine a query. + +## Query Explain + +### Using + +The `Query.explain()` method can provide useful insight when you are trying to diagnose query performance issues and-or optimize queries. To examine how your query is working, either embed the call inside your app (see: Example 1), or use it interactively within a cblite shell (see: Example 2). + +#### Example 1. Using Query Explain in App + +```typescript +// Create the query +const query = database.createQuery('SELECT META().id AS thisId FROM inventory.hotel WHERE city="Medway"'); + +// Print the explanation of the query +const explanation = await query.explain(); +console.log(explanation); +``` + +1. Construct your query as normal. +2. Call the query's `explain` method and print it. + +#### Example 2. Using Query Explain in cblite + +```bash +cblite .cblite2 +(cblite) select --explain domains GROUP BY country ORDER BY country, name +(cblite) query --explain {"GROUP_BY":[[".country"]],"ORDER_BY":[[".country"],[".name"]],"WHAT":[[".domains"]]} +``` + +1. Within a terminal session open your database with cblite and enter your query. +2. Here the query is entered as an SQL++ query using select. +3. Here the query is entered as a JSON-string using query. + +### Output + +The output from explain() remains the same whether invoked by an app, or cblite — see [Example 3](#example-3-queryexplain-output) for an example of how it looks. + +#### Example 3. Query.explain() Output + +``` +SELECT fl_result(fl_value(_doc.body, 'domains')) FROM kv_default AS _doc WHERE (_doc.flags & 1 = 0) GROUP BY fl_value(_doc.body, 'country') ORDER BY fl_value(_doc.body, 'country'), fl_value(_doc.body, 'name') + +7|0|0| SCAN TABLE kv_default AS _doc +12|0|0| USE TEMP B-TREE FOR GROUP BY +52|0|0| USE TEMP B-TREE FOR ORDER BY + +{"GROUP_BY":[[".country"]],"ORDER_BY":[[".country"],[".name"]],"WHAT":[[".domains"]]} +``` + +This output ([Example 3](#example-3-queryexplain-output)) comprises three main elements: + +1. The translated SQL-query, which is not necessarily useful, being aimed more at Couchbase support and-or engineering teams. +2. The [SQLite query plan](https://www.sqlite.org/eqp.html), which gives a high-level view of how the SQL query will be implemented. You can use this to identify potential issues and so optimize problematic queries. +3. The query in JSON-string format, which you can copy-and-paste directly into the *cblite* tool. + +## The Query Plan + +### Format + +The query plan section of the output displays a tabular form of the translated query's execution plan. It primarily shows how the data will be retrieved and, where appropriate, how it will be sorted for navigation and-or presentation purposes. For more on SQLite's Explain Query Plan — see: https://www.sqlite.org/eqp.html. + +#### Example 4. A Query Plan + +``` +7|0|0| SCAN TABLE kv_default AS _doc +12|0|0| USE TEMP B-TREE FOR GROUP BY +52|0|0| USE TEMP B-TREE FOR ORDER BY +``` + +1. **Retrieval method** — This line shows the retrieval method being used for + the query; here a sequential read of the database. Something you may well be + looking to optimize — see [Retrieval Method](#retrieval-method) for more. +2. **Grouping method** --- This line shows that the `GROUP BY` clause used in + the query requires the data to be sorted and that a b-tree will be used for + temporary storage — see [Order and Group](#order-and-group). +3. **Ordering method** — This line shows that the `ORDER BY` clause used in the + query requires the data to be sorted and that a b-tree will be used for + temporary storage — see [Order and Group](#order-and-group). + +### Retrieval Method {#retrieval-method} + +The query optimizer will attempt to retrieve the requested data items as +efficiently as possible, which generally will be by using one or more of the +available indexes. The retrieval method shows the approach decided upon by the +optimizer — see [Table 1](#). + +#### Table 1. Retrieval Methods + +| Retrieval Method | Description | +| ---------------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Search | Here the query is able to access the required data directly using keys into the index. Queries using the Search mode are the fastest. | +| Scan Index | Here the query is able to retrieve the data by scanning all or part-of the index (for example when seeking to match values within a range). This type of query is slower than search, but at least benefits from the compact and ordered form of the index. | +| Scan Table | Here the query must scan the database table(s) to retrieve the required data. It is the slowest of these methods and will benefit most from some form of optimization. | + +When looking to optimize a query's retrieval method, consider whether: + +- Providing an additional index makes sense. +- You could use an existing index — perhaps by restructuring the query to + minimize wildcard use, or the reliance on functions that modify the query's + interpretation of index keys (for example, 'lower'). +- You could reduce the data set being requested to minimize the query's + footprint on the database. + +### Order and Group + +The `USE TEMP B-TREE FOR` lines in the example indicate that the query requires +sorting to cater for grouping and then sorting again to present the output +results. Minimizing, if not eliminating, this ordering and re-ordering will +obviously reduce the amount of time taken to process your query. + +Ask "is the grouping and-or ordering absolutely necessary?": if it isn't, drop +it or modify it to minimize its impact. + +## Queries and Indexes + +Before we begin querying documents, let's briefly mention the importance of +having an appropriate and balanced approach to indexes. + +Creating indexes can speed up the performance of queries. A query will typically +return results more quickly if it can take advantage of an existing database +index to search, narrowing down the set of documents to be examined. + +:::note + +Couchbase Lite for Ionic does not currently support partial value indexes; +indexes with non-property expressions. You should only index with properties +that you plan to use in the query. + +::: + +The Query optimizer converts your query into a parse tree that groups zero or +more _and-connected_ clauses together (as dictated by your where conditionals) +for effective query engine processing. + +Ideally a query will be be able to satisfy its requirements entirely by either +directly accessing the index or searching sequential index rows. Less good is if +the query must scan the whole index; although the compact nature of most indexes +means this is still much faster than the alternative of scanning the entire +database with no help from the indexes at all. + +Searches that begin with or rely upon an inequality with the primary key are +inherently less effective than those using a primary key equality. + +## Working with the Query Optimizer + +You may have noticed that sometimes a query runs faster on a second run, or +after re-opening the database, or after deleting and recreating an index. This +typically happens when SQL Query Optimizer has gathered sufficient stats to +recognize a means of optimizing a sub-optimal query. + +If only those stats were available from the start. In fact they are gathered +after certain events, such as: + +- Following index creation +- On a database close +- When running a database compact. + +So, if your analysis of the [Query Explain output](#example-3) indicates a +sub-optimal query and your rewrites fail to sufficiently optimize it, consider +compacting the database. Then re-generate the Query Explain and note any +improvements in optimization. They may not, in themselves, resolve the issue +entirely; but they can provide a uesful guide toward further optimizing changes +you could make. + +## Wildcard and Like-based Queries + +Like-based searches can use the index(es) only if: + +- The search-string doesn't start with a wildcard. +- The primary search expression uses a property that is an indexed key. +- The search-string is a constant known at run time (that is, not a value + derived during processing of the query). + +To illustrate this we can use a modified query; replacing a simple equality test +with a `LIKE`. + +In [Example 5](#) we use a wildcard prefix and suffix. You can see that the +query plan decides on a retrieval method of `SCAN TABLE`. + +:::tip + +For more on indexes — see: [Indexing](../indexes.md). + +::: + +#### Example 5. Like with Wildcard Prefix + +```typescript +const queryString = ` + SELECT * + FROM hotels AS item + WHERE type LIKE '%hotel%' +`; + +const query = database.createQuery(queryString); +console.log(await query.explain()); +``` + +1. The indexed property, `type`, cannot use its index because of the wildcard + prefix. + +#### Example 6. Resulting Query Plan + +```console +2|0|0| SCAN TABLE kv_default AS _doc +``` + +By contrast, by removing the wildcard prefix `%` (in [Example 7](#example-7-like-with-no-wildcard-prefix)), we see +that the query plan's retrieval method changes to become an index search. Where +practical, simple changes like this can make significant differences in query +performance. + +#### Example 7. Like with No Wildcard-prefix + +```typescript +const queryString = ` + SELECT * + FROM hotels AS item + WHERE type LIKE 'hotel%' AND name LIKE '%royal%' +`; + +const query = database.createQuery(queryString); +console.log(await query.explain()); +``` + +1. Simply removing the wildcard prefix enables the query optimizer to access the + `typeIndex`, which results in a more efficient search. + +#### Example 8. Resulting Query Plan + +```console +3|0|0| SEARCH TABLE kv_default AS _doc USING INDEX typeIndex (>? AND =?) +``` + +Knowing this, you can consider how you create the index; for example, using +`LOWER` when you create the index and then always using +lowercase comparisons. + +## Optimization Considerations + +Try to minimize the amount of data retrieved. Reduce it down to the few +properties you really do need to achieve the required result. + +Consider fetching details lazily. You could break complex queries into +components. Returning just the document IDs, then process the array of document +IDs using either the Document API or a query thats uses the array of document +IDs to return information. + +Consider using paging to minimize the data returned when the number of results +returned is expected to be high. Getting the whole lot at once will be slow and +resource intensive: Plus does anyone want to access them all in one go? Instead +retrieve batches of information at a time, perhaps using `LIMIT` and `OFFSET` +clauese to set a starting point for each subsequent batch. + +Although, note that using query offsets becomes increasingly less effective as +the overhead of skipping a growing number of rows each time increases. You can +work around this, by instead using ranges of search-key values. If the last +search-key value of batch one was 'x' then that could become the starting point +for your next batch and-so-on. + +Optimize document size in design. Smaller documents load more quickly. Break +your data into logical linked units. + +Consider Using Full Text Search instead of complex `LIKE` or `REGEX` patterns — +see [Full Text Search](../full-text-search.md). \ No newline at end of file diff --git a/docs/Queries/sqlplusplus-and-query-builder-differences.md b/docs/Queries/sqlplusplus-and-query-builder-differences.md deleted file mode 100644 index 459b027..0000000 --- a/docs/Queries/sqlplusplus-and-query-builder-differences.md +++ /dev/null @@ -1,6 +0,0 @@ ---- -id: sqlplusplus-and-query-builder-differences -sidebar_position: 3 ---- - -# SQL++ and Query Builder Differences \ No newline at end of file diff --git a/docs/Queries/sqlplusplus-mobile-and-server-differences.md b/docs/Queries/sqlplusplus-mobile-and-server-differences.md index 7e33e80..b708a9b 100644 --- a/docs/Queries/sqlplusplus-mobile-and-server-differences.md +++ b/docs/Queries/sqlplusplus-mobile-and-server-differences.md @@ -3,4 +3,122 @@ id: sqlplusplus-mobile-and-server-differences sidebar_position: 2 --- -# SQL++ for Mobile and Server Differences \ No newline at end of file +# SQL++ for Mobile and Server Differences + +:::important + +N1QL is Couchbase's implementation of the developing SQL++ standard. As such the +terms N1QL and SQL++ are used interchangeably in all Couchbase documentation +unless explicitly stated otherwise. + +::: + +There are several minor but notable behavior differences between SQL++ for +Mobile queries and SQL++ for Server, as shown in [Table 1](#table-1-sql-query-comparison). + +In some instances, if required, you can force SQL++ for Mobile to work in the +same way as SQL++ for Server. These instances are noted in the content below. + +#### Table 1. SQL++ Query Comparison + +| Feature | SQL++ for Server | SQL++ for Mobile | +| ------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `USE KEYS` | `SELECT fname, email FROM tutorial USE KEYS ('dave', 'ian');` | `SELECT fname, email FROM tutorial WHERE META().id IN ('dave', 'ian');` | +| `ON KEYS` | `SELECT * FROM user u JOIN orders o ON KEYS ARRAY s.order_id FOR s IN u.order_history END;` | `SELECT * FROM user u, u.order_history s JOIN orders o ON s.order_id = Meta(o).id;` | +| `USE KEY` | `SELECT * FROM user u JOIN orders o ON KEY o.user_id FOR u;` | `SELECT * FROM user u JOIN orders o ON META(u).id = o.user_id;` | +| `NEST` | `SELECT * FROM user u NEST orders orders ON KEYS ARRAY s.order_id FOR s IN u.order_history END;` | `NEST`/`UNNEST` not supported | +| `LEFT OUTER NEST` | `SELECT * FROM user u LEFT OUTER NEST orders orders ON KEYS ARRAY s.order_id FOR s IN u.order_history END;` | `NEST`/`UNNEST` not supported | +| `ARRAY` | `ARRAY i FOR i IN [1, 2] END` | `(SELECT VALUE i FROM [1, 2] AS i)` | +| `ARRAY FIRST` | `ARRAY FIRST arr` | `arr[0]` | +| `LIMIT l OFFSET o` | Allows `OFFSET` without `LIMIT` | Allows `OFFSET` without `LIMIT` | +| `UNION`, `INTERSECT`, `EXCEPT` | All three are supported (with `ALL` and `DISTINCT` variants). | Not supported | +| `OUTER JOIN` | Both `LEFT` and `RIGHT OUTER JOIN` are supported. | Only `LEFT OUTER JOIN` supported (and necessary for query expressability). | +| `<`, `<=`, `=`, etc. operators | Can compare either complex values or scalar values. | Only scalar values may be compared. | +| `ORDER BY` | Result sequencing is based on specific rules described in [SQL++ for Server `ORDER BY` clause](https://docs.couchbase.com/server/current/n1ql/n1ql-language-reference/orderby.html). | Result sequencing is based on the SQLite ordering described in [SQLite select overview](https://sqlite.org/lang_select.html).

The ordering of Dictionary and Array objects is based on binary ordering. | +| `SELECT DISTINGCT` | Supported | `SELECT DISTINCT VALUE` is supported when the returned values are scalars. | +| `CREATE INDEX` | Supported | Not Supported | +| `INSERT`, `UPSERT`, `DELETE` | Supported | Not Supported | + + +## Boolean Logic Rules + +### SQL++ for Server + +Couchbase Server operates in the same way as Couchbase Lite, except: + +- `MISSING`, `NULL` and `FALSE` are `FALSE` +- Numbers `0` is `FALSE` +- Empty strings, arrays, and objects are `FALSE` +- All other values are `TRUE` + +You can choose to use Couchbase Server's SQL++ rules by using the +`TOBOOLEAN(expr)` function to convert a value to its boolean value. + +### SQL++ for Mobile + +SQL++ for Mobile's boolean logic rules are based on SQLite's, so: + +- `TRUE` is `TRUE`, and `FALSE` is `FALSE` +- Numbers `0` or `0.0` are `FALSE` +- Arrays and dictionaries are `FALSE` +- String and Blob are `TRUE` if the values are casted as a non-zero or `FALSE` + if the values are casted as `0` or `0.0` — see: + [SQLITE's CAST and Boolean expressions](https://sqlite.org/lang_expr.html) for + more details. +- `NULL` is `FALSE` +- `MISSING` is `MISSING` + +### Logical Operations + +In SQL++ for Mobile logical operations will return one of three possible values; +`TRUE`, `FALSE`, or `MISSING`. + +Logical operations with the `MISSING` value could result in `TRUE` or `FALSE` if +the result can be determined regardless of the missing value, otherwise the +result will be `MISSING`. + +In SQL++ for Mobile — unlike SQL++ for Server — `NULL` is implicitly converted +to `FALSE` before evaluating logical operations. [Table 2](#table-2-logical-operations-comparison) summarizes the +result of logical operations with different operand values and also shows where +the Couchbase Server behavior differs. + +#### Table 2. Logical Operations Comparison + +| Operand
a | Operand
b | SQL ++ for Mobile
a AND b | SQL ++ for Mobile
a OR b | SQL ++ for Server
a AND b | SQL ++ for Server
a OR b | +| ------------- | ------------- | ------------------------------ | ----------------------------- | ------------------------------ | ----------------------------- | +| `TRUE` | `TRUE` | `TRUE` | `TRUE` | - | - | +| | `FALSE` | `FALSE` | `TRUE` | - | - | +| | `NULL` | `FALSE` | `TRUE` | `NULL` | - | +| | `MISSING` | `MISSING` | `TRUE` | - | - | +| `FALSE` | `TRUE` | `FALSE` | `TRUE` | - | - | +| | `FALSE` | `FALSE` | `FALSE` | - | - | +| | `NULL` | `FALSE` | `FALSE` | - | `NULL` | +| | `MISSING` | `FALSE` | `MISSING` | - | - | +| `NULL` | `TRUE` | `FALSE` | `TRUE` | `NULL` | - | +| | `FALSE` | `FALSE` | `FALSE` | - | `NULL` | +| | `NULL` | `FALSE` | `FALSE` | `NULL` | `NULL` | +| | `MISSING` | `FALSE` | `MISSING` | `MISSING` | `NULL` | +| `MISSING` | `TRUE` | `MISSING` | `TRUE` | - | - | +| | `FALSE` | `FALSE` | `MISSING` | - | - | +| | `NULL` | `FALSE` | `MISSING` | `MISSING` | `NULL` | +| | `MISSING` | `MISSING` | `MISSING` | - | - | + + +## CRUD Operations + +- SQL++ for Mobile only supports Read or Query operations. +- SQL++ for Server fully supports CRUD operation. + +## Functions + +### Division Operator + +| SQL ++ for Server | SQL++ for Mobile | +| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| SQL++ for Server always performs float division regardless of the types of the operands.

You can force this behavior in SQL++ for Mobile by using the `DIV(x, y)` function. | The operand types determine the division operation performed.

If both are integers, integer division is used.

If one is a floating number, then float division is used. | + +### Round Function + +| SQL ++ for Server | SQL++ for Mobile | +| ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| SQL++ for Server `ROUND()` uses the Rounding to Nearest Even convention (for example, `ROUND(1.85)` returns `1.8`).

You can force this behavior in Couchbase Lite by using the `ROUND_EVEN()` function. | The `ROUND()` function returns a value to the given number of integer digits to the right of the decimal point (left if digits is negative).

Digits are `0` if not given.

Midpoint values are handled using the Rounding Away From Zero convention, which rounds them to the next number away from zero (for example, `ROUND(1.85)` returns `1.9`). | \ No newline at end of file diff --git a/docs/documents.md b/docs/documents.md index bb54b9f..7151371 100644 --- a/docs/documents.md +++ b/docs/documents.md @@ -354,19 +354,4 @@ Couchbase Lite APIs do not explicitly disallow the use of attributes with the un * `_id` * `_removed` * `_rev` -* `_sequence` - - - - - - - - - - - - - - - +* `_sequence` \ No newline at end of file diff --git a/docs/full-text-search.md b/docs/full-text-search.md index aacf93a..527239d 100644 --- a/docs/full-text-search.md +++ b/docs/full-text-search.md @@ -3,4 +3,159 @@ id: full-text-search sidebar_position: 10 --- -# Using Full Text Search \ No newline at end of file +# Using Full Text Search + +> Description - _Couchbase Lite database data querying concepts — full text search_ +> Related Content - [Indexing](indexes.md) + +## Overview + +To run a full-text search (FTS) query, you must create a full-text index on the expression being matched. Unlike regular queries, the index is not optional. + +The following examples use the data model introduced in Indexing. They create and use an FTS index built from the hotel’s `Overview` text. + +## SQL++ + +### Create Index + +SQL++ provides a configuration object to define Full Text Search indexes using `FullTextIndexItem` and `IndexBuilder`. + +#### Using SQL++'s FullTextIndexItem and IndexBuilder + +```typescript +const indexProperty = FullTextIndexItem.property('overview'); +const index = IndexBuilder.fullTextIndex(indexProperty).setIgnoreAccents(false); +await collection.createIndex('overviewFTSIndex', index); +``` + +### Use Index + +FullTextSearch is enabled using the SQL++ match() function. + +With the index created, you can construct and run a Full-text search (FTS) query using the indexed properties. + +The index will omit a set of common words, to avoid words like "I", "the", "an" from overly influencing your queries. See [full list of these stopwords](https://github.com/couchbasedeps/sqlite3-unicodesn/blob/HEAD/stopwords_en.h). + +The following example finds all hotels mentioning *Michigan* in their *Overview* text. + +#### Example 2. Using SQL++ Full Text Search + +```typescript +const ftsQueryString = ` + SELECT _id, overview + FROM _default + WHERE MATCH(overviewFTSIndex, 'michigan') + ORDER BY RANK(overviewFTSIndex) +`; +const ftsQuery = database.createQuery(ftsQueryString); + +const results = await ftsQuery.execute(); +results.forEach(result => { + console.log(result.getString('_id') + ": " + result.getString('overview')); +}); +``` + +## Operation + +In the examples above, the pattern to match is a word, the full-text search query matches all documents that contain the word "michigan" in the value of the `doc.overview` property. + +Search is supported for all languages that use whitespace to separate words. + +Stemming, which is the process of fuzzy matching parts of speech, like "fast" and "faster", is supported in the following languages: Danish, Dutch, English, Finnish, French, German, Hungarian, Italian, Norwegian, Portuguese, Romanian, Russian, Spanish, Swedish and Turkish. + +## Pattern Matching Formats + +As well as providing specific words or strings to match against, you can provide the pattern to match in these formats. + +### Prefix Queries + +The query expression used to search for a term prefix is the prefix itself with a "*" character appended to it. + +#### Example 5. Prefix query + +Query for all documents containing a term with the prefix "lin". + +``` +"lin*" +``` + +This will match + + * All documents that contain "linux" + * And …​ those that contain terms "linear","linker", "linguistic" and so on. + +### Overriding the Property Name + +Normally, a token or token prefix query is matched against the document property specified as the left-hand side of the `match` operator. This may be overridden by specifying a property name followed by a ":" character before a basic term query. There may be space between the ":" and the term to query for, but not between the property name and the ":" character. + +#### Example 6. Override indexed property name + +Query the database for documents for which the term "linux" appears in the document title, and the term "problems" appears in either the title or body of the document. + +``` +'title:linux problems' +``` + +### Phrase Queries + +A `phrase query`is one that retrieves all documents containing a nominated set of terms or term prefixes in a specified order with no intervening tokens. + +Phrase queries are specified by enclosing a space separated sequence of terms or term prefixes in double quotes ("). + +#### Example 7. Phrase query + +Query for all documents that contain the phrase "linux applications". + +``` +"linux applications" +``` + +### NEAR Queries + +Search for a document that contains the phrase "replication" and the term "database" with not more than 2 terms separating the two. + +``` +"database NEAR/2 replication" +``` + +### AND, OR & NOT Query Operators:: + +The enhanced query syntax supports the AND, OR and NOT binary set operators. Each of the two operands to an operator may be a basic FTS query, or the result of another AND, OR or NOT set operation. Operators must be entered using capital letters. Otherwise, they are interpreted as basic term queries instead of set operators. + +#### Example 9. Using And, Or and Not + +Return the set of documents that contain the term "couchbase", and the term "database". + +``` +"couchbase AND database" +``` + +### Operator Precedence + +When using the enhanced query syntax, parenthesis may be used to specify the precedence of the various operators. + +#### Example 10. Operator precedence + +Query for the set of documents that contains the term "linux", and at least one of the phrases "couchbase database" and "sqlite library". + +``` +'("couchbase database" OR "sqlite library") AND "linux"' +``` + +## Ordering Results + +It’s very common to sort full-text results in descending order of relevance. This can be a very difficult heuristic to define, but Couchbase Lite comes with a ranking function you can use. + +In the `OrderBy` array, use a string of the form `Rank(X)`, where `X` is the property or expression being searched, to represent the ranking of the result. + + + + + + + + + + + + diff --git a/docs/scopes-collections.md b/docs/scopes-collections.md index 7ad8f70..2707a2a 100644 --- a/docs/scopes-collections.md +++ b/docs/scopes-collections.md @@ -96,11 +96,4 @@ const scopes = await database.scopes(); // Get Collections of a particular Scope const collections = await database.collections(scopeName); -``` - - - - - - - +``` \ No newline at end of file From 0bae12859c356bf0518be2e12d0e39a705c0f7ca Mon Sep 17 00:00:00 2001 From: Dhiraj Kumar Azad Date: Tue, 28 May 2024 20:14:19 +0530 Subject: [PATCH 4/5] added result set doc --- docs/Queries/query-result-set.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/Queries/query-result-set.md b/docs/Queries/query-result-set.md index fa93ea4..7c49e9e 100644 --- a/docs/Queries/query-result-set.md +++ b/docs/Queries/query-result-set.md @@ -5,3 +5,17 @@ sidebar_position: 5 # Query Result Sets +When querying a database, the results are returned as an array of objects (`ResultSet`). Each object (`Result`) has keys based on the collection names used in the `FROM` statement of your query. If an alias is used, the key will be the alias name. This allows you to access the properties of the results easily and ensures that the structure of your results matches the query structure. + +#### Example 1. Query Result Sets + +```typescript +const query = database.createQuery('SELECT * FROM inventory.hotel AS hotelItems WHERE city="Medway"'); +const resultSet: ResultSet = await query.execute(); + +for (const result of resultSet) { + console.log(result['hotelItems'].propertyName); +} +``` + +In this example, `hotelItems` is the alias used for the collection in the query, and it serves as the key in the `Result` objects within the `ResultSet`. From 3ecf937e74b98823a98a86b17d331a34d4da6d91 Mon Sep 17 00:00:00 2001 From: Dhiraj Kumar Azad Date: Wed, 29 May 2024 13:20:02 +0530 Subject: [PATCH 5/5] worked on comments --- docs/Queries/live-queries.md | 30 ++++++++++++------------------ docs/Queries/query-result-set.md | 2 ++ docs/Queries/sqlplusplus.md | 2 -- docs/blobs.md | 2 +- docs/databases.md | 4 ++-- docs/full-text-search.md | 5 +++-- docs/scopes-collections.md | 3 +++ 7 files changed, 23 insertions(+), 25 deletions(-) diff --git a/docs/Queries/live-queries.md b/docs/Queries/live-queries.md index af70cdf..5b4c76e 100644 --- a/docs/Queries/live-queries.md +++ b/docs/Queries/live-queries.md @@ -20,24 +20,21 @@ With Couchbase Lite for Ionic, live queries can be watched through: Each time you start watching a live query, the query is executed and an initial change notification is dispatched. The query is then kept active and further change notifications are dispatched whenever a change occurs. -## Watching with Change Listeners - -In the case of the synchronous API, all changes are delivered to the listeners as soon as they are registered. - -With the asynchronous API, changes are only guaranteed to be delivered once the `Promise` returned from the registration call is completed: - #### Example 1. Starting a Live Query - Change Listener ```typescript // Register a change listener and await the Promise returned from the registration call. -await query.addChangeListener(async (change) => { - // Await the results of the change. - const results = await change.results; - - results.forEach(result => { - // Do something with the result... - }); -}); +const token = await query.addChangeListener((change) => { + if (change.error !== null && change.error !== undefined) { + // deal with error... + } else { + const results = change.results; + //loop through ResultSet + for (const doc of results) { + //do something with doc + } + } +}); ``` To stop receiving notifications, call `Query.removeChangeListener` with the token that was returned from the registration call. Regardless of the whether the API is synchronous or asynchronous, listeners will stop receiving notifications immediately: @@ -45,9 +42,6 @@ To stop receiving notifications, call `Query.removeChangeListener` with the toke #### Example 2. Stopping a Live Query - Change Listener ```typescript -final token = await query.addChangeListener((change) async { ... }); - -// Some time goes by... - +const token = await query.addChangeListener((change) => { ... }); await query.removeChangeListener(token); ``` \ No newline at end of file diff --git a/docs/Queries/query-result-set.md b/docs/Queries/query-result-set.md index 7c49e9e..b328650 100644 --- a/docs/Queries/query-result-set.md +++ b/docs/Queries/query-result-set.md @@ -19,3 +19,5 @@ for (const result of resultSet) { ``` In this example, `hotelItems` is the alias used for the collection in the query, and it serves as the key in the `Result` objects within the `ResultSet`. + + diff --git a/docs/Queries/sqlplusplus.md b/docs/Queries/sqlplusplus.md index eb72e96..d8a70ff 100644 --- a/docs/Queries/sqlplusplus.md +++ b/docs/Queries/sqlplusplus.md @@ -27,8 +27,6 @@ const query = database.createQuery('SELECT META().id AS thisId FROM inventory.ho const resultSet = await query.execute(); ``` -Here we are accessing the default collection using the shorthand notation (`_`) — see the FROM clause for more on data source selection and Query Parameters for more on parameterized queries. - ## Query Format The API uses query statements of the form shown in [Example 2](#example-2-query-format). diff --git a/docs/blobs.md b/docs/blobs.md index 994e282..90f97fd 100644 --- a/docs/blobs.md +++ b/docs/blobs.md @@ -50,7 +50,7 @@ const imageData = await fetchAvatarImageData(); const avatarBlob = new Blob('image/jpeg', imageData); // Retrieve an existing document -let document = await collection.document(documentId); +const document = await collection.document(documentId); // Assign the Blob to the document under the 'avatar' key document.setBlob('avatar', avatarBlob); diff --git a/docs/databases.md b/docs/databases.md index 10f7087..841f583 100644 --- a/docs/databases.md +++ b/docs/databases.md @@ -66,12 +66,12 @@ This configuration ensures seamless interaction between your Ionic app and the u ### Create or Open a Database -To create or open a database, use the Database class from the cbl-cblite package, specifying the database name and optionally, a DatabaseConfiguration for custom settings like the database directory or encryption. +To create or open a database, use the Database class from the cblite package, specifying the database name and optionally, a DatabaseConfiguration for custom settings like the database directory or encryption. **Example 1. Creating/Opening a Database** ```javascript -import { Database, DatabaseConfiguration } from 'cbl-cblite'; //import the package +import { Database, DatabaseConfiguration } from 'cblite'; //import the package ``` ```javascript diff --git a/docs/full-text-search.md b/docs/full-text-search.md index 527239d..d10e9ba 100644 --- a/docs/full-text-search.md +++ b/docs/full-text-search.md @@ -50,9 +50,10 @@ const ftsQueryString = ` const ftsQuery = database.createQuery(ftsQueryString); const results = await ftsQuery.execute(); -results.forEach(result => { + +for(const result of results) { console.log(result.getString('_id') + ": " + result.getString('overview')); -}); +} ``` ## Operation diff --git a/docs/scopes-collections.md b/docs/scopes-collections.md index 2707a2a..604a125 100644 --- a/docs/scopes-collections.md +++ b/docs/scopes-collections.md @@ -50,8 +50,11 @@ Scope and collection names are case sensitive. **Example 1. Create a scope and collection** ```typescript +const scopeName = "myScopeName"; +const collectionName = "myCollectionName"; const collection = await database.createCollection(collectionName, scopeName); ``` + In the example above, you can see that `Database.createCollection` can take two parameters. The second is the scope assigned to the created collection, if this parameter is omitted then a collection of the given name will be assigned to the `_default` scope. The first parameter is the name of the collection you want to create, in this case `myCollectionName`.