blob: eb1c728888ef82066cf3a5a5662864fd0aac76a2 [file] [log] [blame] [view]
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
## Relations
A relation is a clause that appears after the WHERE keyword, and restricts the rows that the
statement operates on.
Relations are used by the following statements:
* [SELECT](../select/)
* [UPDATE](../update/)
* [DELETE](../delete/)
* [CREATE MATERIALIZED VIEW](../schema/materialized_view/)
The easiest way to add a relation is with a `whereXxx` method in the fluent API:
```java
selectFrom("sensor_data").all()
.whereColumn("id").isEqualTo(bindMarker())
.whereColumn("date").isGreaterThan(bindMarker());
// SELECT * FROM sensor_data WHERE id=? AND date>?
```
You can also create it manually with one of the factory methods in [Relation], and then pass it to
`where()`:
```java
selectFrom("user").all().where(
Relation.column("id").isEqualTo(bindMarker()));
// SELECT * FROM user WHERE id=?
```
If you call `where()` multiple times, the clauses will be joined with the AND keyword. You can also
add multiple relations in a single call. This is a bit more efficient since it creates less
temporary objects:
```java
selectFrom("sensor_data").all()
.where(
Relation.column("id").isEqualTo(bindMarker()),
Relation.column("date").isGreaterThan(bindMarker()));
// SELECT * FROM sensor_data WHERE id=? AND date>?
```
Relations are generally composed of a left operand, an operator, and an optional right-hand-side
[term](../term/). The type of relation determines which operators are available.
### Simple columns
`whereColumn` operates on a single column. It supports basic arithmetic comparison operators:
| Comparison operator | Method name |
|---------------------|--------------------------|
| `=` | `isEqualTo` |
| `<` | `isLessThan` |
| `<=` | `isLessThanOrEqualTo` |
| `>` | `isGreaterThan` |
| `>=` | `isGreaterThanOrEqualTo` |
| `!=` | `isNotEqualTo` |
*Note: we support `!=` because it is present in the CQL grammar but, as of Cassandra 4, it is not
implemented yet.*
See above for comparison operator examples.
If you're using SASI indices, you can also use `like()` for wildcard comparisons:
```java
selectFrom("user").all().whereColumn("last_name").like(literal("M%"));
// SELECT * FROM user WHERE last_name LIKE 'M%'
```
`in()` is like `isEqualTo()`, but with various alternatives. You can either provide each alternative as a
term:
```java
selectFrom("user").all().whereColumn("id").in(literal(1), literal(2), literal(3));
// SELECT * FROM user WHERE id IN (1,2,3)
selectFrom("user").all().whereColumn("id").in(bindMarker(), bindMarker(), bindMarker());
// SELECT * FROM user WHERE id IN (?,?,?)
```
Or bind the whole list of alternatives as a single variable:
```java
selectFrom("user").all().whereColumn("id").in(bindMarker());
// SELECT * FROM user WHERE id IN ?
```
For collection columns, you can check for the presence of an element with `contains()` and
`containsKey()`:
```java
selectFrom("sensor_data")
.all()
.whereColumn("id").isEqualTo(bindMarker())
.whereColumn("date").isEqualTo(bindMarker())
.whereColumn("readings").containsKey(literal("temperature"))
.allowFiltering();
// SELECT * FROM sensor_data WHERE id=? AND date=? AND readings CONTAINS KEY 'temperature' ALLOW FILTERING
```
Finally, `isNotNull()` generates an `IS NOT NULL` check. *Note: we support `IS NOT NULL` because it
is present in the CQL grammar but, as of Cassandra 4, it is not implemented yet.*
### Column components
`whereMapValue` operates on an value inside of a map:
```java
selectFrom("sensor_data")
.all()
.whereColumn("id").isEqualTo(bindMarker())
.whereColumn("date").isEqualTo(bindMarker())
.whereMapValue("readings", literal("temperature")).isGreaterThan(literal(65))
.allowFiltering();
// SELECT * FROM sensor_data WHERE id=? AND date=? AND readings['temperature']>65 ALLOW FILTERING
```
Column components support the six basic arithmetic comparison operators.
### Tokens
`whereToken` hashes one or more columns into a token. It is generally used to perform range queries:
```java
selectFrom("user")
.all()
.whereToken("id").isGreaterThan(bindMarker())
.whereToken("id").isLessThanOrEqualTo(bindMarker());
// SELECT * FROM user WHERE token(id)>? AND token(id)<=?
```
It supports the six basic arithmetic comparison operators.
### Multi-column relations
`whereColumns` compares a set of columns to tuple terms of the same arity. It supports the six basic
arithmetic comparison operators (using lexicographical order):
```java
selectFrom("sensor_data")
.all()
.whereColumn("id").isEqualTo(bindMarker())
.whereColumns("date", "hour").isGreaterThan(tuple(bindMarker(), bindMarker()));
// SELECT * FROM sensor_data WHERE id=? AND (date,hour)>(?,?)
```
In addition, tuples support the `in()` operator. Like with regular columns, bind markers can operate
at different levels:
```java
// Bind the whole list of alternatives (two-element tuples) as a single value:
selectFrom("test")
.all()
.whereColumn("k").isEqualTo(literal(1))
.whereColumns("c1", "c2").in(bindMarker());
// SELECT * FROM test WHERE k=1 AND (c1,c2) IN ?
// Bind each alternative as a value:
selectFrom("test")
.all()
.whereColumn("k").isEqualTo(literal(1))
.whereColumns("c1", "c2").in(bindMarker(), bindMarker(), bindMarker());
// SELECT * FROM test WHERE k=1 AND (c1,c2) IN (?,?,?)
// Bind each element in the alternatives as a value:
selectFrom("test")
.all()
.whereColumn("k").isEqualTo(literal(1))
.whereColumns("c1", "c2").in(
tuple(bindMarker(), bindMarker()),
tuple(bindMarker(), bindMarker()),
tuple(bindMarker(), bindMarker()));
// SELECT * FROM test WHERE k=1 AND (c1,c2) IN ((?,?),(?,?),(?,?))
```
### Custom index expressions
`whereCustomIndex` evaluates a custom index. The argument is a free-form term (what is a legal value
depends on your index implementation):
```java
selectFrom("foo")
.all()
.whereColumn("k").isEqualTo(literal(1))
.whereCustomIndex("my_custom_index", literal("a text expression"));
// SELECT * FROM foo WHERE k=1 AND expr(my_custom_index,'a text expression')
```
### Raw snippets
Finally, it is possible to provide a raw CQL snippet with `whereRaw()`; it will get appended to the
query as-is, without any syntax checking or escaping:
```java
selectFrom("foo").all().whereRaw("k = 1 /*some custom comment*/ AND c<2");
// SELECT * FROM foo WHERE k = 1 /*some custom comment*/ AND c<2
```
This should be used with caution, as it's possible to generate invalid CQL that will fail at
execution time; on the other hand, it can be used as a workaround to handle new CQL features that
are not yet covered by the query builder.
[QueryBuilder]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/querybuilder/QueryBuilder.html
[Relation]: https://docs.datastax.com/en/drivers/java/4.17/com/datastax/oss/driver/api/querybuilder/relation/Relation.html