概览
- 因AI要使用到向量存储,JanusGraph也使用到Cassandra
卸载先前版本
docker stop cassandra && docker remove cassandra && rm -rf cassandra/
运行Cassandra容器
docker run \--name cassandra \--hostname cassandra \-p 9042:9042 \--privileged=true \--net network-common \-v /elf/cassandra:/var/lib/cassandra \-itd cassandra:5.0.0
连接到Cassanra
# docker logs --tail 100 cassandra# 等完全启动后
# docker exec -it cassandra /bin/bash# 查询集群状态,用于应用程序连接等,详情参考Troubleshooting章节
# /opt/cassandra/bin/nodetool status# show version
[cqlsh 6.2.0 | Cassandra 5.0.0 | CQL spec 3.4.7 | Native protocol v5]# cqlsh 127.0.0.1 9042 -u cassandra -p cassandra# describe keyspaces;system system_distributed system_traces system_virtual_schema
system_auth system_schema system_views
# /opt/cassandra/bin/nodetool statusDatacenter: datacenter1
=======================
Status=Up/Down
|/ State=Normal/Leaving/Joining/Moving
-- Address Load Tokens Owns (effective) Host ID Rack
UN 172.18.0.2 136.3 KiB 16 100.0% 63c1c77a-fa0b-4630-9fd0-9568133bd1df rack1
Dependency
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-log4j2</artifactId>
</dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-cassandra</artifactId>
</dependency>
Product
import java.util.UUID;
import org.springframework.data.cassandra.core.mapping.Column;
import org.springframework.data.cassandra.core.mapping.PrimaryKey;
import org.springframework.data.cassandra.core.mapping.Table;@Table
public class Product {@PrimaryKey@Column(value = "product_id")private UUID productId;@Column(value = "product_name")private String productName;@Column(value = "product_memo")private String productMemo;@Column(value = "product_state")private boolean productState;public Product() {} public Product(UUID productId, String productName,String productMemo, boolean productState) {super();this.productId = productId;this.productName = productName;this.productMemo = productMemo;this.productState = productState;}@Overridepublic String toString() {return "Product [productId=" + productId+ ", productName=" + productName + ", productMemo=" + productMemo + ", productState=" + productState + "]";}public UUID getProductId() {return productId;}public void setProductId(UUID productId) {this.productId = productId;}public String getProductName() {return productName;}public void setProductName(String productName) {this.productName = productName;}public String getProductMemo() {return productMemo;}public void setProductMemo(String productMemo) {this.productMemo = productMemo;}public boolean isProductState() {return productState;}public void setProductState(boolean productState) {this.productState = productState;}}
ProductRepository
import java.util.List;
import java.util.UUID;
import io.os.cassandra.model.Product;
import org.springframework.data.cassandra.repository.AllowFiltering;
import org.springframework.data.cassandra.repository.CassandraRepository;
import org.springframework.stereotype.Repository;@Repository
public interface ProductRepository extends CassandraRepository<Product,UUID> {//@AllowFiltering:允许方法对服务器端筛选,等效于//select * from product where product_state = [true/false];@AllowFilteringList<Product> findByProductState(boolean productState);List<Product> findByProductMemo(String productName);
}
ProductController
import java.util.ArrayList;
import java.util.List;
import java.util.UUID;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import io.os.cassandra.model.Product;
import io.os.cassandra.repository.ProductRepository;@RestController
@RequestMapping("/api/product")
public class ProductController {@Autowiredprivate ProductRepository productRepository;@GetMapping("/getAllProductProduct")public ResponseEntity<List<Product>> getAllProductProduct(@RequestParam(required = false,value = "productMemo") String productMemo) {try {List<Product> productList = new ArrayList<Product>();if (productMemo == null) {productRepository.findAll().forEach(productList::add);} else {productRepository.findByProductMemo(productMemo).forEach(productList::add);}if (productList.isEmpty()) {return new ResponseEntity<>(HttpStatus.NO_CONTENT);} else {return new ResponseEntity<>(productList, HttpStatus.OK);}} catch (Exception e) {return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);}}@GetMapping("/getProductById/{id}")public ResponseEntity<Product> getProductById(@PathVariable("id") String id) {var product = productRepository.findById(UUID.fromString(id));if (product.isPresent()) {return new ResponseEntity<>(product.get(), HttpStatus.OK);} else {return new ResponseEntity<>(HttpStatus.NOT_FOUND);}}@PostMapping("/createProduct")public ResponseEntity<Product> createProduct(@RequestBody Product product) {var requestProduct = new Product(UUID.randomUUID(),product.getProductName(),product.getProductMemo(),product.isProductState());try {Product p = productRepository.save(requestProduct);return new ResponseEntity<>(p,HttpStatus.CREATED);} catch (Exception e) {return new ResponseEntity<>(null, HttpStatus.INTERNAL_SERVER_ERROR);}}@PutMapping("/updateProduct/{id}")public ResponseEntity<Product> updateProduct(@PathVariable("id") String id,@RequestBody Product product) {var updateProduct = productRepository.findById(UUID.fromString(id));if (updateProduct.isPresent()) {var p = updateProduct.get();p.setProductName(product.getProductName());p.setProductMemo(product.getProductMemo());p.setProductState(product.isProductState());return new ResponseEntity<>(productRepository.save(p), HttpStatus.OK);} else {return new ResponseEntity<>(HttpStatus.NOT_FOUND);}}@DeleteMapping("/deleteProductById/{id}")public ResponseEntity<HttpStatus> deleteProductById(@PathVariable("id") String id) {try {productRepository.deleteById(UUID.fromString(id));return new ResponseEntity<>(HttpStatus.NO_CONTENT);} catch (Exception e) {return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);}}@DeleteMapping("/deleteAllProduct")public ResponseEntity<HttpStatus> deleteAllProduct() {try {productRepository.deleteAll();return new ResponseEntity<>(HttpStatus.NO_CONTENT);} catch (Exception e) {return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);}}@GetMapping("/getProductByState")public ResponseEntity<List<Product>> findByProductState() {try {var productList = productRepository.findByProductState(true);if (productList.isEmpty()) {return new ResponseEntity<>(HttpStatus.NO_CONTENT);} else {return new ResponseEntity<>(productList, HttpStatus.OK);}} catch (Exception e) {return new ResponseEntity<>(HttpStatus.INTERNAL_SERVER_ERROR);}}}
application.yaml
- application.yaml和CassandraConfig.java,二选一
server:port: 8080servlet:context-path: /spring: data:cassandra:repositories:type: imperativecassandra:keyspace-name: elfport: 9042contact-points:- 192.168.0.123:9042schema-action: create-if-not-existspassword: cassandrausername: cassandra# /opt/cassandra/bin/nodetool status命令查询local-datacenter: datacenter1
import java.net.InetSocketAddress;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.cassandra.repository.config.EnableCassandraRepositories;
import com.datastax.oss.driver.api.core.CqlSession;@Configuration
@EnableCassandraRepositories
public class CassandraConfig {@BeanCqlSession session() {return CqlSession.builder().withKeyspace("elf").withAuthCredentials("cassandra", "cassandra").withLocalDatacenter("datacenter1").addContactPoint(InetSocketAddress.createUnresolved("192.168.0.123",9042)).build();}}
CassandraEntry
import java.lang.invoke.MethodHandles;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.SpringBootConfiguration;@SpringBootApplication
public class CassandraEntry {public static void main(String[] sa) {var cls = MethodHandles.lookup().lookupClass();SpringApplication.run(cls,sa);}}
Environment
# cassandra为Cassandra容器名
docker start cassandra# docker exec -it cassandra /bin/bash# 查询集群状态,用于应用程序连接等,详情参考Troubleshooting章节
# /opt/cassandra/bin/nodetool status# cqlsh 127.0.0.1 9042 -u cassandra -p cassandra# describe keyspaces;
Product.cql
create keyspace if not exists elf with replication={'class':'SimpleStrategy','replication_factor':1};use elf;create table if not exists elf.product(product_id uuid primary key,product_name varchar,product_memo text,product_state boolean
);select * from elf.product;
truncate table elf.product;# 不要换行,不要随意添加空格,否则cql语法可能无法通过;
# UUID:Java UUID.randomUUID()形式,Cassandra uuid()形式;insert into elf.product(product_id,product_name,product_memo,product_state)values(uuid(),'JanusGraph','Graph,Open Source',true);
insert into elf.product(product_id,product_name,product_memo,product_state)values(uuid(),'Cassandra','Wide Column,Vector,Multi Model',true);
insert into elf.product(product_id,product_name,product_memo,product_state)values(uuid(),'Presto','Relational,Open Source,Big Data',false);
insert into elf.product(product_id,product_name,product_memo,product_state)values(uuid(),'Trino','Relational,Document,Spatial,TS,KV',true);
insert into elf.product(product_id,product_name,product_memo,product_state)values(uuid(),'Weaviate','Vector,AI,Real Time',true);
insert into elf.product(product_id,product_name,product_memo,product_state)values(uuid(),'Milvus','Vector,Similarity Search,ML',true);
insert into elf.product(product_id,product_name,product_memo,product_state)values(uuid(),'Qdrant','Vector,NN,Semantic Matching',true);
insert into elf.product(product_id,product_name,product_memo,product_state)values(uuid(),'ElasticSearch','Search,Document,Spatial,Vector',true);
+--------------------------------------+-----------------------------------+---------------+--------------+
| product_id | product_memo | product_name | product_state|
|--------------------------------------+-----------------------------------+---------------+--------------|
| 29af0738-4a17-4571-90cd-700ab8995db7 | Vector,Similarity Search,ML | Milvus | True|
| 3a3415aa-8dfa-42fa-9723-0142960b687a | Relational,Open Source,Big Data | Presto | False|
| 741e5240-8a7b-40fa-b07c-e88b3638bf36 | Graph,Open Source | JanusGraph | True|
| 63608ee0-eaf1-41cf-970e-04238a556103 | Vector,AI,Real Time | Weaviate | True|
| 6d580a60-daba-46cc-b3d9-6061aa48f0ff | Search,Document,Spatial,Vector | ElasticSearch | True|
| d4760f62-9b76-4c40-8a56-8c1e7214dfdd | Wide Column,Vector,Multi Model | Cassandra | True|
| ddfe8f96-c49f-4ad0-899a-9b3b484419d8 | Vector,NN,Semantic Matching | Qdrant | True|
| cb99a803-2443-48ec-accb-5262ef9bc429 | Relational,Document,Spatial,TS,KV | Trino | True|
+--------------------------------------+-----------------------------------+---------------+--------------+
Visualization Tool
-
安装DbVisualizer V24.1.4;
-
Tool ☞ Driver Manager ☞ 搜索Cassandra
☞ 右键选择Create User Driver from Template
-
Name:Custom Cassandra
-
Url:jdbc:cassandra://192.168.0.123:9042
-
Driver Class:com.simba.cassandra.jdbc42.Driver
-
点击右侧地球仪:Download/Update driver for remote artifact,
下载完成后关闭窗口; -
点击左上角加号 ☞ Create Database Connection:
- SQL Commander ☞ New SQL Commander ☞ select * from elf.product;
Create Product
-
http://localhost:8080/api/product/createProduct
-
请求方法:POST,请求对象和响应对象分别为:
{"productName":"Cassandra","productMemo":"Wide Column,Vector,Multi Model","productState":true
}{"productId": "24672630-4ca4-424b-8fc5-c20f1400074b","productName": "Cassandra","productMemo": "Wide Column,Vector,Multi Model","productState": true
}
Update Product
-
http://localhost:8080/api/product/updateProduct/productId
http://localhost:8080/api/product/updateProduct/24672630-4ca4-424b-8fc5-c20f1400074b -
请求方法:PUT,请求对象和响应对象分别为:
{"productName":"Cassandra-Update","productMemo":"Wide Column,Vector,Multi Model","productState":true
}{"productId": "24672630-4ca4-424b-8fc5-c20f1400074b","productName": "Cassandra-Update","productMemo": "Wide Column,Vector,Multi Model","productState": true
}
Retrieve By Id
-
请求方法:GET;
-
http://localhost:8080/api/product/getProductById/productId
http://localhost:8080/api/product/getProductById/29af0738-4a17-4571-90cd-700ab8995db7
{"productId": "29af0738-4a17-4571-90cd-700ab8995db7","productName": "Milvus","productMemo": "Vector,Similarity Search,ML","productState": true
}
Retrieve All Product
-
请求方法:GET;
-
http://localhost:8080/api/product/getAllProductProduct
[{"productId": "29af0738-4a17-4571-90cd-700ab8995db7","productName": "Milvus","productMemo": "Vector,Similarity Search,ML","productState": true},{"productId": "3a3415aa-8dfa-42fa-9723-0142960b687a","productName": "Presto","productMemo": "Relational,Open Source,Big Data","productState": false},{"productId": "741e5240-8a7b-40fa-b07c-e88b3638bf36","productName": "JanusGraph","productMemo": "Graph,Open Source","productState": true},{"productId": "63608ee0-eaf1-41cf-970e-04238a556103","productName": "Weaviate","productMemo": "Vector,AI,Real Time","productState": true},{"productId": "6d580a60-daba-46cc-b3d9-6061aa48f0ff","productName": "ElasticSearch","productMemo": "Search,Document,Spatial,Vector","productState": true},{"productId": "d4760f62-9b76-4c40-8a56-8c1e7214dfdd","productName": "Cassandra","productMemo": "Wide Column,Vector,Multi Model","productState": true},{"productId": "ddfe8f96-c49f-4ad0-899a-9b3b484419d8","productName": "Qdrant","productMemo": "Vector,NN,Semantic Matching","productState": true},{"productId": "cb99a803-2443-48ec-accb-5262ef9bc429","productName": "Trino","productMemo": "Relational,Document,Spatial,TS,KV","productState": true}
]
-
http://localhost:8080/api/product/getAllProductProduct?productMemo=vector
-
关于自定义索引,后续章节说明,此处不再赘述;
select * from elf.product where product_memo like '%vector%';
----[source,sql]
----
InvalidRequest: Error from server: code=2200 [Invalid query] msg="like restriction only support on properly indexed columnproduct_memo LIKE '%vector%' is not valid."
Delete Product
-
请求方法:DELETE;
-
http://localhost:8080/api/deleteProductById/productId
http://localhost:8080/api/deleteProductById/6d580a60-daba-46cc-b3d9-6061aa48f0ff
Delete All Product
-
请求方法:DELETE;
-
http://localhost:8080/api/product/deleteAllProduct
Retrieve By State
-
请求方法:GET;
-
http://localhost:8080/api/product/getProductByState
[{"productId": "29af0738-4a17-4571-90cd-700ab8995db7","productName": "Milvus","productMemo": "Vector,Similarity Search,ML","productState": true},{"productId": "741e5240-8a7b-40fa-b07c-e88b3638bf36","productName": "JanusGraph","productMemo": "Graph,Open Source","productState": true},{"productId": "63608ee0-eaf1-41cf-970e-04238a556103","productName": "Weaviate","productMemo": "Vector,AI,Real Time","productState": true},{"productId": "d4760f62-9b76-4c40-8a56-8c1e7214dfdd","productName": "Cassandra","productMemo": "Wide Column,Vector,Multi Model","productState": true},{"productId": "ddfe8f96-c49f-4ad0-899a-9b3b484419d8","productName": "Qdrant","productMemo": "Vector,NN,Semantic Matching","productState": true},{"productId": "cb99a803-2443-48ec-accb-5262ef9bc429","productName": "Trino","productMemo": "Relational,Document,Spatial,TS,KV","productState": true}
]