diff --git a/.env b/.env
index 96cd6e27ed183..6beb24525c5e1 100644
--- a/.env
+++ b/.env
@@ -5,12 +5,12 @@ IMAGE_ARCH=amd64
OS_NAME=ubuntu20.04
# for services.builder.image in docker-compose.yml
-DATE_VERSION=20240429-6289f3a
-LATEST_DATE_VERSION=20240429-6289f3a
+DATE_VERSION=20240520-d27db99
+LATEST_DATE_VERSION=20240520-d27db99
# for services.gpubuilder.image in docker-compose.yml
-GPU_DATE_VERSION=20240409-08bfb43
-LATEST_GPU_DATE_VERSION=20240409-08bfb43
+GPU_DATE_VERSION=20240520-c35eaaa
+LATEST_GPU_DATE_VERSION=20240520-c35eaaa
# for other services in docker-compose.yml
MINIO_ADDRESS=minio:9000
diff --git a/.github/workflows/mac.yaml b/.github/workflows/mac.yaml
index d17125b9d7c86..ccb21ebaab5af 100644
--- a/.github/workflows/mac.yaml
+++ b/.github/workflows/mac.yaml
@@ -56,7 +56,7 @@ jobs:
- name: Setup Go environment
uses: actions/setup-go@v2.2.0
with:
- go-version: '~1.20.7'
+ go-version: '~1.21.10'
- name: Mac Cache Go Mod Volumes
uses: actions/cache@v3
with:
diff --git a/.golangci.yml b/.golangci.yml
index 09779daf2548c..91895ce0cc115 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -1,5 +1,5 @@
run:
- go: "1.20"
+ go: "1.21"
skip-dirs:
- build
- configs
diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md
index cb6493a9fa12f..99bfc0f1546ae 100644
--- a/DEVELOPMENT.md
+++ b/DEVELOPMENT.md
@@ -104,7 +104,7 @@ You can use Vscode to integrate C++ and Go together. Please replace user.setting
Linux systems (Recommend Ubuntu 20.04 or later):
```bash
-go: >= 1.20
+go: >= 1.21
cmake: >= 3.18
gcc: 7.5
conan: 1.61
@@ -113,7 +113,7 @@ conan: 1.61
MacOS systems with x86_64 (Big Sur 11.5 or later recommended):
```bash
-go: >= 1.20
+go: >= 1.21
cmake: >= 3.18
llvm: >= 15
conan: 1.61
@@ -122,7 +122,7 @@ conan: 1.61
MacOS systems with Apple Silicon (Monterey 12.0.1 or later recommended):
```bash
-go: >= 1.20 (Arch=ARM64)
+go: >= 1.21 (Arch=ARM64)
cmake: >= 3.18
llvm: >= 15
conan: 1.61
@@ -178,7 +178,7 @@ Confirm that your `GOPATH` and `GOBIN` environment variables are correctly set a
```shell
$ go version
```
-Note: go >= 1.20 is required to build Milvus.
+Note: go >= 1.21 is required to build Milvus.
#### Docker & Docker Compose
@@ -236,7 +236,7 @@ sudo apt install -y clang-format clang-tidy ninja-build gcc g++ curl zip unzip t
#### Install conan
```bash
-# Verify python3 version, need python3 version > 3.8
+# Verify python3 version, need python3 version > 3.8 and version <= 3.11
python3 --version
# pip install conan 1.61.0
pip3 install conan==1.61.0
@@ -245,8 +245,8 @@ pip3 install conan==1.61.0
#### Install GO 1.80
```bash
-wget https://go.dev/dl/go1.18.10.linux-arm64.tar.gz
-tar zxf go1.18.10.linux-arm64.tar.gz
+wget https://go.dev/dl/go1.21.10.linux-arm64.tar.gz
+tar zxf go1.21.10.linux-arm64.tar.gz
mv ./go /usr/local
vi /etc/profile
export PATH=$PATH:/usr/local/go/bin
diff --git a/Makefile b/Makefile
index 24cf720f10e97..9ccfe22604b0e 100644
--- a/Makefile
+++ b/Makefile
@@ -17,6 +17,7 @@ OBJPREFIX := "github.com/milvus-io/milvus/cmd/milvus"
INSTALL_PATH := $(PWD)/bin
LIBRARY_PATH := $(PWD)/lib
+PGO_PATH := $(PWD)/configs/pgo
OS := $(shell uname -s)
mode = Release
@@ -72,14 +73,14 @@ milvus: build-cpp print-build-info
@echo "Building Milvus ..."
@source $(PWD)/scripts/setenv.sh && \
mkdir -p $(INSTALL_PATH) && go env -w CGO_ENABLED="1" && \
- GO111MODULE=on $(GO) build -ldflags="-r $${RPATH} -X '$(OBJPREFIX).BuildTags=$(BUILD_TAGS)' -X '$(OBJPREFIX).BuildTime=$(BUILD_TIME)' -X '$(OBJPREFIX).GitCommit=$(GIT_COMMIT)' -X '$(OBJPREFIX).GoVersion=$(GO_VERSION)'" \
+ GO111MODULE=on $(GO) build -pgo=$(PGO_PATH)/default.pgo -ldflags="-r $${RPATH} -X '$(OBJPREFIX).BuildTags=$(BUILD_TAGS)' -X '$(OBJPREFIX).BuildTime=$(BUILD_TIME)' -X '$(OBJPREFIX).GitCommit=$(GIT_COMMIT)' -X '$(OBJPREFIX).GoVersion=$(GO_VERSION)'" \
-tags dynamic -o $(INSTALL_PATH)/milvus $(PWD)/cmd/main.go 1>/dev/null
milvus-gpu: build-cpp-gpu print-gpu-build-info
@echo "Building Milvus-gpu ..."
@source $(PWD)/scripts/setenv.sh && \
mkdir -p $(INSTALL_PATH) && go env -w CGO_ENABLED="1" && \
- GO111MODULE=on $(GO) build -ldflags="-r $${RPATH} -X '$(OBJPREFIX).BuildTags=$(BUILD_TAGS_GPU)' -X '$(OBJPREFIX).BuildTime=$(BUILD_TIME)' -X '$(OBJPREFIX).GitCommit=$(GIT_COMMIT)' -X '$(OBJPREFIX).GoVersion=$(GO_VERSION)'" \
+ GO111MODULE=on $(GO) build -pgo=$(PGO_PATH)/default.pgo -ldflags="-r $${RPATH} -X '$(OBJPREFIX).BuildTags=$(BUILD_TAGS_GPU)' -X '$(OBJPREFIX).BuildTime=$(BUILD_TIME)' -X '$(OBJPREFIX).GitCommit=$(GIT_COMMIT)' -X '$(OBJPREFIX).GoVersion=$(GO_VERSION)'" \
-tags dynamic -o $(INSTALL_PATH)/milvus $(PWD)/cmd/main.go 1>/dev/null
get-build-deps:
@@ -106,7 +107,7 @@ getdeps:
tools/bin/revive: tools/check/go.mod
cd tools/check; \
- $(GO) build -o ../bin/revive github.com/mgechev/revive
+ $(GO) build -pgo=$(PGO_PATH)/default.pgo -o ../bin/revive github.com/mgechev/revive
cppcheck:
@#(env bash ${PWD}/scripts/core_build.sh -l)
@@ -142,20 +143,25 @@ lint-fix: getdeps
@$(INSTALL_PATH)/gofumpt -l -w internal/
@$(INSTALL_PATH)/gofumpt -l -w cmd/
@$(INSTALL_PATH)/gofumpt -l -w pkg/
+ @$(INSTALL_PATH)/gofumpt -l -w client/
@$(INSTALL_PATH)/gofumpt -l -w tests/integration/
@echo "Running gci fix"
@$(INSTALL_PATH)/gci write cmd/ --skip-generated -s standard -s default -s "prefix(github.com/milvus-io)" --custom-order
@$(INSTALL_PATH)/gci write internal/ --skip-generated -s standard -s default -s "prefix(github.com/milvus-io)" --custom-order
@$(INSTALL_PATH)/gci write pkg/ --skip-generated -s standard -s default -s "prefix(github.com/milvus-io)" --custom-order
+ @$(INSTALL_PATH)/gci write client/ --skip-generated -s standard -s default -s "prefix(github.com/milvus-io)" --custom-order
@$(INSTALL_PATH)/gci write tests/ --skip-generated -s standard -s default -s "prefix(github.com/milvus-io)" --custom-order
@echo "Running golangci-lint auto-fix"
- @source $(PWD)/scripts/setenv.sh && GO111MODULE=on $(INSTALL_PATH)/golangci-lint run --fix --timeout=30m --config $(PWD)/.golangci.yml; cd pkg && GO111MODULE=on $(INSTALL_PATH)/golangci-lint run --fix --timeout=30m --config $(PWD)/.golangci.yml
+ @source $(PWD)/scripts/setenv.sh && GO111MODULE=on $(INSTALL_PATH)/golangci-lint run --fix --timeout=30m --config $(PWD)/.golangci.yml;
+ @source $(PWD)/scripts/setenv.sh && cd pkg && GO111MODULE=on $(INSTALL_PATH)/golangci-lint run --fix --timeout=30m --config $(PWD)/.golangci.yml
+ @source $(PWD)/scripts/setenv.sh && cd client && GO111MODULE=on $(INSTALL_PATH)/golangci-lint run --fix --timeout=30m --config $(PWD)/client/.golangci.yml
#TODO: Check code specifications by golangci-lint
static-check: getdeps
@echo "Running $@ check"
@source $(PWD)/scripts/setenv.sh && GO111MODULE=on $(INSTALL_PATH)/golangci-lint run --timeout=30m --config $(PWD)/.golangci.yml
@source $(PWD)/scripts/setenv.sh && cd pkg && GO111MODULE=on $(INSTALL_PATH)/golangci-lint run --timeout=30m --config $(PWD)/.golangci.yml
+ @source $(PWD)/scripts/setenv.sh && cd client && GO111MODULE=on $(INSTALL_PATH)/golangci-lint run --timeout=30m --config $(PWD)/client/.golangci.yml
verifiers: build-cpp getdeps cppcheck fmt static-check
@@ -164,14 +170,14 @@ binlog:
@echo "Building binlog ..."
@source $(PWD)/scripts/setenv.sh && \
mkdir -p $(INSTALL_PATH) && go env -w CGO_ENABLED="1" && \
- GO111MODULE=on $(GO) build -ldflags="-r $${RPATH}" -o $(INSTALL_PATH)/binlog $(PWD)/cmd/tools/binlog/main.go 1>/dev/null
+ GO111MODULE=on $(GO) build -pgo=$(PGO_PATH)/default.pgo -ldflags="-r $${RPATH}" -o $(INSTALL_PATH)/binlog $(PWD)/cmd/tools/binlog/main.go 1>/dev/null
MIGRATION_PATH = $(PWD)/cmd/tools/migration
meta-migration:
@echo "Building migration tool ..."
@source $(PWD)/scripts/setenv.sh && \
mkdir -p $(INSTALL_PATH) && go env -w CGO_ENABLED="1" && \
- GO111MODULE=on $(GO) build -ldflags="-r $${RPATH} -X '$(OBJPREFIX).BuildTags=$(BUILD_TAGS)' -X '$(OBJPREFIX).BuildTime=$(BUILD_TIME)' -X '$(OBJPREFIX).GitCommit=$(GIT_COMMIT)' -X '$(OBJPREFIX).GoVersion=$(GO_VERSION)'" \
+ GO111MODULE=on $(GO) build -pgo=$(PGO_PATH)/default.pgo -ldflags="-r $${RPATH} -X '$(OBJPREFIX).BuildTags=$(BUILD_TAGS)' -X '$(OBJPREFIX).BuildTime=$(BUILD_TIME)' -X '$(OBJPREFIX).GitCommit=$(GIT_COMMIT)' -X '$(OBJPREFIX).GoVersion=$(GO_VERSION)'" \
-tags dynamic -o $(INSTALL_PATH)/meta-migration $(MIGRATION_PATH)/main.go 1>/dev/null
INTERATION_PATH = $(PWD)/tests/integration
@@ -366,7 +372,7 @@ clean:
milvus-tools: print-build-info
@echo "Building tools ..."
@mkdir -p $(INSTALL_PATH)/tools && go env -w CGO_ENABLED="1" && GO111MODULE=on $(GO) build \
- -ldflags="-X 'main.BuildTags=$(BUILD_TAGS)' -X 'main.BuildTime=$(BUILD_TIME)' -X 'main.GitCommit=$(GIT_COMMIT)' -X 'main.GoVersion=$(GO_VERSION)'" \
+ -pgo=$(PGO_PATH)/default.pgo -ldflags="-X 'main.BuildTags=$(BUILD_TAGS)' -X 'main.BuildTime=$(BUILD_TIME)' -X 'main.GitCommit=$(GIT_COMMIT)' -X 'main.GoVersion=$(GO_VERSION)'" \
-o $(INSTALL_PATH)/tools $(PWD)/cmd/tools/* 1>/dev/null
rpm-setup:
@@ -474,6 +480,7 @@ generate-mockery-datanode: getdeps
$(INSTALL_PATH)/mockery --name=BinlogIO --dir=$(PWD)/internal/datanode/io --output=$(PWD)/internal/datanode/io --filename=mock_binlogio.go --with-expecter --structname=MockBinlogIO --outpkg=io --inpackage
$(INSTALL_PATH)/mockery --name=FlowgraphManager --dir=$(PWD)/internal/datanode --output=$(PWD)/internal/datanode --filename=mock_fgmanager.go --with-expecter --structname=MockFlowgraphManager --outpkg=datanode --inpackage
$(INSTALL_PATH)/mockery --name=ChannelManager --dir=$(PWD)/internal/datanode --output=$(PWD)/internal/datanode --filename=mock_channelmanager.go --with-expecter --structname=MockChannelManager --outpkg=datanode --inpackage
+ $(INSTALL_PATH)/mockery --name=Compactor --dir=$(PWD)/internal/datanode/compaction --output=$(PWD)/internal/datanode/compaction --filename=mock_compactor.go --with-expecter --structname=MockCompactor --outpkg=compaction --inpackage
generate-mockery-metastore: getdeps
$(INSTALL_PATH)/mockery --name=RootCoordCatalog --dir=$(PWD)/internal/metastore --output=$(PWD)/internal/metastore/mocks --filename=mock_rootcoord_catalog.go --with-expecter --structname=RootCoordCatalog --outpkg=mocks
@@ -515,5 +522,5 @@ mmap-migration:
@echo "Building migration tool ..."
@source $(PWD)/scripts/setenv.sh && \
mkdir -p $(INSTALL_PATH) && go env -w CGO_ENABLED="1" && \
- GO111MODULE=on $(GO) build -ldflags="-r $${RPATH} -X '$(OBJPREFIX).BuildTags=$(BUILD_TAGS)' -X '$(OBJPREFIX).BuildTime=$(BUILD_TIME)' -X '$(OBJPREFIX).GitCommit=$(GIT_COMMIT)' -X '$(OBJPREFIX).GoVersion=$(GO_VERSION)'" \
- -tags dynamic -o $(INSTALL_PATH)/mmap-migration $(MMAP_MIGRATION_PATH)/main.go 1>/dev/null
\ No newline at end of file
+ GO111MODULE=on $(GO) build -pgo=$(PGO_PATH)/default.pgo -ldflags="-r $${RPATH} -X '$(OBJPREFIX).BuildTags=$(BUILD_TAGS)' -X '$(OBJPREFIX).BuildTime=$(BUILD_TIME)' -X '$(OBJPREFIX).GitCommit=$(GIT_COMMIT)' -X '$(OBJPREFIX).GoVersion=$(GO_VERSION)'" \
+ -tags dynamic -o $(INSTALL_PATH)/mmap-migration $(MMAP_MIGRATION_PATH)/main.go 1>/dev/null
diff --git a/README.md b/README.md
index d1bcc8413b7a0..f1566e7fd2719 100644
--- a/README.md
+++ b/README.md
@@ -72,23 +72,26 @@ Check the requirements first.
Linux systems (Ubuntu 20.04 or later recommended):
```bash
-go: >= 1.20
+go: >= 1.21
cmake: >= 3.26.4
gcc: 7.5
+python: > 3.8 and <= 3.11
```
MacOS systems with x86_64 (Big Sur 11.5 or later recommended):
```bash
-go: >= 1.20
+go: >= 1.21
cmake: >= 3.26.4
llvm: >= 15
+python: > 3.8 and <= 3.11
```
MacOS systems with Apple Silicon (Monterey 12.0.1 or later recommended):
```bash
-go: >= 1.20 (Arch=ARM64)
+go: >= 1.21 (Arch=ARM64)
cmake: >= 3.26.4
llvm: >= 15
+python: > 3.8 and <= 3.11
```
Clone Milvus repo and build.
@@ -169,7 +172,7 @@ Contributions to Milvus are welcome from everyone. See [Guidelines for Contribut
### All contributors
-
+
@@ -179,6 +182,7 @@ Contributions to Milvus are welcome from everyone. See [Guidelines for Contribut
+
@@ -212,6 +216,7 @@ Contributions to Milvus are welcome from everyone. See [Guidelines for Contribut
+
@@ -220,6 +225,7 @@ Contributions to Milvus are welcome from everyone. See [Guidelines for Contribut
+
@@ -384,7 +390,6 @@ Contributions to Milvus are welcome from everyone. See [Guidelines for Contribut
-
@@ -444,6 +449,7 @@ Contributions to Milvus are welcome from everyone. See [Guidelines for Contribut
+
diff --git a/README_CN.md b/README_CN.md
index 2b97a7138535b..e11333770fd7f 100644
--- a/README_CN.md
+++ b/README_CN.md
@@ -68,7 +68,7 @@ Milvus 基于 [Apache 2.0 License](https://github.com/milvus-io/milvus/blob/mast
请先安装相关依赖。
```
-go: 1.20
+go: 1.21
cmake: >=3.18
gcc: 7.5
protobuf: >=3.7
@@ -154,7 +154,7 @@ Milvus [训练营](https://github.com/milvus-io/bootcamp)能够帮助你了解
### All contributors
-
+
@@ -164,6 +164,7 @@ Milvus [训练营](https://github.com/milvus-io/bootcamp)能够帮助你了解
+
@@ -197,6 +198,7 @@ Milvus [训练营](https://github.com/milvus-io/bootcamp)能够帮助你了解
+
@@ -205,6 +207,7 @@ Milvus [训练营](https://github.com/milvus-io/bootcamp)能够帮助你了解
+
@@ -369,7 +372,6 @@ Milvus [训练营](https://github.com/milvus-io/bootcamp)能够帮助你了解
-
@@ -429,6 +431,7 @@ Milvus [训练营](https://github.com/milvus-io/bootcamp)能够帮助你了解
+
diff --git a/build/docker/builder/cpu/amazonlinux2023/Dockerfile b/build/docker/builder/cpu/amazonlinux2023/Dockerfile
index d5516fd46ab0f..0e0502d0ad621 100644
--- a/build/docker/builder/cpu/amazonlinux2023/Dockerfile
+++ b/build/docker/builder/cpu/amazonlinux2023/Dockerfile
@@ -14,10 +14,19 @@ FROM amazonlinux:2023
ARG TARGETARCH
RUN dnf install -y wget g++ gcc gdb libatomic libstdc++-static ninja-build git make zip unzip tar which \
- autoconf automake golang python3 python3-pip perl-FindBin texinfo \
+ autoconf automake python3 python3-pip perl-FindBin texinfo \
pkg-config libuuid-devel libaio perl-IPC-Cmd libasan openblas-devel && \
rm -rf /var/cache/yum/*
+ENV GOPATH /go
+ENV GOROOT /usr/local/go
+ENV GO111MODULE on
+ENV PATH $GOPATH/bin:$GOROOT/bin:$PATH
+RUN mkdir -p /usr/local/go && wget -qO- "https://go.dev/dl/go1.21.10.linux-$TARGETARCH.tar.gz" | tar --strip-components=1 -xz -C /usr/local/go && \
+ mkdir -p "$GOPATH/src" "$GOPATH/bin" && \
+ go clean --modcache && \
+ chmod -R 777 "$GOPATH" && chmod -R a+w $(go env GOTOOLDIR)
+
RUN pip3 install conan==1.61.0
RUN echo "target arch $TARGETARCH"
diff --git a/ci/jenkins/PR-Arm.groovy b/ci/jenkins/PR-Arm.groovy
new file mode 100644
index 0000000000000..cdf50e1678b27
--- /dev/null
+++ b/ci/jenkins/PR-Arm.groovy
@@ -0,0 +1,324 @@
+#!/usr/bin/env groovy
+
+int total_timeout_minutes = 60 * 5
+int e2e_timeout_seconds = 120 * 60
+def imageTag=''
+int case_timeout_seconds = 20 * 60
+def chart_version='4.1.28'
+pipeline {
+ options {
+ timestamps()
+ timeout(time: total_timeout_minutes, unit: 'MINUTES')
+ buildDiscarder logRotator(artifactDaysToKeepStr: '30')
+ parallelsAlwaysFailFast()
+ preserveStashes(buildCount: 5)
+ disableConcurrentBuilds(abortPrevious: true)
+
+ }
+ agent {
+ kubernetes {
+ cloud '4am'
+ defaultContainer 'main'
+ yamlFile 'ci/jenkins/pod/rte-arm.yaml'
+ customWorkspace '/home/jenkins/agent/workspace'
+ }
+ }
+ environment {
+ PROJECT_NAME = 'milvus'
+ SEMVER = "${BRANCH_NAME.contains('/') ? BRANCH_NAME.substring(BRANCH_NAME.lastIndexOf('/') + 1) : BRANCH_NAME}"
+ DOCKER_BUILDKIT = 1
+ ARTIFACTS = "${env.WORKSPACE}/_artifacts"
+ CI_DOCKER_CREDENTIAL_ID = "harbor-milvus-io-registry"
+ MILVUS_HELM_NAMESPACE = "milvus-ci"
+ DISABLE_KIND = true
+ HUB = 'harbor.milvus.io/milvus'
+ JENKINS_BUILD_ID = "${env.BUILD_ID}"
+ CI_MODE="pr"
+ SHOW_MILVUS_CONFIGMAP= true
+
+ DOCKER_CREDENTIALS_ID = "dockerhub"
+ TARGET_REPO = "milvusdb"
+ HARBOR_REPO = "harbor.milvus.io"
+ }
+
+ stages {
+ stage ('Build'){
+ steps {
+ container('main') {
+ script {
+ sh 'printenv'
+ def date = sh(returnStdout: true, script: 'date +%Y%m%d').trim()
+ sh 'git config --global --add safe.directory /home/jenkins/agent/workspace'
+ def gitShortCommit = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
+ imageTag="${env.BRANCH_NAME}-${date}-${gitShortCommit}"
+
+
+ sh """
+ echo "Building image with tag: ${imageTag}"
+
+ set -a # automatically export all variables from .env
+ . .env
+ set +a # stop automatically
+
+
+ docker run --net=host -v /root/.conan:/root/.conan -v \$(pwd):/root/milvus -w /root/milvus milvusdb/milvus-env:ubuntu20.04-\${DATE_VERSION} sh -c "make clean && make install"
+ """
+
+ withCredentials([usernamePassword(credentialsId: "${env.CI_DOCKER_CREDENTIAL_ID}", usernameVariable: 'CI_REGISTRY_USERNAME', passwordVariable: 'CI_REGISTRY_PASSWORD')]){
+ sh "docker login ${env.HARBOR_REPO} -u '${CI_REGISTRY_USERNAME}' -p '${CI_REGISTRY_PASSWORD}'"
+ sh """
+ export MILVUS_HARBOR_IMAGE_REPO="${env.HARBOR_REPO}/milvus/milvus"
+ export MILVUS_IMAGE_TAG="${imageTag}"
+
+ docker build --build-arg TARGETARCH=arm64 -f "./build/docker/milvus/ubuntu20.04/Dockerfile" -t \${MILVUS_HARBOR_IMAGE_REPO}:\${MILVUS_IMAGE_TAG} .
+
+ docker push \${MILVUS_HARBOR_IMAGE_REPO}:\${MILVUS_IMAGE_TAG}
+ docker logout
+ """
+ }
+
+ // stash imageTag info for rebuild install & E2E Test only
+ sh "echo ${imageTag} > imageTag.txt"
+ stash includes: 'imageTag.txt', name: 'imageTag'
+
+ }
+ }
+ }
+ }
+
+
+ stage('Install & E2E Test') {
+ matrix {
+ axes {
+ axis {
+ name 'MILVUS_SERVER_TYPE'
+ values 'standalone'
+ }
+ axis {
+ name 'MILVUS_CLIENT'
+ values 'pymilvus'
+ }
+ }
+
+ stages {
+ stage('Install') {
+ agent {
+ kubernetes {
+ cloud '4am'
+ inheritFrom 'milvus-e2e-4am'
+ defaultContainer 'main'
+ yamlFile 'ci/jenkins/pod/rte-build.yaml'
+ customWorkspace '/home/jenkins/agent/workspace'
+ }
+ }
+ steps {
+ container('main') {
+ stash includes: 'tests/**', name: 'testCode', useDefaultExcludes: false
+ dir ('tests/scripts') {
+ script {
+ sh 'printenv'
+ def clusterEnabled = "false"
+ def valuesFile = "pr-arm.yaml"
+
+ if ("${MILVUS_SERVER_TYPE}" == "standalone-one-pod") {
+ valuesFile = "nightly-one-pod.yaml"
+ }
+
+ if ("${MILVUS_CLIENT}" == "pymilvus") {
+ if ("${imageTag}"==''){
+ dir ("imageTag"){
+ try{
+ unstash 'imageTag'
+ imageTag=sh(returnStdout: true, script: 'cat imageTag.txt | tr -d \'\n\r\'')
+ }catch(e){
+ print "No Image Tag info remained ,please rerun build to build new image."
+ exit 1
+ }
+ }
+ }
+ // modify values file to enable kafka
+ if ("${MILVUS_SERVER_TYPE}".contains("kafka")) {
+ sh '''
+ apt-get update
+ apt-get install wget -y
+ wget https://github.com/mikefarah/yq/releases/download/v4.34.1/yq_linux_amd64 -O /usr/bin/yq
+ chmod +x /usr/bin/yq
+ '''
+ sh """
+ cp values/ci/pr-4am.yaml values/ci/pr_kafka.yaml
+ yq -i '.pulsar.enabled=false' values/ci/pr_kafka.yaml
+ yq -i '.kafka.enabled=true' values/ci/pr_kafka.yaml
+ yq -i '.kafka.metrics.kafka.enabled=true' values/ci/pr_kafka.yaml
+ yq -i '.kafka.metrics.jmx.enabled=true' values/ci/pr_kafka.yaml
+ yq -i '.kafka.metrics.serviceMonitor.enabled=true' values/ci/pr_kafka.yaml
+ """
+ }
+ withCredentials([usernamePassword(credentialsId: "${env.CI_DOCKER_CREDENTIAL_ID}", usernameVariable: 'CI_REGISTRY_USERNAME', passwordVariable: 'CI_REGISTRY_PASSWORD')]){
+ if ("${MILVUS_SERVER_TYPE}" == "standalone-one-pod") {
+ try {
+ sh """
+ MILVUS_CLUSTER_ENABLED=${clusterEnabled} \
+ MILVUS_HELM_REPO="https://nexus-ci.zilliz.cc/repository/milvus-proxy" \
+ TAG=${imageTag}\
+ ./e2e-k8s.sh \
+ --skip-export-logs \
+ --skip-cleanup \
+ --skip-setup \
+ --skip-test \
+ --skip-build \
+ --skip-build-image \
+ --install-extra-arg "
+ --set etcd.metrics.enabled=true \
+ --set etcd.metrics.podMonitor.enabled=true \
+ --set indexCoordinator.gc.interval=1 \
+ --set indexNode.disk.enabled=true \
+ --set queryNode.disk.enabled=true \
+ --set standalone.disk.enabled=true \
+ --version ${chart_version} \
+ -f values/ci/${valuesFile}"
+ """
+ } catch (Exception e) {
+ echo "Tests failed, but the build will not be marked as failed."
+ }
+
+ }else{
+ sh """
+ MILVUS_CLUSTER_ENABLED=${clusterEnabled} \
+ MILVUS_HELM_REPO="https://nexus-ci.zilliz.cc/repository/milvus-proxy" \
+ TAG=${imageTag}\
+ ./e2e-k8s.sh \
+ --skip-export-logs \
+ --skip-cleanup \
+ --skip-setup \
+ --skip-test \
+ --skip-build \
+ --skip-build-image \
+ --install-extra-arg "
+ --set etcd.metrics.enabled=true \
+ --set etcd.metrics.podMonitor.enabled=true \
+ --set indexCoordinator.gc.interval=1 \
+ --set indexNode.disk.enabled=true \
+ --set queryNode.disk.enabled=true \
+ --set standalone.disk.enabled=true \
+ --version ${chart_version} \
+ -f values/ci/${valuesFile}"
+ """
+ }
+ }
+ } else {
+ error "Error: Unsupported Milvus client: ${MILVUS_CLIENT}"
+ }
+ }
+ }
+ }
+
+ }
+ }
+ stage('E2E Test'){
+ options {
+ skipDefaultCheckout()
+ }
+ agent {
+ kubernetes {
+ cloud '4am'
+ inheritFrom 'default'
+ defaultContainer 'main'
+ yamlFile 'ci/jenkins/pod/e2e.yaml'
+ customWorkspace '/home/jenkins/agent/workspace'
+ }
+ }
+ steps {
+ container('pytest') {
+ unstash('testCode')
+ script {
+ sh 'ls -lah'
+ }
+ dir ('tests/scripts') {
+ script {
+ def release_name=sh(returnStdout: true, script: './get_release_name.sh')
+ def clusterEnabled = 'false'
+ if ("${MILVUS_SERVER_TYPE}".contains("distributed")) {
+ clusterEnabled = "true"
+ }
+ if ("${MILVUS_CLIENT}" == "pymilvus") {
+ if ("${MILVUS_SERVER_TYPE}" == "standalone-one-pod") {
+ try {
+ sh """
+ MILVUS_HELM_RELEASE_NAME="${release_name}" \
+ MILVUS_HELM_NAMESPACE="milvus-ci" \
+ MILVUS_CLUSTER_ENABLED="${clusterEnabled}" \
+ TEST_TIMEOUT="${e2e_timeout_seconds}" \
+ ./ci_e2e_4am.sh "-n 6 -x --tags L0 L1 --timeout ${case_timeout_seconds}"
+ """
+ } catch (Exception e) {
+ echo "Tests failed, but the build will not be marked as failed."
+ }
+ }else{
+ sh """
+ MILVUS_HELM_RELEASE_NAME="${release_name}" \
+ MILVUS_HELM_NAMESPACE="milvus-ci" \
+ MILVUS_CLUSTER_ENABLED="${clusterEnabled}" \
+ TEST_TIMEOUT="${e2e_timeout_seconds}" \
+ ./ci_e2e_4am.sh "-n 6 -x --tags L0 L1 --timeout ${case_timeout_seconds}"
+ """
+ }
+ } else {
+ error "Error: Unsupported Milvus client: ${MILVUS_CLIENT}"
+ }
+ }
+ }
+ }
+ }
+ post{
+ always {
+ container('pytest'){
+ dir("${env.ARTIFACTS}") {
+ sh "tar -zcvf ${PROJECT_NAME}-${MILVUS_SERVER_TYPE}-${MILVUS_CLIENT}-pytest-logs.tar.gz /tmp/ci_logs/test --remove-files || true"
+ archiveArtifacts artifacts: "${PROJECT_NAME}-${MILVUS_SERVER_TYPE}-${MILVUS_CLIENT}-pytest-logs.tar.gz ", allowEmptyArchive: true
+ }
+ }
+ }
+
+ }
+ }
+ }
+ post{
+ always {
+ container('main') {
+ dir ('tests/scripts') {
+ script {
+ def release_name=sh(returnStdout: true, script: './get_release_name.sh')
+ sh "kubectl get pods -n ${MILVUS_HELM_NAMESPACE} | grep ${release_name} "
+ sh "./uninstall_milvus.sh --release-name ${release_name}"
+ sh "./ci_logs.sh --log-dir /ci-logs --artifacts-name ${env.ARTIFACTS}/artifacts-${PROJECT_NAME}-${MILVUS_SERVER_TYPE}-${SEMVER}-${env.BUILD_NUMBER}-${MILVUS_CLIENT}-e2e-logs \
+ --release-name ${release_name}"
+ dir("${env.ARTIFACTS}") {
+ archiveArtifacts artifacts: "artifacts-${PROJECT_NAME}-${MILVUS_SERVER_TYPE}-${SEMVER}-${env.BUILD_NUMBER}-${MILVUS_CLIENT}-e2e-logs.tar.gz", allowEmptyArchive: true
+ }
+ }
+ }
+ }
+ }
+ }
+
+ }
+
+ }
+ }
+ post{
+ unsuccessful {
+ container('jnlp') {
+ dir ('tests/scripts') {
+ script {
+ def authorEmail = sh(returnStdout: true, script: './get_author_email.sh ')
+ emailext subject: '$DEFAULT_SUBJECT',
+ body: '$DEFAULT_CONTENT',
+ recipientProviders: [developers(), culprits()],
+ replyTo: '$DEFAULT_REPLYTO',
+ to: "${authorEmail},devops@zilliz.com"
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/ci/jenkins/PublishArmBasedGPUImages.groovy b/ci/jenkins/PublishArmBasedGPUImages.groovy
index 3ed8affb075f4..4540c6f45c3e9 100644
--- a/ci/jenkins/PublishArmBasedGPUImages.groovy
+++ b/ci/jenkins/PublishArmBasedGPUImages.groovy
@@ -2,7 +2,15 @@
pipeline {
agent {
- label 'arm'
+ kubernetes {
+ cloud '4am'
+ defaultContainer 'main'
+ yamlFile "ci/jenkins/pod/rte-arm.yaml"
+ customWorkspace '/home/jenkins/agent/workspace'
+ // We allow this pod to remain active for a while, later jobs can
+ // reuse cache in previous created nodes.
+ // idleMinutes 120
+ }
}
options {
@@ -26,16 +34,20 @@ pipeline {
steps {
script {
sh """
- set -a # automatically export all variables from .env
- . ${WORKSPACE}/.env
- set +a # stop automatically
-
- docker run -v \$(pwd):/root/milvus -v \$(pwd)/.docker/.conan:/root/.conan -w /root/milvus milvusdb/milvus-env:gpu-ubuntu22.04-\${GPU_DATE_VERSION} sh -c "make clean && make gpu-install"
+ git config --global --add safe.directory /home/jenkins/agent/workspace
"""
def date = sh(returnStdout: true, script: 'date +%Y%m%d').trim()
def gitShortCommit = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
+ sh """
+ set -a # automatically export all variables from .env
+ . .env
+ set +a # stop automatically
+
+ docker run --net=host -v \$(pwd):/root/milvus -v /root/.conan:/root/.conan -w /root/milvus milvusdb/milvus-env:gpu-ubuntu22.04-\${GPU_DATE_VERSION} sh -c "make clean && make gpu-install"
+ """
+
withCredentials([usernamePassword(credentialsId: "${env.DOCKER_CREDENTIALS_ID}", usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
sh 'docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD}'
sh """
diff --git a/ci/jenkins/PublishArmBasedImages.groovy b/ci/jenkins/PublishArmBasedImages.groovy
new file mode 100644
index 0000000000000..0fd77c7da7666
--- /dev/null
+++ b/ci/jenkins/PublishArmBasedImages.groovy
@@ -0,0 +1,93 @@
+#!/usr/bin/env groovy
+
+pipeline {
+ agent {
+ kubernetes {
+ cloud '4am'
+ defaultContainer 'main'
+ yamlFile "ci/jenkins/pod/rte-arm.yaml"
+ customWorkspace '/home/jenkins/agent/workspace'
+ // We allow this pod to remain active for a while, later jobs can
+ // reuse cache in previous created nodes.
+ // idleMinutes 120
+ }
+ }
+ parameters {
+ string(name: 'image-tag', defaultValue: '', description: 'the image tag to be pushed to image registry')
+ }
+
+ options {
+ timestamps()
+ timeout(time: 300, unit: 'MINUTES')
+ // parallelsAlwaysFailFast()
+ disableConcurrentBuilds()
+ }
+
+ environment {
+ DOCKER_CREDENTIALS_ID = "dockerhub"
+ DOCKER_BUILDKIT = 1
+ TARGET_REPO = "milvusdb"
+ CI_DOCKER_CREDENTIAL_ID = "harbor-milvus-io-registry"
+ HARBOR_REPO = "harbor.milvus.io"
+ }
+
+ stages {
+ stage('Publish Milvus cpu Images'){
+
+ steps {
+ script {
+ sh """
+ git config --global --add safe.directory /home/jenkins/agent/workspace
+ """
+
+ def tag = ""
+ if (params['image-tag'] == '') {
+ def date = sh(returnStdout: true, script: 'date +%Y%m%d').trim()
+ def gitShortCommit = sh(returnStdout: true, script: 'git rev-parse --short HEAD').trim()
+ tag = "${env.BRANCH_NAME}-${date}-${gitShortCommit}-arm"
+ }else{
+ tag = params['image-tag']
+ }
+
+ sh """
+ echo "Building image with tag: ${tag}"
+
+ set -a # automatically export all variables from .env
+ . .env
+ set +a # stop automatically
+
+
+ docker run --net=host -v /root/.conan:/root/.conan -v \$(pwd):/root/milvus -w /root/milvus milvusdb/milvus-env:ubuntu20.04-\${DATE_VERSION} sh -c "make clean && make install"
+ """
+
+
+ withCredentials([usernamePassword(credentialsId: "${env.DOCKER_CREDENTIALS_ID}", usernameVariable: 'DOCKER_USERNAME', passwordVariable: 'DOCKER_PASSWORD')]) {
+ sh 'docker login -u ${DOCKER_USERNAME} -p ${DOCKER_PASSWORD}'
+ sh """
+ export MILVUS_IMAGE_REPO="${env.TARGET_REPO}/milvus"
+ export MILVUS_HARBOR_IMAGE_REPO="${env.HARBOR_REPO}/milvus/milvus"
+ export MILVUS_IMAGE_TAG="${tag}"
+
+ docker build --build-arg TARGETARCH=arm64 -f "./build/docker/milvus/ubuntu20.04/Dockerfile" -t \${MILVUS_IMAGE_REPO}:\${MILVUS_IMAGE_TAG} .
+
+ docker push \${MILVUS_IMAGE_REPO}:\${MILVUS_IMAGE_TAG}
+ docker tag \${MILVUS_IMAGE_REPO}:\${MILVUS_IMAGE_TAG} \${MILVUS_HARBOR_IMAGE_REPO}:\${MILVUS_IMAGE_TAG}
+ docker logout
+ """
+ }
+
+ withCredentials([usernamePassword(credentialsId: "${env.CI_DOCKER_CREDENTIAL_ID}", usernameVariable: 'CI_REGISTRY_USERNAME', passwordVariable: 'CI_REGISTRY_PASSWORD')]){
+ sh "docker login ${env.HARBOR_REPO} -u '${CI_REGISTRY_USERNAME}' -p '${CI_REGISTRY_PASSWORD}'"
+ sh """
+ export MILVUS_HARBOR_IMAGE_REPO="${env.HARBOR_REPO}/milvus/milvus"
+ export MILVUS_IMAGE_TAG="${tag}"
+ docker push \${MILVUS_HARBOR_IMAGE_REPO}:\${MILVUS_IMAGE_TAG}
+ docker logout
+ """
+ }
+ }
+ }
+ }
+ }
+
+}
diff --git a/ci/jenkins/pod/rte-arm.yaml b/ci/jenkins/pod/rte-arm.yaml
new file mode 100644
index 0000000000000..7d10349f40275
--- /dev/null
+++ b/ci/jenkins/pod/rte-arm.yaml
@@ -0,0 +1,66 @@
+apiVersion: v1
+kind: Pod
+metadata:
+ labels:
+ app: milvus-e2e
+ namespace: milvus-ci
+spec:
+ hostNetwork: true
+ securityContext: # Optional: Restrict capabilities for some security hardening
+ privileged: true
+ tolerations:
+ - key: "node-role.kubernetes.io/arm"
+ operator: "Exists"
+ effect: "NoSchedule"
+ nodeSelector:
+ "kubernetes.io/arch": "arm64"
+ enableServiceLinks: false
+ containers:
+ - name: main
+ image: docker:latest
+ args: ["sleep", "36000"]
+ # workingDir: /home/jenkins/agent/workspace
+ securityContext:
+ privileged: true
+ resources:
+ limits:
+ cpu: "6"
+ memory: 12Gi
+ requests:
+ cpu: "0.5"
+ memory: 5Gi
+ volumeMounts:
+ - mountPath: /var/run
+ name: docker-root
+ - mountPath: /root/.conan
+ name: build-cache
+ # - mountPath: /ci-logs
+ # name: ci-logs
+ - name: dind
+ image: docker:dind
+ securityContext:
+ privileged: true
+ args: ["dockerd","--host=unix:///var/run/docker.sock","--registry-mirror=https://docker-nexus-ci.zilliz.cc"]
+ resources:
+ limits:
+ cpu: "6"
+ memory: 12Gi
+ requests:
+ cpu: "0.5"
+ memory: 5Gi
+ volumeMounts:
+ - mountPath: /var/run
+ name: docker-root
+ - mountPath: /root/.conan
+ name: build-cache
+ volumes:
+ - emptyDir: {}
+ name: docker-root
+ - hostPath:
+ path: /root/.conan
+ type: DirectoryOrCreate
+ name: build-cache
+ # - name: ci-logs
+ # nfs:
+ # path: /ci-logs
+ # server: 172.16.70.249
diff --git a/client/.golangci.yml b/client/.golangci.yml
new file mode 100644
index 0000000000000..8b90a9f55a473
--- /dev/null
+++ b/client/.golangci.yml
@@ -0,0 +1,172 @@
+run:
+ go: "1.21"
+ skip-dirs:
+ - build
+ - configs
+ - deployments
+ - docs
+ - scripts
+ - internal/core
+ - cmake_build
+ skip-files:
+ - partial_search_test.go
+
+linters:
+ disable-all: true
+ enable:
+ - gosimple
+ - govet
+ - ineffassign
+ - staticcheck
+ - decorder
+ - depguard
+ - gofmt
+ - goimports
+ - gosec
+ - revive
+ - unconvert
+ - misspell
+ - typecheck
+ - durationcheck
+ - forbidigo
+ - gci
+ - whitespace
+ - gofumpt
+ - gocritic
+
+linters-settings:
+ gci:
+ sections:
+ - standard
+ - default
+ - prefix(github.com/milvus-io)
+ custom-order: true
+ gofumpt:
+ lang-version: "1.18"
+ module-path: github.com/milvus-io
+ goimports:
+ local-prefixes: github.com/milvus-io
+ revive:
+ rules:
+ - name: unused-parameter
+ disabled: true
+ - name: var-naming
+ severity: warning
+ disabled: false
+ arguments:
+ - ["ID"] # Allow list
+ - name: context-as-argument
+ severity: warning
+ disabled: false
+ arguments:
+ - allowTypesBefore: "*testing.T"
+ - name: datarace
+ severity: warning
+ disabled: false
+ - name: duplicated-imports
+ severity: warning
+ disabled: false
+ - name: waitgroup-by-value
+ severity: warning
+ disabled: false
+ - name: indent-error-flow
+ severity: warning
+ disabled: false
+ arguments:
+ - "preserveScope"
+ - name: range-val-in-closure
+ severity: warning
+ disabled: false
+ - name: range-val-address
+ severity: warning
+ disabled: false
+ - name: string-of-int
+ severity: warning
+ disabled: false
+ misspell:
+ locale: US
+ gocritic:
+ enabled-checks:
+ - ruleguard
+ settings:
+ ruleguard:
+ failOnError: true
+ rules: "ruleguard/rules.go"
+ depguard:
+ rules:
+ main:
+ deny:
+ - pkg: "errors"
+ desc: not allowed, use github.com/cockroachdb/errors
+ - pkg: "github.com/pkg/errors"
+ desc: not allowed, use github.com/cockroachdb/errors
+ - pkg: "github.com/pingcap/errors"
+ desc: not allowed, use github.com/cockroachdb/errors
+ - pkg: "golang.org/x/xerrors"
+ desc: not allowed, use github.com/cockroachdb/errors
+ - pkg: "github.com/go-errors/errors"
+ desc: not allowed, use github.com/cockroachdb/errors
+ - pkg: "io/ioutil"
+ desc: ioutil is deprecated after 1.16, 1.17, use os and io package instead
+ - pkg: "github.com/tikv/client-go/rawkv"
+ desc: not allowed, use github.com/tikv/client-go/v2/txnkv
+ - pkg: "github.com/tikv/client-go/v2/rawkv"
+ desc: not allowed, use github.com/tikv/client-go/v2/txnkv
+ forbidigo:
+ forbid:
+ - '^time\.Tick$'
+ - 'return merr\.Err[a-zA-Z]+'
+ - 'merr\.Wrap\w+\(\)\.Error\(\)'
+ - '\.(ErrorCode|Reason) = '
+ - 'Reason:\s+\w+\.Error\(\)'
+ - 'errors.New\((.+)\.GetReason\(\)\)'
+ - 'commonpb\.Status\{[\s\n]*ErrorCode:[\s\n]*.+[\s\S\n]*?\}'
+ - 'os\.Open\(.+\)'
+ - 'os\.ReadFile\(.+\)'
+ - 'os\.WriteFile\(.+\)'
+ - "runtime.NumCPU"
+ - "runtime.GOMAXPROCS(0)"
+ #- 'fmt\.Print.*' WIP
+
+issues:
+ exclude-use-default: false
+ exclude-rules:
+ - path: .+_test\.go
+ linters:
+ - forbidigo
+ exclude:
+ - should have a package comment
+ - should have comment
+ - should be of the form
+ - should not use dot imports
+ - which can be annoying to use
+ # Binds to all network interfaces
+ - G102
+ # Use of unsafe calls should be audited
+ - G103
+ # Errors unhandled
+ - G104
+ # file/folder Permission
+ - G301
+ - G302
+ # Potential file inclusion via variable
+ - G304
+ # Deferring unsafe method like *os.File Close
+ - G307
+ # TLS MinVersion too low
+ - G402
+ # Use of weak random number generator math/rand
+ - G404
+ # Unused parameters
+ - SA1019
+ # defer return errors
+ - SA5001
+
+ # Maximum issues count per one linter. Set to 0 to disable. Default is 50.
+ max-issues-per-linter: 0
+ # Maximum count of issues with the same text. Set to 0 to disable. Default is 3.
+ max-same-issues: 0
+
+service:
+ # use the fixed version to not introduce new linters unexpectedly
+ golangci-lint-version: 1.55.2
diff --git a/client/client_config.go b/client/client_config.go
index 63a4f6d2b8565..01f82877f7967 100644
--- a/client/client_config.go
+++ b/client/client_config.go
@@ -10,10 +10,11 @@ import (
"time"
"github.com/cockroachdb/errors"
- "github.com/milvus-io/milvus/pkg/util/crypto"
"google.golang.org/grpc"
"google.golang.org/grpc/backoff"
"google.golang.org/grpc/keepalive"
+
+ "github.com/milvus-io/milvus/pkg/util/crypto"
)
const (
diff --git a/client/client_test.go b/client/client_test.go
index f23a9d9941d83..c6d0867ee8af3 100644
--- a/client/client_test.go
+++ b/client/client_test.go
@@ -31,7 +31,7 @@ func (s *ClientSuite) TestNewClient() {
s.NotNil(c)
})
- s.Run("emtpy_addr", func() {
+ s.Run("empty_addr", func() {
_, err := New(ctx, &ClientConfig{})
s.Error(err)
s.T().Log(err)
diff --git a/client/collection.go b/client/collection.go
index 039ff2460d64c..4031c687d9993 100644
--- a/client/collection.go
+++ b/client/collection.go
@@ -62,10 +62,6 @@ func (c *Client) CreateCollection(ctx context.Context, option CreateCollectionOp
return nil
}
-type ListCollectionOption interface {
- Request() *milvuspb.ShowCollectionsRequest
-}
-
func (c *Client) ListCollections(ctx context.Context, option ListCollectionOption, callOptions ...grpc.CallOption) (collectionNames []string, err error) {
req := option.Request()
err = c.callService(func(milvusService milvuspb.MilvusServiceClient) error {
@@ -82,7 +78,7 @@ func (c *Client) ListCollections(ctx context.Context, option ListCollectionOptio
return collectionNames, err
}
-func (c *Client) DescribeCollection(ctx context.Context, option *describeCollectionOption, callOptions ...grpc.CallOption) (collection *entity.Collection, err error) {
+func (c *Client) DescribeCollection(ctx context.Context, option DescribeCollectionOption, callOptions ...grpc.CallOption) (collection *entity.Collection, err error) {
req := option.Request()
err = c.callService(func(milvusService milvuspb.MilvusServiceClient) error {
resp, err := milvusService.DescribeCollection(ctx, req, callOptions...)
diff --git a/client/collection_options.go b/client/collection_options.go
index adb59e37b5145..696fe702273a2 100644
--- a/client/collection_options.go
+++ b/client/collection_options.go
@@ -159,6 +159,10 @@ func NewCreateCollectionOption(name string, collectionSchema *entity.Schema) *cr
}
}
+type ListCollectionOption interface {
+ Request() *milvuspb.ShowCollectionsRequest
+}
+
type listCollectionOption struct{}
func (opt *listCollectionOption) Request() *milvuspb.ShowCollectionsRequest {
diff --git a/client/column/columns.go b/client/column/columns.go
index 8a2a52d87941f..a30b064e15235 100644
--- a/client/column/columns.go
+++ b/client/column/columns.go
@@ -239,7 +239,7 @@ func FieldDataColumn(fd *schemapb.FieldData, begin, end int) (Column, error) {
data := x.FloatVector.GetData()
dim := int(vectors.GetDim())
if end < 0 {
- end = int(len(data) / dim)
+ end = len(data) / dim
}
vector := make([][]float32, 0, end-begin) // shall not have remanunt
for i := begin; i < end; i++ {
@@ -262,7 +262,7 @@ func FieldDataColumn(fd *schemapb.FieldData, begin, end int) (Column, error) {
dim := int(vectors.GetDim())
blen := dim / 8
if end < 0 {
- end = int(len(data) / blen)
+ end = len(data) / blen
}
vector := make([][]byte, 0, end-begin)
for i := begin; i < end; i++ {
@@ -281,7 +281,7 @@ func FieldDataColumn(fd *schemapb.FieldData, begin, end int) (Column, error) {
data := x.Float16Vector
dim := int(vectors.GetDim())
if end < 0 {
- end = int(len(data) / dim)
+ end = len(data) / dim
}
vector := make([][]byte, 0, end-begin)
for i := begin; i < end; i++ {
@@ -300,7 +300,7 @@ func FieldDataColumn(fd *schemapb.FieldData, begin, end int) (Column, error) {
data := x.Bfloat16Vector
dim := int(vectors.GetDim())
if end < 0 {
- end = int(len(data) / dim)
+ end = len(data) / dim
}
vector := make([][]byte, 0, end-begin) // shall not have remanunt
for i := begin; i < end; i++ {
diff --git a/client/column/sparse.go b/client/column/sparse.go
index b9d20fd616ded..cc02e3ee2ffe2 100644
--- a/client/column/sparse.go
+++ b/client/column/sparse.go
@@ -22,6 +22,7 @@ import (
"math"
"github.com/cockroachdb/errors"
+
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
"github.com/milvus-io/milvus/client/v2/entity"
)
diff --git a/client/column/sparse_test.go b/client/column/sparse_test.go
index 387df9efe7d7c..564f223ff1532 100644
--- a/client/column/sparse_test.go
+++ b/client/column/sparse_test.go
@@ -21,9 +21,10 @@ import (
"math/rand"
"testing"
- "github.com/milvus-io/milvus/client/v2/entity"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
+
+ "github.com/milvus-io/milvus/client/v2/entity"
)
func TestColumnSparseEmbedding(t *testing.T) {
diff --git a/client/column/varchar.go b/client/column/varchar.go
index 9ed1646450189..63aff96ae94c8 100644
--- a/client/column/varchar.go
+++ b/client/column/varchar.go
@@ -17,9 +17,10 @@
package column
import (
- "errors"
"fmt"
+ "github.com/cockroachdb/errors"
+
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
"github.com/milvus-io/milvus/client/v2/entity"
)
@@ -70,7 +71,7 @@ func (c *ColumnVarChar) FieldData() *schemapb.FieldData {
}
data := make([]string, 0, c.Len())
for i := 0; i < c.Len(); i++ {
- data = append(data, string(c.values[i]))
+ data = append(data, c.values[i])
}
fd.Field = &schemapb.FieldData_Scalars{
Scalars: &schemapb.ScalarField{
diff --git a/client/database_test.go b/client/database_test.go
index f46a0cafb8b7b..d7555d7d5aa44 100644
--- a/client/database_test.go
+++ b/client/database_test.go
@@ -5,11 +5,12 @@ import (
"fmt"
"testing"
+ mock "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/suite"
+
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/pkg/util/merr"
- mock "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/suite"
)
type DatabaseSuite struct {
diff --git a/client/entity/schema.go b/client/entity/schema.go
index ce30b53f51483..8225ba6c2fd3c 100644
--- a/client/entity/schema.go
+++ b/client/entity/schema.go
@@ -19,6 +19,8 @@ package entity
import (
"strconv"
+ "github.com/cockroachdb/errors"
+
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
)
@@ -293,6 +295,18 @@ func (f *Field) WithDim(dim int64) *Field {
return f
}
+func (f *Field) GetDim() (int64, error) {
+ dimStr, has := f.TypeParams[TypeParamDim]
+ if !has {
+ return -1, errors.New("field with no dim")
+ }
+ dim, err := strconv.ParseInt(dimStr, 10, 64)
+ if err != nil {
+ return -1, errors.Newf("field with bad format dim: %s", err.Error())
+ }
+ return dim, nil
+}
+
func (f *Field) WithMaxLength(maxLen int64) *Field {
if f.TypeParams == nil {
f.TypeParams = make(map[string]string)
diff --git a/client/entity/sparse.go b/client/entity/sparse.go
index 00f41c60d355e..87edf58d152b2 100644
--- a/client/entity/sparse.go
+++ b/client/entity/sparse.go
@@ -29,6 +29,7 @@ type SparseEmbedding interface {
Len() int // the actual items in this vector
Get(idx int) (pos uint32, value float32, ok bool)
Serialize() []byte
+ FieldType() FieldType
}
var (
@@ -56,7 +57,7 @@ func (e sliceSparseEmbedding) FieldType() FieldType {
}
func (e sliceSparseEmbedding) Get(idx int) (uint32, float32, bool) {
- if idx < 0 || idx >= int(e.len) {
+ if idx < 0 || idx >= e.len {
return 0, 0, false
}
return e.positions[idx], e.values[idx], true
@@ -88,7 +89,7 @@ func deserializeSliceSparceEmbedding(bs []byte) (sliceSparseEmbedding, error) {
return sliceSparseEmbedding{}, errors.New("not valid sparse embedding bytes")
}
- length = length / 8
+ length /= 8
result := sliceSparseEmbedding{
positions: make([]uint32, length),
diff --git a/client/example/database/main.go b/client/example/database/main.go
index 5b978b6261549..0069923d9a2c6 100644
--- a/client/example/database/main.go
+++ b/client/example/database/main.go
@@ -5,6 +5,7 @@ import (
"log"
milvusclient "github.com/milvus-io/milvus/client/v2"
+ "github.com/milvus-io/milvus/client/v2/entity"
)
const (
@@ -34,4 +35,55 @@ func main() {
log.Fatal("failed to list databases", err.Error())
}
log.Println("=== Databases: ", dbNames)
+
+ schema := entity.NewSchema().WithName("hello_milvus").
+ WithField(entity.NewField().WithName("ID").WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true)).
+ WithField(entity.NewField().WithName("Vector").WithDataType(entity.FieldTypeFloatVector).WithDim(128))
+
+ if err := c.CreateCollection(ctx, milvusclient.NewCreateCollectionOption("hello_milvus", schema)); err != nil {
+ log.Fatal("failed to create collection:", err.Error())
+ }
+
+ collections, err := c.ListCollections(ctx, milvusclient.NewListCollectionOption())
+ if err != nil {
+ log.Fatal("failed to list collections,", err.Error())
+ }
+
+ for _, collectionName := range collections {
+ collection, err := c.DescribeCollection(ctx, milvusclient.NewDescribeCollectionOption(collectionName))
+ if err != nil {
+ log.Fatal(err.Error())
+ }
+ log.Println(collection.Name)
+ for _, field := range collection.Schema.Fields {
+ log.Println("=== Field: ", field.Name, field.DataType, field.AutoID)
+ }
+ }
+
+ c.CreateDatabase(ctx, milvusclient.NewCreateDatabaseOption("test"))
+ c.UsingDatabase(ctx, milvusclient.NewUsingDatabaseOption("test"))
+
+ schema = entity.NewSchema().WithName("hello_milvus").
+ WithField(entity.NewField().WithName("ID").WithDataType(entity.FieldTypeVarChar).WithMaxLength(64).WithIsPrimaryKey(true)).
+ WithField(entity.NewField().WithName("Vector").WithDataType(entity.FieldTypeFloatVector).WithDim(128))
+
+ if err := c.CreateCollection(ctx, milvusclient.NewCreateCollectionOption("hello_milvus", schema)); err != nil {
+ log.Fatal("failed to create collection:", err.Error())
+ }
+
+ collections, err = c.ListCollections(ctx, milvusclient.NewListCollectionOption())
+ if err != nil {
+ log.Fatal("failed to list collections,", err.Error())
+ }
+
+ for _, collectionName := range collections {
+ collection, err := c.DescribeCollection(ctx, milvusclient.NewDescribeCollectionOption(collectionName))
+ if err != nil {
+ log.Fatal(err.Error())
+ }
+ log.Println(collection.Name)
+ for _, field := range collection.Schema.Fields {
+ log.Println("=== Field: ", field.Name, field.DataType, field.AutoID)
+ }
+ }
}
diff --git a/client/example/playground/main.go b/client/example/playground/main.go
index e5984648cf71c..43ae57915cfd2 100644
--- a/client/example/playground/main.go
+++ b/client/example/playground/main.go
@@ -18,6 +18,7 @@ const (
helloMilvusCmd = `hello_milvus`
partitionsCmd = `partitions`
indexCmd = `indexes`
+ countCmd = `count`
milvusAddr = `localhost:19530`
nEntities, dim = 3000, 128
@@ -38,9 +39,109 @@ func main() {
Partitions()
case indexCmd:
Indexes()
+ case countCmd:
+ Count()
}
}
+func Count() {
+ ctx := context.Background()
+
+ collectionName := "hello_count_inverted"
+
+ c, err := milvusclient.New(ctx, &milvusclient.ClientConfig{
+ Address: "127.0.0.1:19530",
+ })
+ if err != nil {
+ log.Fatal("failed to connect to milvus, err: ", err.Error())
+ }
+
+ schema := entity.NewSchema().WithName(collectionName).
+ WithField(entity.NewField().WithName("id").WithDataType(entity.FieldTypeInt64).WithIsAutoID(true).WithIsPrimaryKey(true)).
+ WithField(entity.NewField().WithName("vector").WithDataType(entity.FieldTypeFloatVector).WithDim(128))
+
+ err = c.CreateCollection(ctx, milvusclient.NewCreateCollectionOption(collectionName, schema))
+ if err != nil {
+ log.Fatal("failed to connect to milvus, err: ", err.Error())
+ }
+
+ indexTask, err := c.CreateIndex(ctx, milvusclient.NewCreateIndexOption(collectionName, "id", index.NewGenericIndex("inverted", map[string]string{})))
+ if err != nil {
+ log.Fatal("failed to connect to milvus, err: ", err.Error())
+ }
+
+ indexTask.Await(ctx)
+
+ indexTask, err = c.CreateIndex(ctx, milvusclient.NewCreateIndexOption(collectionName, "vector", index.NewHNSWIndex(entity.L2, 16, 32)))
+ if err != nil {
+ log.Fatal("failed to connect to milvus, err: ", err.Error())
+ }
+
+ indexTask.Await(ctx)
+
+ loadTask, err := c.LoadCollection(ctx, milvusclient.NewLoadCollectionOption(collectionName))
+ if err != nil {
+ log.Fatal("faied to load collection, err: ", err.Error())
+ }
+ loadTask.Await(ctx)
+
+ for i := 0; i < 100; i++ {
+ // randomData := make([]int64, 0, nEntities)
+ vectorData := make([][]float32, 0, nEntities)
+ // generate data
+ for i := 0; i < nEntities; i++ {
+ // randomData = append(randomData, rand.Int63n(1000))
+ vec := make([]float32, 0, dim)
+ for j := 0; j < dim; j++ {
+ vec = append(vec, rand.Float32())
+ }
+ vectorData = append(vectorData, vec)
+ }
+
+ _, err = c.Insert(ctx, milvusclient.NewColumnBasedInsertOption(collectionName).WithFloatVectorColumn("vector", dim, vectorData))
+ if err != nil {
+ log.Fatal("failed to insert data")
+ }
+
+ log.Println("start flush collection")
+ flushTask, err := c.Flush(ctx, milvusclient.NewFlushOption(collectionName))
+ if err != nil {
+ log.Fatal("failed to flush", err.Error())
+ }
+ start := time.Now()
+ err = flushTask.Await(ctx)
+ if err != nil {
+ log.Fatal("failed to flush", err.Error())
+ }
+ log.Println("flush done, elapsed", time.Since(start))
+
+ result, err := c.Query(ctx, milvusclient.NewQueryOption(collectionName).
+ WithOutputFields([]string{"count(*)"}).
+ WithConsistencyLevel(entity.ClStrong))
+ if err != nil {
+ log.Fatal("failed to connect to milvus, err: ", err.Error())
+ }
+ for _, rs := range result.Fields {
+ log.Println(rs)
+ }
+ result, err = c.Query(ctx, milvusclient.NewQueryOption(collectionName).
+ WithOutputFields([]string{"count(*)"}).
+ WithFilter("id > 0").
+ WithConsistencyLevel(entity.ClStrong))
+ if err != nil {
+ log.Fatal("failed to connect to milvus, err: ", err.Error())
+ }
+ for _, rs := range result.Fields {
+ log.Println(rs)
+ }
+ }
+
+ // err = c.DropCollection(ctx, milvusclient.NewDropCollectionOption(collectionName))
+ // if err != nil {
+ // log.Fatal("=== Failed to drop collection", err.Error())
+ // }
+}
+
func HelloMilvus() {
ctx := context.Background()
@@ -92,7 +193,7 @@ func HelloMilvus() {
vectorData = append(vectorData, vec)
}
- err = c.Insert(ctx, milvusclient.NewColumnBasedInsertOption(collectionName).WithFloatVectorColumn("vector", dim, vectorData))
+ _, err = c.Insert(ctx, milvusclient.NewColumnBasedInsertOption(collectionName).WithFloatVectorColumn("vector", dim, vectorData))
if err != nil {
log.Fatal("failed to insert data")
}
@@ -107,22 +208,7 @@ func HelloMilvus() {
if err != nil {
log.Fatal("failed to flush", err.Error())
}
- log.Println("flush done, elasped", time.Since(start))
-
- indexTask, err := c.CreateIndex(ctx, milvusclient.NewCreateIndexOption(collectionName, "vector", index.NewHNSWIndex(entity.L2, 16, 100)))
- if err != nil {
- log.Fatal("failed to create index, err: ", err.Error())
- }
- err = indexTask.Await(ctx)
- if err != nil {
- log.Fatal("failed to wait index construction complete")
- }
-
- loadTask, err := c.LoadCollection(ctx, milvusclient.NewLoadCollectionOption(collectionName))
- if err != nil {
- log.Fatal("failed to load collection", err.Error())
- }
- loadTask.Await(ctx)
+ log.Println("flush done, elapsed", time.Since(start))
vec2search := []entity.Vector{
entity.FloatVector(vectorData[len(vectorData)-2]),
diff --git a/client/go.mod b/client/go.mod
index c0f6882c3d768..af0f2721f0df9 100644
--- a/client/go.mod
+++ b/client/go.mod
@@ -1,6 +1,6 @@
module github.com/milvus-io/milvus/client/v2
-go 1.20
+go 1.21
require (
github.com/blang/semver/v4 v4.0.0
@@ -10,6 +10,7 @@ require (
github.com/grpc-ecosystem/go-grpc-middleware v1.3.0
github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20240430035521-259ae1d10016
github.com/milvus-io/milvus/pkg v0.0.2-0.20240317152703-17b4938985f3
+ github.com/quasilyte/go-ruleguard/dsl v0.3.22
github.com/samber/lo v1.27.0
github.com/stretchr/testify v1.8.4
github.com/tidwall/gjson v1.17.1
diff --git a/client/go.sum b/client/go.sum
index 1efeee2111774..44e4615201642 100644
--- a/client/go.sum
+++ b/client/go.sum
@@ -476,6 +476,8 @@ github.com/prometheus/procfs v0.6.0/go.mod h1:cz+aTbrPOrUb4q7XlbU9ygM+/jj0fzG6c1
github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI=
github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY=
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
+github.com/quasilyte/go-ruleguard/dsl v0.3.22 h1:wd8zkOhSNr+I+8Qeciml08ivDt1pSXe60+5DqOpCjPE=
+github.com/quasilyte/go-ruleguard/dsl v0.3.22/go.mod h1:KeCP03KrjuSO0H1kTuZQCWlQPulDV6YMIXmpQss17rU=
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
diff --git a/client/index.go b/client/index.go
index 79dd57ed3e9c6..79320484632e7 100644
--- a/client/index.go
+++ b/client/index.go
@@ -21,12 +21,13 @@ import (
"fmt"
"time"
+ "google.golang.org/grpc"
+
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/index"
"github.com/milvus-io/milvus/pkg/util/merr"
- "google.golang.org/grpc"
)
type CreateIndexTask struct {
diff --git a/client/index_test.go b/client/index_test.go
index ac9f5e40699e5..920457f9a2160 100644
--- a/client/index_test.go
+++ b/client/index_test.go
@@ -22,14 +22,15 @@ import (
"testing"
"time"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/suite"
+ "go.uber.org/atomic"
+
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/client/v2/index"
"github.com/milvus-io/milvus/pkg/util/merr"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/suite"
- "go.uber.org/atomic"
)
type IndexSuite struct {
diff --git a/client/interceptors.go b/client/interceptors.go
index 16396c4aed7f9..6756a74895825 100644
--- a/client/interceptors.go
+++ b/client/interceptors.go
@@ -20,12 +20,12 @@ import (
"context"
"time"
+ grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
"google.golang.org/grpc"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/metadata"
"google.golang.org/grpc/status"
- grpc_retry "github.com/grpc-ecosystem/go-grpc-middleware/retry"
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
)
diff --git a/client/interceptors_test.go b/client/interceptors_test.go
index e3bcb34fcea66..648575dbd42ed 100644
--- a/client/interceptors_test.go
+++ b/client/interceptors_test.go
@@ -28,9 +28,11 @@ import (
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
)
-var mockInvokerError error
-var mockInvokerReply interface{}
-var mockInvokeTimes = 0
+var (
+ mockInvokerError error
+ mockInvokerReply interface{}
+ mockInvokeTimes = 0
+)
var mockInvoker grpc.UnaryInvoker = func(ctx context.Context, method string, req, reply interface{}, cc *grpc.ClientConn, opts ...grpc.CallOption) error {
mockInvokeTimes++
diff --git a/client/maintenance_test.go b/client/maintenance_test.go
index 333146f8ca4c9..0efcd449dfc41 100644
--- a/client/maintenance_test.go
+++ b/client/maintenance_test.go
@@ -22,13 +22,14 @@ import (
"testing"
"time"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/suite"
+ "go.uber.org/atomic"
+
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
"github.com/milvus-io/milvus/pkg/util/merr"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/suite"
- "go.uber.org/atomic"
)
type MaintenanceSuite struct {
diff --git a/client/partition.go b/client/partition.go
index 93036b2300dc8..18483687175b4 100644
--- a/client/partition.go
+++ b/client/partition.go
@@ -19,9 +19,10 @@ package client
import (
"context"
+ "google.golang.org/grpc"
+
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/pkg/util/merr"
- "google.golang.org/grpc"
)
// CreatePartition is the API for creating a partition for a collection.
diff --git a/client/partition_test.go b/client/partition_test.go
index 2c6c4e2ed82c4..7bd7cd74360b0 100644
--- a/client/partition_test.go
+++ b/client/partition_test.go
@@ -21,11 +21,12 @@ import (
"fmt"
"testing"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/suite"
+
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/pkg/util/merr"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/suite"
)
type PartitionSuite struct {
diff --git a/client/read.go b/client/read.go
index 3aeaff769d31b..1907ed8e07fa4 100644
--- a/client/read.go
+++ b/client/read.go
@@ -19,9 +19,9 @@ package client
import (
"context"
+ "github.com/cockroachdb/errors"
"google.golang.org/grpc"
- "github.com/cockroachdb/errors"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
"github.com/milvus-io/milvus/client/v2/column"
diff --git a/client/read_options.go b/client/read_options.go
index a1f563bfc0642..2bdaf78a553eb 100644
--- a/client/read_options.go
+++ b/client/read_options.go
@@ -21,6 +21,7 @@ import (
"strconv"
"github.com/golang/protobuf/proto"
+
"github.com/milvus-io/milvus-proto/go-api/v2/commonpb"
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus/client/v2/entity"
diff --git a/client/read_test.go b/client/read_test.go
index 6606226d1bb76..0e815a0563382 100644
--- a/client/read_test.go
+++ b/client/read_test.go
@@ -6,13 +6,14 @@ import (
"math/rand"
"testing"
+ "github.com/samber/lo"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/suite"
+
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/pkg/util/merr"
- "github.com/samber/lo"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/suite"
)
type ReadSuite struct {
diff --git a/client/row/data.go b/client/row/data.go
new file mode 100644
index 0000000000000..292661ade29be
--- /dev/null
+++ b/client/row/data.go
@@ -0,0 +1,332 @@
+// Licensed to the LF AI & Data foundation 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.
+
+package row
+
+import (
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "strconv"
+
+ "github.com/cockroachdb/errors"
+
+ "github.com/milvus-io/milvus/client/v2/column"
+ "github.com/milvus-io/milvus/client/v2/entity"
+)
+
+const (
+ // MilvusTag struct tag const for milvus row based struct
+ MilvusTag = `milvus`
+
+ // MilvusSkipTagValue struct tag const for skip this field.
+ MilvusSkipTagValue = `-`
+
+ // MilvusTagSep struct tag const for attribute separator
+ MilvusTagSep = `;`
+
+ // MilvusTagName struct tag const for field name
+ MilvusTagName = `NAME`
+
+ // VectorDimTag struct tag const for vector dimension
+ VectorDimTag = `DIM`
+
+ // VectorTypeTag struct tag const for binary vector type
+ VectorTypeTag = `VECTOR_TYPE`
+
+ // MilvusPrimaryKey struct tag const for primary key indicator
+ MilvusPrimaryKey = `PRIMARY_KEY`
+
+ // MilvusAutoID struct tag const for auto id indicator
+ MilvusAutoID = `AUTO_ID`
+
+ // DimMax dimension max value
+ DimMax = 65535
+)
+
+func AnyToColumns(rows []interface{}, schemas ...*entity.Schema) ([]column.Column, error) {
+ rowsLen := len(rows)
+ if rowsLen == 0 {
+ return []column.Column{}, errors.New("0 length column")
+ }
+
+ var sch *entity.Schema
+ var err error
+ // if schema not provided, try to parse from row
+ if len(schemas) == 0 {
+ sch, err = ParseSchema(rows[0])
+ if err != nil {
+ return []column.Column{}, err
+ }
+ } else {
+ // use first schema provided
+ sch = schemas[0]
+ }
+
+ isDynamic := sch.EnableDynamicField
+ var dynamicCol *column.ColumnJSONBytes
+
+ nameColumns := make(map[string]column.Column)
+ for _, field := range sch.Fields {
+ // skip auto id pk field
+ if field.PrimaryKey && field.AutoID {
+ continue
+ }
+ switch field.DataType {
+ case entity.FieldTypeBool:
+ data := make([]bool, 0, rowsLen)
+ col := column.NewColumnBool(field.Name, data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeInt8:
+ data := make([]int8, 0, rowsLen)
+ col := column.NewColumnInt8(field.Name, data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeInt16:
+ data := make([]int16, 0, rowsLen)
+ col := column.NewColumnInt16(field.Name, data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeInt32:
+ data := make([]int32, 0, rowsLen)
+ col := column.NewColumnInt32(field.Name, data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeInt64:
+ data := make([]int64, 0, rowsLen)
+ col := column.NewColumnInt64(field.Name, data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeFloat:
+ data := make([]float32, 0, rowsLen)
+ col := column.NewColumnFloat(field.Name, data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeDouble:
+ data := make([]float64, 0, rowsLen)
+ col := column.NewColumnDouble(field.Name, data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeString, entity.FieldTypeVarChar:
+ data := make([]string, 0, rowsLen)
+ col := column.NewColumnString(field.Name, data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeJSON:
+ data := make([][]byte, 0, rowsLen)
+ col := column.NewColumnJSONBytes(field.Name, data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeArray:
+ col := NewArrayColumn(field)
+ if col == nil {
+ return nil, errors.Newf("unsupported element type %s for Array", field.ElementType.String())
+ }
+ nameColumns[field.Name] = col
+ case entity.FieldTypeFloatVector:
+ data := make([][]float32, 0, rowsLen)
+ dimStr, has := field.TypeParams[entity.TypeParamDim]
+ if !has {
+ return []column.Column{}, errors.New("vector field with no dim")
+ }
+ dim, err := strconv.ParseInt(dimStr, 10, 64)
+ if err != nil {
+ return []column.Column{}, fmt.Errorf("vector field with bad format dim: %s", err.Error())
+ }
+ col := column.NewColumnFloatVector(field.Name, int(dim), data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeBinaryVector:
+ data := make([][]byte, 0, rowsLen)
+ dim, err := field.GetDim()
+ if err != nil {
+ return []column.Column{}, err
+ }
+ col := column.NewColumnBinaryVector(field.Name, int(dim), data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeFloat16Vector:
+ data := make([][]byte, 0, rowsLen)
+ dim, err := field.GetDim()
+ if err != nil {
+ return []column.Column{}, err
+ }
+ col := column.NewColumnFloat16Vector(field.Name, int(dim), data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeBFloat16Vector:
+ data := make([][]byte, 0, rowsLen)
+ dim, err := field.GetDim()
+ if err != nil {
+ return []column.Column{}, err
+ }
+ col := column.NewColumnBFloat16Vector(field.Name, int(dim), data)
+ nameColumns[field.Name] = col
+ case entity.FieldTypeSparseVector:
+ data := make([]entity.SparseEmbedding, 0, rowsLen)
+ col := column.NewColumnSparseVectors(field.Name, data)
+ nameColumns[field.Name] = col
+ }
+ }
+
+ if isDynamic {
+ dynamicCol = column.NewColumnJSONBytes("", make([][]byte, 0, rowsLen)).WithIsDynamic(true)
+ }
+
+ for _, row := range rows {
+ // collection schema name need not to be same, since receiver could has other names
+ v := reflect.ValueOf(row)
+ set, err := reflectValueCandi(v)
+ if err != nil {
+ return nil, err
+ }
+
+ for idx, field := range sch.Fields {
+ // skip dynamic field if visible
+ if isDynamic && field.IsDynamic {
+ continue
+ }
+ // skip auto id pk field
+ if field.PrimaryKey && field.AutoID {
+ // remove pk field from candidates set, avoid adding it into dynamic column
+ delete(set, field.Name)
+ continue
+ }
+ column, ok := nameColumns[field.Name]
+ if !ok {
+ return nil, fmt.Errorf("expected unhandled field %s", field.Name)
+ }
+
+ candi, ok := set[field.Name]
+ if !ok {
+ return nil, fmt.Errorf("row %d does not has field %s", idx, field.Name)
+ }
+ err := column.AppendValue(candi.v.Interface())
+ if err != nil {
+ return nil, err
+ }
+ delete(set, field.Name)
+ }
+
+ if isDynamic {
+ m := make(map[string]interface{})
+ for name, candi := range set {
+ m[name] = candi.v.Interface()
+ }
+ bs, err := json.Marshal(m)
+ if err != nil {
+ return nil, fmt.Errorf("failed to marshal dynamic field %w", err)
+ }
+ err = dynamicCol.AppendValue(bs)
+ if err != nil {
+ return nil, fmt.Errorf("failed to append value to dynamic field %w", err)
+ }
+ }
+ }
+ columns := make([]column.Column, 0, len(nameColumns))
+ for _, column := range nameColumns {
+ columns = append(columns, column)
+ }
+ if isDynamic {
+ columns = append(columns, dynamicCol)
+ }
+ return columns, nil
+}
+
+func NewArrayColumn(f *entity.Field) column.Column {
+ switch f.ElementType {
+ case entity.FieldTypeBool:
+ return column.NewColumnBoolArray(f.Name, nil)
+
+ case entity.FieldTypeInt8:
+ return column.NewColumnInt8Array(f.Name, nil)
+
+ case entity.FieldTypeInt16:
+ return column.NewColumnInt16Array(f.Name, nil)
+
+ case entity.FieldTypeInt32:
+ return column.NewColumnInt32Array(f.Name, nil)
+
+ case entity.FieldTypeInt64:
+ return column.NewColumnInt64Array(f.Name, nil)
+
+ case entity.FieldTypeFloat:
+ return column.NewColumnFloatArray(f.Name, nil)
+
+ case entity.FieldTypeDouble:
+ return column.NewColumnDoubleArray(f.Name, nil)
+
+ case entity.FieldTypeVarChar:
+ return column.NewColumnVarCharArray(f.Name, nil)
+
+ default:
+ return nil
+ }
+}
+
+type fieldCandi struct {
+ name string
+ v reflect.Value
+ options map[string]string
+}
+
+func reflectValueCandi(v reflect.Value) (map[string]fieldCandi, error) {
+ if v.Kind() == reflect.Ptr {
+ v = v.Elem()
+ }
+
+ result := make(map[string]fieldCandi)
+ switch v.Kind() {
+ case reflect.Map: // map[string]any
+ iter := v.MapRange()
+ for iter.Next() {
+ key := iter.Key().String()
+ result[key] = fieldCandi{
+ name: key,
+ v: iter.Value(),
+ }
+ }
+ return result, nil
+ case reflect.Struct:
+ for i := 0; i < v.NumField(); i++ {
+ ft := v.Type().Field(i)
+ name := ft.Name
+ tag, ok := ft.Tag.Lookup(MilvusTag)
+
+ settings := make(map[string]string)
+ if ok {
+ if tag == MilvusSkipTagValue {
+ continue
+ }
+ settings = ParseTagSetting(tag, MilvusTagSep)
+ fn, has := settings[MilvusTagName]
+ if has {
+ // overwrite column to tag name
+ name = fn
+ }
+ }
+ _, ok = result[name]
+ // duplicated
+ if ok {
+ return nil, fmt.Errorf("column has duplicated name: %s when parsing field: %s", name, ft.Name)
+ }
+
+ v := v.Field(i)
+ if v.Kind() == reflect.Array {
+ v = v.Slice(0, v.Len())
+ }
+
+ result[name] = fieldCandi{
+ name: name,
+ v: v,
+ options: settings,
+ }
+ }
+
+ return result, nil
+ default:
+ return nil, fmt.Errorf("unsupport row type: %s", v.Kind().String())
+ }
+}
diff --git a/client/row/data_test.go b/client/row/data_test.go
new file mode 100644
index 0000000000000..9e8b7fb216fbc
--- /dev/null
+++ b/client/row/data_test.go
@@ -0,0 +1,174 @@
+package row
+
+import (
+ "reflect"
+ "testing"
+
+ "github.com/stretchr/testify/suite"
+
+ "github.com/milvus-io/milvus/client/v2/entity"
+)
+
+type ValidStruct struct {
+ ID int64 `milvus:"primary_key"`
+ Attr1 int8
+ Attr2 int16
+ Attr3 int32
+ Attr4 float32
+ Attr5 float64
+ Attr6 string
+ Attr7 bool
+ Vector []float32 `milvus:"dim:16"`
+ Vector2 []byte `milvus:"dim:32"`
+}
+
+type ValidStruct2 struct {
+ ID int64 `milvus:"primary_key"`
+ Vector [16]float32
+ Vector2 [4]byte
+ Ignored bool `milvus:"-"`
+}
+
+type ValidStructWithNamedTag struct {
+ ID int64 `milvus:"primary_key;name:id"`
+ Vector [16]float32 `milvus:"name:vector"`
+}
+
+type RowsSuite struct {
+ suite.Suite
+}
+
+func (s *RowsSuite) TestRowsToColumns() {
+ s.Run("valid_cases", func() {
+ columns, err := AnyToColumns([]any{&ValidStruct{}})
+ s.Nil(err)
+ s.Equal(10, len(columns))
+
+ columns, err = AnyToColumns([]any{&ValidStruct2{}})
+ s.Nil(err)
+ s.Equal(3, len(columns))
+ })
+
+ s.Run("auto_id_pk", func() {
+ type AutoPK struct {
+ ID int64 `milvus:"primary_key;auto_id"`
+ Vector []float32 `milvus:"dim:32"`
+ }
+ columns, err := AnyToColumns([]any{&AutoPK{}})
+ s.Nil(err)
+ s.Require().Equal(1, len(columns))
+ s.Equal("Vector", columns[0].Name())
+ })
+
+ s.Run("fp16", func() {
+ type BF16Struct struct {
+ ID int64 `milvus:"primary_key;auto_id"`
+ Vector []byte `milvus:"dim:16;vector_type:bf16"`
+ }
+ columns, err := AnyToColumns([]any{&BF16Struct{}})
+ s.Nil(err)
+ s.Require().Equal(1, len(columns))
+ s.Equal("Vector", columns[0].Name())
+ s.Equal(entity.FieldTypeBFloat16Vector, columns[0].Type())
+ })
+
+ s.Run("fp16", func() {
+ type FP16Struct struct {
+ ID int64 `milvus:"primary_key;auto_id"`
+ Vector []byte `milvus:"dim:16;vector_type:fp16"`
+ }
+ columns, err := AnyToColumns([]any{&FP16Struct{}})
+ s.Nil(err)
+ s.Require().Equal(1, len(columns))
+ s.Equal("Vector", columns[0].Name())
+ s.Equal(entity.FieldTypeFloat16Vector, columns[0].Type())
+ })
+
+ s.Run("invalid_cases", func() {
+ // empty input
+ _, err := AnyToColumns([]any{})
+ s.NotNil(err)
+
+ // incompatible rows
+ _, err = AnyToColumns([]any{&ValidStruct{}, &ValidStruct2{}})
+ s.NotNil(err)
+
+ // schema & row not compatible
+ _, err = AnyToColumns([]any{&ValidStruct{}}, &entity.Schema{
+ Fields: []*entity.Field{
+ {
+ Name: "int64",
+ DataType: entity.FieldTypeInt64,
+ },
+ },
+ })
+ s.NotNil(err)
+ })
+}
+
+func (s *RowsSuite) TestDynamicSchema() {
+ s.Run("all_fallback_dynamic", func() {
+ columns, err := AnyToColumns([]any{&ValidStruct{}},
+ entity.NewSchema().WithDynamicFieldEnabled(true),
+ )
+ s.NoError(err)
+ s.Equal(1, len(columns))
+ })
+
+ s.Run("dynamic_not_found", func() {
+ _, err := AnyToColumns([]any{&ValidStruct{}},
+ entity.NewSchema().WithField(
+ entity.NewField().WithName("ID").WithDataType(entity.FieldTypeInt64).WithIsPrimaryKey(true),
+ ).WithDynamicFieldEnabled(true),
+ )
+ s.NoError(err)
+ })
+}
+
+func (s *RowsSuite) TestReflectValueCandi() {
+ cases := []struct {
+ tag string
+ v reflect.Value
+ expect map[string]fieldCandi
+ expectErr bool
+ }{
+ {
+ tag: "MapRow",
+ v: reflect.ValueOf(map[string]interface{}{
+ "A": "abd", "B": int64(8),
+ }),
+ expect: map[string]fieldCandi{
+ "A": {
+ name: "A",
+ v: reflect.ValueOf("abd"),
+ },
+ "B": {
+ name: "B",
+ v: reflect.ValueOf(int64(8)),
+ },
+ },
+ expectErr: false,
+ },
+ }
+
+ for _, c := range cases {
+ s.Run(c.tag, func() {
+ r, err := reflectValueCandi(c.v)
+ if c.expectErr {
+ s.Error(err)
+ return
+ }
+ s.NoError(err)
+ s.Equal(len(c.expect), len(r))
+ for k, v := range c.expect {
+ rv, has := r[k]
+ s.Require().True(has)
+ s.Equal(v.name, rv.name)
+ }
+ })
+ }
+}
+
+func TestRows(t *testing.T) {
+ suite.Run(t, new(RowsSuite))
+}
diff --git a/client/row/schema.go b/client/row/schema.go
new file mode 100644
index 0000000000000..6022275653f17
--- /dev/null
+++ b/client/row/schema.go
@@ -0,0 +1,185 @@
+// Licensed to the LF AI & Data foundation 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.
+
+package row
+
+import (
+ "fmt"
+ "go/ast"
+ "reflect"
+ "strconv"
+ "strings"
+
+ "github.com/cockroachdb/errors"
+
+ "github.com/milvus-io/milvus/client/v2/entity"
+)
+
+// ParseSchema parses schema from interface{}.
+func ParseSchema(r interface{}) (*entity.Schema, error) {
+ sch := &entity.Schema{}
+ t := reflect.TypeOf(r)
+ if t.Kind() == reflect.Array || t.Kind() == reflect.Slice || t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+
+ // MapRow is not supported for schema definition
+ // TODO add PrimaryKey() interface later
+ if t.Kind() == reflect.Map {
+ return nil, fmt.Errorf("map row is not supported for schema definition")
+ }
+
+ if t.Kind() != reflect.Struct {
+ return nil, fmt.Errorf("unsupported data type: %+v", r)
+ }
+
+ // Collection method not overwrited, try use Row type name
+ if sch.CollectionName == "" {
+ sch.CollectionName = t.Name()
+ if sch.CollectionName == "" {
+ return nil, errors.New("collection name not provided")
+ }
+ }
+ sch.Fields = make([]*entity.Field, 0, t.NumField())
+ for i := 0; i < t.NumField(); i++ {
+ f := t.Field(i)
+ // ignore anonymous field for now
+ if f.Anonymous || !ast.IsExported(f.Name) {
+ continue
+ }
+
+ field := &entity.Field{
+ Name: f.Name,
+ }
+ ft := f.Type
+ if f.Type.Kind() == reflect.Ptr {
+ ft = ft.Elem()
+ }
+ fv := reflect.New(ft)
+ tag := f.Tag.Get(MilvusTag)
+ if tag == MilvusSkipTagValue {
+ continue
+ }
+ tagSettings := ParseTagSetting(tag, MilvusTagSep)
+ if _, has := tagSettings[MilvusPrimaryKey]; has {
+ field.PrimaryKey = true
+ }
+ if _, has := tagSettings[MilvusAutoID]; has {
+ field.AutoID = true
+ }
+ if name, has := tagSettings[MilvusTagName]; has {
+ field.Name = name
+ }
+ switch reflect.Indirect(fv).Kind() {
+ case reflect.Bool:
+ field.DataType = entity.FieldTypeBool
+ case reflect.Int8:
+ field.DataType = entity.FieldTypeInt8
+ case reflect.Int16:
+ field.DataType = entity.FieldTypeInt16
+ case reflect.Int32:
+ field.DataType = entity.FieldTypeInt32
+ case reflect.Int64:
+ field.DataType = entity.FieldTypeInt64
+ case reflect.Float32:
+ field.DataType = entity.FieldTypeFloat
+ case reflect.Float64:
+ field.DataType = entity.FieldTypeDouble
+ case reflect.String:
+ field.DataType = entity.FieldTypeString
+ case reflect.Array:
+ arrayLen := ft.Len()
+ elemType := ft.Elem()
+ switch elemType.Kind() {
+ case reflect.Uint8:
+ field.WithDataType(entity.FieldTypeBinaryVector)
+ field.WithDim(int64(arrayLen) * 8)
+ case reflect.Float32:
+ field.WithDataType(entity.FieldTypeFloatVector)
+ field.WithDim(int64(arrayLen))
+ default:
+ return nil, fmt.Errorf("field %s is array of %v, which is not supported", f.Name, elemType)
+ }
+ case reflect.Slice:
+ dimStr, has := tagSettings[VectorDimTag]
+ if !has {
+ return nil, fmt.Errorf("field %s is slice but dim not provided", f.Name)
+ }
+ dim, err := strconv.ParseInt(dimStr, 10, 64)
+ if err != nil {
+ return nil, fmt.Errorf("dim value %s is not valid", dimStr)
+ }
+ if dim < 1 || dim > DimMax {
+ return nil, fmt.Errorf("dim value %d is out of range", dim)
+ }
+ field.WithDim(dim)
+
+ elemType := ft.Elem()
+ switch elemType.Kind() {
+ case reflect.Uint8: // []byte, could be BinaryVector, fp16, bf 6
+ switch tagSettings[VectorTypeTag] {
+ case "fp16":
+ field.DataType = entity.FieldTypeFloat16Vector
+ case "bf16":
+ field.DataType = entity.FieldTypeBFloat16Vector
+ default:
+ field.DataType = entity.FieldTypeBinaryVector
+ }
+ case reflect.Float32:
+ field.DataType = entity.FieldTypeFloatVector
+ default:
+ return nil, fmt.Errorf("field %s is slice of %v, which is not supported", f.Name, elemType)
+ }
+ default:
+ return nil, fmt.Errorf("field %s is %v, which is not supported", field.Name, ft)
+ }
+ sch.Fields = append(sch.Fields, field)
+ }
+
+ return sch, nil
+}
+
+// ParseTagSetting parses struct tag into map settings
+func ParseTagSetting(str string, sep string) map[string]string {
+ settings := map[string]string{}
+ names := strings.Split(str, sep)
+
+ for i := 0; i < len(names); i++ {
+ j := i
+ if len(names[j]) > 0 {
+ for {
+ if names[j][len(names[j])-1] == '\\' {
+ i++
+ names[j] = names[j][0:len(names[j])-1] + sep + names[i]
+ names[i] = ""
+ } else {
+ break
+ }
+ }
+ }
+
+ values := strings.Split(names[j], ":")
+ k := strings.TrimSpace(strings.ToUpper(values[0]))
+
+ if len(values) >= 2 {
+ settings[k] = strings.Join(values[1:], ":")
+ } else if k != "" {
+ settings[k] = k
+ }
+ }
+
+ return settings
+}
diff --git a/client/row/schema_test.go b/client/row/schema_test.go
new file mode 100644
index 0000000000000..fbfdc19f27058
--- /dev/null
+++ b/client/row/schema_test.go
@@ -0,0 +1,213 @@
+// Licensed to the LF AI & Data foundation 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.
+
+package row
+
+import (
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "github.com/milvus-io/milvus/client/v2/entity"
+)
+
+// ArrayRow test case type
+type ArrayRow [16]float32
+
+func (ar *ArrayRow) Collection() string { return "" }
+func (ar *ArrayRow) Partition() string { return "" }
+func (ar *ArrayRow) Description() string { return "" }
+
+type Uint8Struct struct {
+ Attr uint8
+}
+
+type StringArrayStruct struct {
+ Vector [8]string
+}
+
+type StringSliceStruct struct {
+ Vector []string `milvus:"dim:8"`
+}
+
+type SliceNoDimStruct struct {
+ Vector []float32 `milvus:""`
+}
+
+type SliceBadDimStruct struct {
+ Vector []float32 `milvus:"dim:str"`
+}
+
+type SliceBadDimStruct2 struct {
+ Vector []float32 `milvus:"dim:0"`
+}
+
+func TestParseSchema(t *testing.T) {
+ t.Run("invalid cases", func(t *testing.T) {
+ // anonymous struct with default collection name ("") will cause error
+ anonymusStruct := struct{}{}
+ sch, err := ParseSchema(anonymusStruct)
+ assert.Nil(t, sch)
+ assert.NotNil(t, err)
+
+ // non struct
+ arrayRow := ArrayRow([16]float32{})
+ sch, err = ParseSchema(&arrayRow)
+ assert.Nil(t, sch)
+ assert.NotNil(t, err)
+
+ // uint8 not supported
+ sch, err = ParseSchema(&Uint8Struct{})
+ assert.Nil(t, sch)
+ assert.NotNil(t, err)
+
+ // string array not supported
+ sch, err = ParseSchema(&StringArrayStruct{})
+ assert.Nil(t, sch)
+ assert.NotNil(t, err)
+
+ // string slice not supported
+ sch, err = ParseSchema(&StringSliceStruct{})
+ assert.Nil(t, sch)
+ assert.NotNil(t, err)
+
+ // slice vector with no dim
+ sch, err = ParseSchema(&SliceNoDimStruct{})
+ assert.Nil(t, sch)
+ assert.NotNil(t, err)
+
+ // slice vector with bad format dim
+ sch, err = ParseSchema(&SliceBadDimStruct{})
+ assert.Nil(t, sch)
+ assert.NotNil(t, err)
+
+ // slice vector with bad format dim 2
+ sch, err = ParseSchema(&SliceBadDimStruct2{})
+ assert.Nil(t, sch)
+ assert.NotNil(t, err)
+ })
+
+ t.Run("valid cases", func(t *testing.T) {
+ getVectorField := func(schema *entity.Schema) *entity.Field {
+ for _, field := range schema.Fields {
+ if field.DataType == entity.FieldTypeFloatVector ||
+ field.DataType == entity.FieldTypeBinaryVector ||
+ field.DataType == entity.FieldTypeBFloat16Vector ||
+ field.DataType == entity.FieldTypeFloat16Vector {
+ return field
+ }
+ }
+ return nil
+ }
+
+ type ValidStruct struct {
+ ID int64 `milvus:"primary_key"`
+ Attr1 int8
+ Attr2 int16
+ Attr3 int32
+ Attr4 float32
+ Attr5 float64
+ Attr6 string
+ Vector []float32 `milvus:"dim:128"`
+ }
+ vs := &ValidStruct{}
+ sch, err := ParseSchema(vs)
+ assert.Nil(t, err)
+ assert.NotNil(t, sch)
+ assert.Equal(t, "ValidStruct", sch.CollectionName)
+
+ type ValidFp16Struct struct {
+ ID int64 `milvus:"primary_key"`
+ Attr1 int8
+ Attr2 int16
+ Attr3 int32
+ Attr4 float32
+ Attr5 float64
+ Attr6 string
+ Vector []byte `milvus:"dim:128;vector_type:fp16"`
+ }
+ fp16Vs := &ValidFp16Struct{}
+ sch, err = ParseSchema(fp16Vs)
+ assert.Nil(t, err)
+ assert.NotNil(t, sch)
+ assert.Equal(t, "ValidFp16Struct", sch.CollectionName)
+ vectorField := getVectorField(sch)
+ assert.Equal(t, entity.FieldTypeFloat16Vector, vectorField.DataType)
+
+ type ValidBf16Struct struct {
+ ID int64 `milvus:"primary_key"`
+ Attr1 int8
+ Attr2 int16
+ Attr3 int32
+ Attr4 float32
+ Attr5 float64
+ Attr6 string
+ Vector []byte `milvus:"dim:128;vector_type:bf16"`
+ }
+ bf16Vs := &ValidBf16Struct{}
+ sch, err = ParseSchema(bf16Vs)
+ assert.Nil(t, err)
+ assert.NotNil(t, sch)
+ assert.Equal(t, "ValidBf16Struct", sch.CollectionName)
+ vectorField = getVectorField(sch)
+ assert.Equal(t, entity.FieldTypeBFloat16Vector, vectorField.DataType)
+
+ type ValidByteStruct struct {
+ ID int64 `milvus:"primary_key"`
+ Vector []byte `milvus:"dim:128"`
+ }
+ vs2 := &ValidByteStruct{}
+ sch, err = ParseSchema(vs2)
+ assert.Nil(t, err)
+ assert.NotNil(t, sch)
+
+ type ValidArrayStruct struct {
+ ID int64 `milvus:"primary_key"`
+ Vector [64]float32
+ }
+ vs3 := &ValidArrayStruct{}
+ sch, err = ParseSchema(vs3)
+ assert.Nil(t, err)
+ assert.NotNil(t, sch)
+
+ type ValidArrayStructByte struct {
+ ID int64 `milvus:"primary_key;auto_id"`
+ Data *string `milvus:"extra:test\\;false"`
+ Vector [64]byte
+ }
+ vs4 := &ValidArrayStructByte{}
+ sch, err = ParseSchema(vs4)
+ assert.Nil(t, err)
+ assert.NotNil(t, sch)
+
+ vs5 := &ValidStructWithNamedTag{}
+ sch, err = ParseSchema(vs5)
+ assert.Nil(t, err)
+ assert.NotNil(t, sch)
+ i64f, vecf := false, false
+ for _, field := range sch.Fields {
+ if field.Name == "id" {
+ i64f = true
+ }
+ if field.Name == "vector" {
+ vecf = true
+ }
+ }
+
+ assert.True(t, i64f)
+ assert.True(t, vecf)
+ })
+}
diff --git a/client/ruleguard/rules.go b/client/ruleguard/rules.go
new file mode 100644
index 0000000000000..5bc3422c9b450
--- /dev/null
+++ b/client/ruleguard/rules.go
@@ -0,0 +1,409 @@
+// Licensed to the LF AI & Data foundation 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.
+
+package gorules
+
+import (
+ "github.com/quasilyte/go-ruleguard/dsl"
+)
+
+// This is a collection of rules for ruleguard: https://github.com/quasilyte/go-ruleguard
+
+// Remove extra conversions: mdempsky/unconvert
+func unconvert(m dsl.Matcher) {
+ m.Match("int($x)").Where(m["x"].Type.Is("int") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x")
+
+ m.Match("float32($x)").Where(m["x"].Type.Is("float32") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x")
+ m.Match("float64($x)").Where(m["x"].Type.Is("float64") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x")
+
+ // m.Match("byte($x)").Where(m["x"].Type.Is("byte")).Report("unnecessary conversion").Suggest("$x")
+ // m.Match("rune($x)").Where(m["x"].Type.Is("rune")).Report("unnecessary conversion").Suggest("$x")
+ m.Match("bool($x)").Where(m["x"].Type.Is("bool") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x")
+
+ m.Match("int8($x)").Where(m["x"].Type.Is("int8") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x")
+ m.Match("int16($x)").Where(m["x"].Type.Is("int16") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x")
+ m.Match("int32($x)").Where(m["x"].Type.Is("int32") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x")
+ m.Match("int64($x)").Where(m["x"].Type.Is("int64") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x")
+
+ m.Match("uint8($x)").Where(m["x"].Type.Is("uint8") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x")
+ m.Match("uint16($x)").Where(m["x"].Type.Is("uint16") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x")
+ m.Match("uint32($x)").Where(m["x"].Type.Is("uint32") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x")
+ m.Match("uint64($x)").Where(m["x"].Type.Is("uint64") && !m["x"].Const).Report("unnecessary conversion").Suggest("$x")
+
+ m.Match("time.Duration($x)").Where(m["x"].Type.Is("time.Duration") && !m["x"].Text.Matches("^[0-9]*$")).Report("unnecessary conversion").Suggest("$x")
+}
+
+// Don't use == or != with time.Time
+// https://github.com/dominikh/go-tools/issues/47 : Wontfix
+func timeeq(m dsl.Matcher) {
+ m.Match("$t0 == $t1").Where(m["t0"].Type.Is("time.Time")).Report("using == with time.Time")
+ m.Match("$t0 != $t1").Where(m["t0"].Type.Is("time.Time")).Report("using != with time.Time")
+ m.Match(`map[$k]$v`).Where(m["k"].Type.Is("time.Time")).Report("map with time.Time keys are easy to misuse")
+}
+
+// err but no an error
+func errnoterror(m dsl.Matcher) {
+ // Would be easier to check for all err identifiers instead, but then how do we get the type from m[] ?
+
+ m.Match(
+ "if $*_, err := $x; $err != nil { $*_ } else if $_ { $*_ }",
+ "if $*_, err := $x; $err != nil { $*_ } else { $*_ }",
+ "if $*_, err := $x; $err != nil { $*_ }",
+
+ "if $*_, err = $x; $err != nil { $*_ } else if $_ { $*_ }",
+ "if $*_, err = $x; $err != nil { $*_ } else { $*_ }",
+ "if $*_, err = $x; $err != nil { $*_ }",
+
+ "$*_, err := $x; if $err != nil { $*_ } else if $_ { $*_ }",
+ "$*_, err := $x; if $err != nil { $*_ } else { $*_ }",
+ "$*_, err := $x; if $err != nil { $*_ }",
+
+ "$*_, err = $x; if $err != nil { $*_ } else if $_ { $*_ }",
+ "$*_, err = $x; if $err != nil { $*_ } else { $*_ }",
+ "$*_, err = $x; if $err != nil { $*_ }",
+ ).
+ Where(m["err"].Text == "err" && !m["err"].Type.Is("error") && m["x"].Text != "recover()").
+ Report("err variable not error type")
+}
+
+// Identical if and else bodies
+func ifbodythenbody(m dsl.Matcher) {
+ m.Match("if $*_ { $body } else { $body }").
+ Report("identical if and else bodies")
+
+ // Lots of false positives.
+ // m.Match("if $*_ { $body } else if $*_ { $body }").
+ // Report("identical if and else bodies")
+}
+
+// Odd inequality: A - B < 0 instead of !=
+// Too many false positives.
+/*
+func subtractnoteq(m dsl.Matcher) {
+ m.Match("$a - $b < 0").Report("consider $a != $b")
+ m.Match("$a - $b > 0").Report("consider $a != $b")
+ m.Match("0 < $a - $b").Report("consider $a != $b")
+ m.Match("0 > $a - $b").Report("consider $a != $b")
+}
+*/
+
+// Self-assignment
+func selfassign(m dsl.Matcher) {
+ m.Match("$x = $x").Report("useless self-assignment")
+}
+
+// Odd nested ifs
+func oddnestedif(m dsl.Matcher) {
+ m.Match("if $x { if $x { $*_ }; $*_ }",
+ "if $x == $y { if $x != $y {$*_ }; $*_ }",
+ "if $x != $y { if $x == $y {$*_ }; $*_ }",
+ "if $x { if !$x { $*_ }; $*_ }",
+ "if !$x { if $x { $*_ }; $*_ }").
+ Report("odd nested ifs")
+
+ m.Match("for $x { if $x { $*_ }; $*_ }",
+ "for $x == $y { if $x != $y {$*_ }; $*_ }",
+ "for $x != $y { if $x == $y {$*_ }; $*_ }",
+ "for $x { if !$x { $*_ }; $*_ }",
+ "for !$x { if $x { $*_ }; $*_ }").
+ Report("odd nested for/ifs")
+}
+
+// odd bitwise expressions
+func oddbitwise(m dsl.Matcher) {
+ m.Match("$x | $x",
+ "$x | ^$x",
+ "^$x | $x").
+ Report("odd bitwise OR")
+
+ m.Match("$x & $x",
+ "$x & ^$x",
+ "^$x & $x").
+ Report("odd bitwise AND")
+
+ m.Match("$x &^ $x").
+ Report("odd bitwise AND-NOT")
+}
+
+// odd sequence of if tests with return
+func ifreturn(m dsl.Matcher) {
+ m.Match("if $x { return $*_ }; if $x {$*_ }").Report("odd sequence of if test")
+ m.Match("if $x { return $*_ }; if !$x {$*_ }").Report("odd sequence of if test")
+ m.Match("if !$x { return $*_ }; if $x {$*_ }").Report("odd sequence of if test")
+ m.Match("if $x == $y { return $*_ }; if $x != $y {$*_ }").Report("odd sequence of if test")
+ m.Match("if $x != $y { return $*_ }; if $x == $y {$*_ }").Report("odd sequence of if test")
+}
+
+func oddifsequence(m dsl.Matcher) {
+ /*
+ m.Match("if $x { $*_ }; if $x {$*_ }").Report("odd sequence of if test")
+
+ m.Match("if $x == $y { $*_ }; if $y == $x {$*_ }").Report("odd sequence of if tests")
+ m.Match("if $x != $y { $*_ }; if $y != $x {$*_ }").Report("odd sequence of if tests")
+
+ m.Match("if $x < $y { $*_ }; if $y > $x {$*_ }").Report("odd sequence of if tests")
+ m.Match("if $x <= $y { $*_ }; if $y >= $x {$*_ }").Report("odd sequence of if tests")
+
+ m.Match("if $x > $y { $*_ }; if $y < $x {$*_ }").Report("odd sequence of if tests")
+ m.Match("if $x >= $y { $*_ }; if $y <= $x {$*_ }").Report("odd sequence of if tests")
+ */
+}
+
+// odd sequence of nested if tests
+func nestedifsequence(m dsl.Matcher) {
+ /*
+ m.Match("if $x < $y { if $x >= $y {$*_ }; $*_ }").Report("odd sequence of nested if tests")
+ m.Match("if $x <= $y { if $x > $y {$*_ }; $*_ }").Report("odd sequence of nested if tests")
+ m.Match("if $x > $y { if $x <= $y {$*_ }; $*_ }").Report("odd sequence of nested if tests")
+ m.Match("if $x >= $y { if $x < $y {$*_ }; $*_ }").Report("odd sequence of nested if tests")
+ */
+}
+
+// odd sequence of assignments
+func identicalassignments(m dsl.Matcher) {
+ m.Match("$x = $y; $y = $x").Report("odd sequence of assignments")
+}
+
+func oddcompoundop(m dsl.Matcher) {
+ m.Match("$x += $x + $_",
+ "$x += $x - $_").
+ Report("odd += expression")
+
+ m.Match("$x -= $x + $_",
+ "$x -= $x - $_").
+ Report("odd -= expression")
+}
+
+func constswitch(m dsl.Matcher) {
+ m.Match("switch $x { $*_ }", "switch $*_; $x { $*_ }").
+ Where(m["x"].Const && !m["x"].Text.Matches(`^runtime\.`)).
+ Report("constant switch")
+}
+
+func oddcomparisons(m dsl.Matcher) {
+ m.Match(
+ "$x - $y == 0",
+ "$x - $y != 0",
+ "$x - $y < 0",
+ "$x - $y <= 0",
+ "$x - $y > 0",
+ "$x - $y >= 0",
+ "$x ^ $y == 0",
+ "$x ^ $y != 0",
+ ).Report("odd comparison")
+}
+
+func oddmathbits(m dsl.Matcher) {
+ m.Match(
+ "64 - bits.LeadingZeros64($x)",
+ "32 - bits.LeadingZeros32($x)",
+ "16 - bits.LeadingZeros16($x)",
+ "8 - bits.LeadingZeros8($x)",
+ ).Report("odd math/bits expression: use bits.Len*() instead?")
+}
+
+// func floateq(m dsl.Matcher) {
+// m.Match(
+// "$x == $y",
+// "$x != $y",
+// ).
+// Where(m["x"].Type.Is("float32") && !m["x"].Const && !m["y"].Text.Matches("0(.0+)?") && !m.File().Name.Matches("floating_comparision.go")).
+// Report("floating point tested for equality")
+
+// m.Match(
+// "$x == $y",
+// "$x != $y",
+// ).
+// Where(m["x"].Type.Is("float64") && !m["x"].Const && !m["y"].Text.Matches("0(.0+)?") && !m.File().Name.Matches("floating_comparision.go")).
+// Report("floating point tested for equality")
+
+// m.Match("switch $x { $*_ }", "switch $*_; $x { $*_ }").
+// Where(m["x"].Type.Is("float32")).
+// Report("floating point as switch expression")
+
+// m.Match("switch $x { $*_ }", "switch $*_; $x { $*_ }").
+// Where(m["x"].Type.Is("float64")).
+// Report("floating point as switch expression")
+
+// }
+
+func badexponent(m dsl.Matcher) {
+ m.Match(
+ "2 ^ $x",
+ "10 ^ $x",
+ ).
+ Report("caret (^) is not exponentiation")
+}
+
+func floatloop(m dsl.Matcher) {
+ m.Match(
+ "for $i := $x; $i < $y; $i += $z { $*_ }",
+ "for $i = $x; $i < $y; $i += $z { $*_ }",
+ ).
+ Where(m["i"].Type.Is("float64")).
+ Report("floating point for loop counter")
+
+ m.Match(
+ "for $i := $x; $i < $y; $i += $z { $*_ }",
+ "for $i = $x; $i < $y; $i += $z { $*_ }",
+ ).
+ Where(m["i"].Type.Is("float32")).
+ Report("floating point for loop counter")
+}
+
+func urlredacted(m dsl.Matcher) {
+ m.Match(
+ "log.Println($x, $*_)",
+ "log.Println($*_, $x, $*_)",
+ "log.Println($*_, $x)",
+ "log.Printf($*_, $x, $*_)",
+ "log.Printf($*_, $x)",
+
+ "log.Println($x, $*_)",
+ "log.Println($*_, $x, $*_)",
+ "log.Println($*_, $x)",
+ "log.Printf($*_, $x, $*_)",
+ "log.Printf($*_, $x)",
+ ).
+ Where(m["x"].Type.Is("*url.URL")).
+ Report("consider $x.Redacted() when outputting URLs")
+}
+
+func sprinterr(m dsl.Matcher) {
+ m.Match(`fmt.Sprint($err)`,
+ `fmt.Sprintf("%s", $err)`,
+ `fmt.Sprintf("%v", $err)`,
+ ).
+ Where(m["err"].Type.Is("error")).
+ Report("maybe call $err.Error() instead of fmt.Sprint()?")
+}
+
+// disable this check, because it can not apply to generic type
+//func largeloopcopy(m dsl.Matcher) {
+// m.Match(
+// `for $_, $v := range $_ { $*_ }`,
+// ).
+// Where(m["v"].Type.Size > 1024).
+// Report(`loop copies large value each iteration`)
+//}
+
+func joinpath(m dsl.Matcher) {
+ m.Match(
+ `strings.Join($_, "/")`,
+ `strings.Join($_, "\\")`,
+ "strings.Join($_, `\\`)",
+ ).
+ Report(`did you mean path.Join() or filepath.Join() ?`)
+}
+
+func readfull(m dsl.Matcher) {
+ m.Match(`$n, $err := io.ReadFull($_, $slice)
+ if $err != nil || $n != len($slice) {
+ $*_
+ }`,
+ `$n, $err := io.ReadFull($_, $slice)
+ if $n != len($slice) || $err != nil {
+ $*_
+ }`,
+ `$n, $err = io.ReadFull($_, $slice)
+ if $err != nil || $n != len($slice) {
+ $*_
+ }`,
+ `$n, $err = io.ReadFull($_, $slice)
+ if $n != len($slice) || $err != nil {
+ $*_
+ }`,
+ `if $n, $err := io.ReadFull($_, $slice); $n != len($slice) || $err != nil {
+ $*_
+ }`,
+ `if $n, $err := io.ReadFull($_, $slice); $err != nil || $n != len($slice) {
+ $*_
+ }`,
+ `if $n, $err = io.ReadFull($_, $slice); $n != len($slice) || $err != nil {
+ $*_
+ }`,
+ `if $n, $err = io.ReadFull($_, $slice); $err != nil || $n != len($slice) {
+ $*_
+ }`,
+ ).Report("io.ReadFull() returns err == nil iff n == len(slice)")
+}
+
+func nilerr(m dsl.Matcher) {
+ m.Match(
+ `if err == nil { return err }`,
+ `if err == nil { return $*_, err }`,
+ ).
+ Report(`return nil error instead of nil value`)
+}
+
+func mailaddress(m dsl.Matcher) {
+ m.Match(
+ "fmt.Sprintf(`\"%s\" <%s>`, $NAME, $EMAIL)",
+ "fmt.Sprintf(`\"%s\"<%s>`, $NAME, $EMAIL)",
+ "fmt.Sprintf(`%s <%s>`, $NAME, $EMAIL)",
+ "fmt.Sprintf(`%s<%s>`, $NAME, $EMAIL)",
+ `fmt.Sprintf("\"%s\"<%s>", $NAME, $EMAIL)`,
+ `fmt.Sprintf("\"%s\" <%s>", $NAME, $EMAIL)`,
+ `fmt.Sprintf("%s<%s>", $NAME, $EMAIL)`,
+ `fmt.Sprintf("%s <%s>", $NAME, $EMAIL)`,
+ ).
+ Report("use net/mail Address.String() instead of fmt.Sprintf()").
+ Suggest("(&mail.Address{Name:$NAME, Address:$EMAIL}).String()")
+}
+
+func errnetclosed(m dsl.Matcher) {
+ m.Match(
+ `strings.Contains($err.Error(), $text)`,
+ ).
+ Where(m["text"].Text.Matches("\".*closed network connection.*\"")).
+ Report(`String matching against error texts is fragile; use net.ErrClosed instead`).
+ Suggest(`errors.Is($err, net.ErrClosed)`)
+}
+
+func httpheaderadd(m dsl.Matcher) {
+ m.Match(
+ `$H.Add($KEY, $VALUE)`,
+ ).
+ Where(m["H"].Type.Is("http.Header")).
+ Report("use http.Header.Set method instead of Add to overwrite all existing header values").
+ Suggest(`$H.Set($KEY, $VALUE)`)
+}
+
+func hmacnew(m dsl.Matcher) {
+ m.Match("hmac.New(func() hash.Hash { return $x }, $_)",
+ `$f := func() hash.Hash { return $x }
+ $*_
+ hmac.New($f, $_)`,
+ ).Where(m["x"].Pure).
+ Report("invalid hash passed to hmac.New()")
+}
+
+func writestring(m dsl.Matcher) {
+ m.Match(`io.WriteString($w, string($b))`).
+ Where(m["b"].Type.Is("[]byte")).
+ Suggest("$w.Write($b)")
+}
+
+func badlock(m dsl.Matcher) {
+ // Shouldn't give many false positives without type filter
+ // as Lock+Unlock pairs in combination with defer gives us pretty
+ // a good chance to guess correctly. If we constrain the type to sync.Mutex
+ // then it'll be harder to match embedded locks and custom methods
+ // that may forward the call to the sync.Mutex (or other synchronization primitive).
+
+ m.Match(`$mu.Lock(); defer $mu.RUnlock()`).Report(`maybe $mu.RLock() was intended?`)
+ m.Match(`$mu.RLock(); defer $mu.Unlock()`).Report(`maybe $mu.Lock() was intended?`)
+}
diff --git a/client/write_options.go b/client/write_options.go
index 54139ef0b21fa..612cc7fe2d995 100644
--- a/client/write_options.go
+++ b/client/write_options.go
@@ -28,6 +28,7 @@ import (
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
"github.com/milvus-io/milvus/client/v2/column"
"github.com/milvus-io/milvus/client/v2/entity"
+ "github.com/milvus-io/milvus/client/v2/row"
)
type InsertOption interface {
@@ -71,10 +72,8 @@ func (opt *columnBasedDataOption) processInsertColumns(colSchema *entity.Schema,
l := col.Len()
if rowSize == 0 {
rowSize = l
- } else {
- if rowSize != l {
- return nil, 0, errors.New("column size not match")
- }
+ } else if rowSize != l {
+ return nil, 0, errors.New("column size not match")
}
field, has := mNameField[col.Name()]
if !has {
@@ -247,6 +246,56 @@ func NewColumnBasedInsertOption(collName string, columns ...column.Column) *colu
}
}
+type rowBasedDataOption struct {
+ *columnBasedDataOption
+ rows []any
+}
+
+func NewRowBasedInsertOption(collName string, rows ...any) *rowBasedDataOption {
+ return &rowBasedDataOption{
+ columnBasedDataOption: &columnBasedDataOption{
+ collName: collName,
+ },
+ rows: rows,
+ }
+}
+
+func (opt *rowBasedDataOption) InsertRequest(coll *entity.Collection) (*milvuspb.InsertRequest, error) {
+ columns, err := row.AnyToColumns(opt.rows, coll.Schema)
+ if err != nil {
+ return nil, err
+ }
+ opt.columnBasedDataOption.columns = columns
+ fieldsData, rowNum, err := opt.processInsertColumns(coll.Schema, opt.columns...)
+ if err != nil {
+ return nil, err
+ }
+ return &milvuspb.InsertRequest{
+ CollectionName: opt.collName,
+ PartitionName: opt.partitionName,
+ FieldsData: fieldsData,
+ NumRows: uint32(rowNum),
+ }, nil
+}
+
+func (opt *rowBasedDataOption) UpsertRequest(coll *entity.Collection) (*milvuspb.UpsertRequest, error) {
+ columns, err := row.AnyToColumns(opt.rows, coll.Schema)
+ if err != nil {
+ return nil, err
+ }
+ opt.columnBasedDataOption.columns = columns
+ fieldsData, rowNum, err := opt.processInsertColumns(coll.Schema, opt.columns...)
+ if err != nil {
+ return nil, err
+ }
+ return &milvuspb.UpsertRequest{
+ CollectionName: opt.collName,
+ PartitionName: opt.partitionName,
+ FieldsData: fieldsData,
+ NumRows: uint32(rowNum),
+ }, nil
+}
+
type DeleteOption interface {
Request() *milvuspb.DeleteRequest
}
diff --git a/client/write_test.go b/client/write_test.go
index 3fdb9ece0f615..a87957e615c0a 100644
--- a/client/write_test.go
+++ b/client/write_test.go
@@ -22,13 +22,14 @@ import (
"math/rand"
"testing"
+ "github.com/samber/lo"
+ "github.com/stretchr/testify/mock"
+ "github.com/stretchr/testify/suite"
+
"github.com/milvus-io/milvus-proto/go-api/v2/milvuspb"
"github.com/milvus-io/milvus-proto/go-api/v2/schemapb"
"github.com/milvus-io/milvus/client/v2/entity"
"github.com/milvus-io/milvus/pkg/util/merr"
- "github.com/samber/lo"
- "github.com/stretchr/testify/mock"
- "github.com/stretchr/testify/suite"
)
type WriteSuite struct {
diff --git a/cmd/roles/roles.go b/cmd/roles/roles.go
index 1105498f5f6e7..16be7ac378958 100644
--- a/cmd/roles/roles.go
+++ b/cmd/roles/roles.go
@@ -411,7 +411,7 @@ func (mr *MilvusRoles) Run() {
}
tracer.SetTracerProvider(exp, params.TraceCfg.SampleFraction.GetAsFloat())
- log.Info("Reset tracer finished", zap.String("Exporter", params.TraceCfg.Exporter.GetValue()))
+ log.Info("Reset tracer finished", zap.String("Exporter", params.TraceCfg.Exporter.GetValue()), zap.Float64("SampleFraction", params.TraceCfg.SampleFraction.GetAsFloat()))
if paramtable.GetRole() == typeutil.QueryNodeRole || paramtable.GetRole() == typeutil.StandaloneRole {
initcore.InitTraceConfig(params)
diff --git a/configs/milvus.yaml b/configs/milvus.yaml
index 6cf78cc058e63..d6415e1a76208 100644
--- a/configs/milvus.yaml
+++ b/configs/milvus.yaml
@@ -68,7 +68,7 @@ tikv:
tlsCACert: # path to your CACert file
localStorage:
- path: /tmp/milvus/data/ # please adjust in embedded Milvus: /tmp/milvus/data/
+ path: /var/lib/milvus/data/ # please adjust in embedded Milvus: /tmp/milvus/data/
# Related configuration of MinIO/S3/GCS or any other service supports S3 API, which is responsible for data persistence for Milvus.
# We refer to the storage service as MinIO/S3 in the following description for simplicity.
@@ -329,13 +329,13 @@ queryNode:
enabled: true
memoryLimit: 2147483648 # 2 GB, 2 * 1024 *1024 *1024
readAheadPolicy: willneed # The read ahead policy of chunk cache, options: `normal, random, sequential, willneed, dontneed`
- # options: async, sync, off.
+ # options: async, sync, disable.
# Specifies the necessity for warming up the chunk cache.
- # 1. If set to "sync" or "async," the original vector data will be synchronously/asynchronously loaded into the
+ # 1. If set to "sync" or "async" the original vector data will be synchronously/asynchronously loaded into the
# chunk cache during the load process. This approach has the potential to substantially reduce query/search latency
# for a specific duration post-load, albeit accompanied by a concurrent increase in disk usage;
- # 2. If set to "off," original vector data will only be loaded into the chunk cache during search/query.
- warmup: async
+ # 2. If set to "disable" original vector data will only be loaded into the chunk cache during search/query.
+ warmup: disable
mmap:
mmapEnabled: false # Enable mmap for loading data
lazyload:
@@ -377,6 +377,7 @@ queryNode:
maxQueueLength: 16 # Maximum length of task queue in flowgraph
maxParallelism: 1024 # Maximum number of tasks executed in parallel in the flowgraph
enableSegmentPrune: false # use partition prune function on shard delegator
+ queryStreamBatchSize: 4194304 # return batch size of stream query
ip: # if not specified, use the first unicastable address
port: 21123
grpc:
@@ -481,6 +482,7 @@ dataCoord:
serverMaxRecvSize: 268435456
clientMaxSendSize: 268435456
clientMaxRecvSize: 536870912
+ syncSegmentsInterval: 300
dataNode:
dataSync:
@@ -494,7 +496,7 @@ dataNode:
coldTime: 60 # Turn on skip mode after there are only timetick msg for x seconds
segment:
insertBufSize: 16777216 # Max buffer size to flush for a single segment.
- deleteBufBytes: 67108864 # Max buffer size in bytes to flush del for a single channel, default as 16MB
+ deleteBufBytes: 16777216 # Max buffer size in bytes to flush del for a single channel, default as 16MB
syncPeriod: 600 # The period to sync segments if buffer is not empty.
memory:
forceSyncEnable: true # Set true to force sync if memory usage is too high
@@ -612,7 +614,7 @@ common:
ttMsgEnabled: true # Whether the instance disable sending ts messages
traceLogMode: 0 # trace request info
bloomFilterSize: 100000 # bloom filter initial size
- maxBloomFalsePositive: 0.05 # max false positive rate for bloom filter
+ maxBloomFalsePositive: 0.001 # max false positive rate for bloom filter
# QuotaConfig, configurations of Milvus quota and limits.
# By default, we enable:
@@ -631,6 +633,14 @@ quotaAndLimits:
# collects metrics from Proxies, Query cluster and Data cluster.
# seconds, (0 ~ 65536)
quotaCenterCollectInterval: 3
+ limits:
+ allocRetryTimes: 15 # retry times when delete alloc forward data from rate limit failed
+ allocWaitInterval: 1000 # retry wait duration when delete alloc forward data rate failed, in millisecond
+ complexDeleteLimitEnable: false # whether complex delete check forward data by limiter
+ maxCollectionNum: 65536
+ maxCollectionNumPerDB: 65536
+ maxInsertSize: -1 # maximum size of a single insert request, in bytes, -1 means no limit
+ maxResourceGroupNumOfQueryNode: 1024 # maximum number of resource groups of query nodes
ddl:
enabled: false
collectionRate: -1 # qps, default no limit, rate for CreateCollection, DropCollection, LoadCollection, ReleaseCollection
@@ -711,11 +721,6 @@ quotaAndLimits:
max: -1 # qps, default no limit
partition:
max: -1 # qps, default no limit
- limits:
- maxCollectionNum: 65536
- maxCollectionNumPerDB: 65536
- maxInsertSize: -1 # maximum size of a single insert request, in bytes, -1 means no limit
- maxResourceGroupNumOfQueryNode: 1024 # maximum number of resource groups of query nodes
limitWriting:
# forceDeny false means dml requests are allowed (except for some
# specific conditions, such as memory of nodes to water marker), true means always reject all dml requests.
diff --git a/configs/pgo/default.pgo b/configs/pgo/default.pgo
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/go.mod b/go.mod
index f50cfb447b574..3412f20c408a5 100644
--- a/go.mod
+++ b/go.mod
@@ -1,6 +1,6 @@
module github.com/milvus-io/milvus
-go 1.20
+go 1.21
require (
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.7.0
@@ -65,9 +65,11 @@ require (
require github.com/milvus-io/milvus-storage/go v0.0.0-20231227072638-ebd0b8e56d70
require (
+ github.com/greatroar/blobloom v0.0.0-00010101000000-000000000000
github.com/jolestar/go-commons-pool/v2 v2.1.2
github.com/milvus-io/milvus/pkg v0.0.0-00010101000000-000000000000
github.com/pkg/errors v0.9.1
+ github.com/zeebo/xxh3 v1.0.2
gopkg.in/yaml.v3 v3.0.1
)
@@ -209,7 +211,6 @@ require (
github.com/x448/float16 v0.8.4 // indirect
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 // indirect
github.com/yusufpapurcu/wmi v1.2.2 // indirect
- github.com/zeebo/xxh3 v1.0.2 // indirect
go.etcd.io/bbolt v1.3.6 // indirect
go.etcd.io/etcd/client/pkg/v3 v3.5.5 // indirect
go.etcd.io/etcd/client/v2 v2.305.5 // indirect
@@ -250,6 +251,7 @@ replace (
github.com/bketelsen/crypt => github.com/bketelsen/crypt v0.0.4 // Fix security alert for core-os/etcd
github.com/expr-lang/expr => github.com/SimFG/expr v0.0.0-20231218130003-94d085776dc5
github.com/go-kit/kit => github.com/go-kit/kit v0.1.0
+ github.com/greatroar/blobloom => github.com/milvus-io/blobloom v0.0.0-20240603110411-471ae49f3b93
// github.com/milvus-io/milvus-storage/go => ../milvus-storage/go
github.com/milvus-io/milvus/pkg => ./pkg
github.com/streamnative/pulsarctl => github.com/xiaofan-luan/pulsarctl v0.5.1
diff --git a/go.sum b/go.sum
index 20a4faf084194..f0f45360fef38 100644
--- a/go.sum
+++ b/go.sum
@@ -56,12 +56,14 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.3.0/go.mod h1:OQeznEEkTZ9Orh
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0 h1:sXr+ck84g/ZlZUOZiNELInmMgOsuGwdjjVkEIde0OtY=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.3.0/go.mod h1:okt5dMMTOFjX/aovMlrjvvXoPMBVSPzk9185BT0+eZM=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0 h1:Ma67P/GGprNwsslzEH6+Kb8nybI8jpDTm4Wmzu2ReK8=
+github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/storage/armstorage v1.2.0/go.mod h1:c+Lifp3EDEamAkPVzMooRNOK6CZjNSdEnf1A7jsI9u4=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0 h1:nVocQV40OQne5613EeLayJiRAJuKlBGy+m22qWG+WRg=
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.1.0/go.mod h1:7QJP7dr2wznCMeqIrhMgWGf7XpAQnVrJqDm9nvV3Cu4=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0 h1:OBhqkivkhkMqLPymWEppkm7vgPQY2XsHoEkaMQ0AdZY=
github.com/AzureAD/microsoft-authentication-library-for-go v1.0.0/go.mod h1:kgDmCTgBzIEPFElEF+FK0SdjAor06dRq2Go927dnQ6o=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak=
+github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
github.com/CloudyKit/fastprinter v0.0.0-20200109182630-33d98a066a53/go.mod h1:+3IMCy2vIlbG1XG/0ggNQv0SvxCAIpPM5b1nCz56Xno=
github.com/CloudyKit/jet/v3 v3.0.0/go.mod h1:HKQPgSJmdK8hdoAbKUUWajkHyHo4RaU5rMdUywE7VMo=
@@ -169,6 +171,7 @@ github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWH
github.com/cncf/xds/go v0.0.0-20211001041855-01bcc9b48dfe/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k=
+github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
github.com/cockroachdb/datadriven v1.0.2 h1:H9MtNqVoVhvd9nCBwOyDjUEdZCREqbIdCJD93PBm/jA=
github.com/cockroachdb/datadriven v1.0.2/go.mod h1:a9RdTaap04u637JoCzcUoIcDmvwSUtcUFtT/C3kJlTU=
@@ -215,6 +218,7 @@ github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8
github.com/dimfeld/httptreemux v5.0.1+incompatible h1:Qj3gVcDNoOthBAqftuD596rm4wg/adLLz5xh5CmpiCA=
github.com/dimfeld/httptreemux v5.0.1+incompatible/go.mod h1:rbUlSV+CCpv/SuqUTP/8Bk2O3LyUV436/yaRGkhP6Z0=
github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI=
+github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ=
github.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=
github.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
@@ -235,6 +239,7 @@ github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.
github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.mod h1:KJwIaB5Mv44NWtYuAOFCVOjcI94vtpEz2JU/D2v6IjE=
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8=
+github.com/envoyproxy/protoc-gen-validate v0.10.1/go.mod h1:DRjgyB0I43LtJapqN6NiRwroiAU2PaFuvk/vjgh61ss=
github.com/etcd-io/bbolt v1.3.3/go.mod h1:ZF2nL25h33cCyBtcyWeZ2/I3HQOfTP+0PIEvHjkjCrw=
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c h1:8ISkoahWXwZR41ois5lSJBSVw4D0OV19Ht/JSTzvSv0=
github.com/facebookgo/ensure v0.0.0-20200202191622-63f1cf65ac4c/go.mod h1:Yg+htXGokKKdzcwhuNDwVvN+uBxDGXJ7G/VN1d8fa64=
@@ -245,6 +250,7 @@ github.com/facebookgo/subset v0.0.0-20200203212716-c811ad88dec4/go.mod h1:5tD+ne
github.com/fasthttp-contrib/websocket v0.0.0-20160511215533-1f3b11f56072/go.mod h1:duJ4Jxv5lDcvg4QuQr0oowTf7dz4/CR8NtyCooz9HL8=
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
github.com/fatih/color v1.10.0 h1:s36xzo75JdqLaaWoiEHk767eHiwo0598uUxyfiPkDsg=
+github.com/fatih/color v1.10.0/go.mod h1:ELkj/draVOlAH/xkhN6mQ50Qd0MPOk5AAr3maGEBuJM=
github.com/fatih/structs v1.1.0/go.mod h1:9NiDSp5zOcgEDl+j00MP/WkGVPOlPRLejGD8Ga6PJ7M=
github.com/fogleman/gg v1.2.1-0.20190220221249-0403632d5b90/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
github.com/fogleman/gg v1.3.0/go.mod h1:R/bRT+9gY/C5z7JzPU0zXsXHKM4/ayA+zqcVNZzPa1k=
@@ -257,6 +263,7 @@ github.com/frankban/quicktest v1.7.2/go.mod h1:jaStnuzAqU1AJdCO0l53JDCJrVDKcS03D
github.com/frankban/quicktest v1.10.0/go.mod h1:ui7WezCLWMWxVWr1GETZY3smRy0G4KWq9vcPtJmFl7Y=
github.com/frankban/quicktest v1.14.0/go.mod h1:NeW+ay9A/U67EYXNFA1nPE8e/tnQv/09mUdL/ijj8og=
github.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=
+github.com/frankban/quicktest v1.14.5/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
@@ -299,6 +306,7 @@ github.com/go-martini/martini v0.0.0-20170121215854-22fa46961aab/go.mod h1:/P9AE
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
+github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
@@ -337,6 +345,7 @@ github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0/go.mod h1:E/TSTwGw
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
github.com/golang/glog v1.0.0/go.mod h1:EWib/APOK0SL3dFbYqvxE3UYd8E6s1ouQ7iEp/0LWV4=
github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE=
+github.com/golang/glog v1.1.0/go.mod h1:pfYeQZ3JWZoXTV5sFc986z3HTpwQs9At6P4ImfuP3NQ=
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
@@ -400,6 +409,7 @@ github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN
github.com/google/go-querystring v1.0.0/go.mod h1:odCYkC5MyYFN7vkCjXpyrEuKhc/BUO6wN/zVPAxq5ck=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
+github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
github.com/google/martian/v3 v3.0.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
github.com/google/martian/v3 v3.1.0/go.mod h1:y5Zk1BBys9G+gd6Jrk0W3cC1+ELVxBWuIGO+w/tUAp0=
@@ -546,7 +556,9 @@ github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/kris-nova/logger v0.0.0-20181127235838-fd0d87064b06 h1:vN4d3jSss3ExzUn2cE0WctxztfOgiKvMKnDrydBsg00=
+github.com/kris-nova/logger v0.0.0-20181127235838-fd0d87064b06/go.mod h1:++9BgZujZd4v0ZTZCb5iPsaomXdZWyxotIAh1IiDm44=
github.com/kris-nova/lolgopher v0.0.0-20180921204813-313b3abb0d9b h1:xYEM2oBUhBEhQjrV+KJ9lEWDWYZoNVZUaBF++Wyljq4=
+github.com/kris-nova/lolgopher v0.0.0-20180921204813-313b3abb0d9b/go.mod h1:V0HF/ZBlN86HqewcDC/cVxMmYDiRukWjSrgKLUAn9Js=
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
github.com/labstack/echo/v4 v4.5.0/go.mod h1:czIriw4a0C1dFun+ObrXp7ok03xON0N1awStJ6ArI7Y=
@@ -554,6 +566,7 @@ github.com/labstack/gommon v0.3.0/go.mod h1:MULnywXg0yavhxWKc+lOruYdAhDwPK9wf0OL
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/lingdor/stackerror v0.0.0-20191119040541-976d8885ed76 h1:IVlcvV0CjvfBYYod5ePe89l+3LBAl//6n9kJ9Vr2i0k=
+github.com/lingdor/stackerror v0.0.0-20191119040541-976d8885ed76/go.mod h1:Iu9BHUvTh8/KpbuSoKx/CaJEdJvFxSverxIy7I+nq7s=
github.com/linkedin/goavro v2.1.0+incompatible/go.mod h1:bBCwI2eGYpUI/4820s67MElg9tdeLbINjLjiM2xZFYM=
github.com/linkedin/goavro/v2 v2.9.8/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA=
github.com/linkedin/goavro/v2 v2.10.0/go.mod h1:UgQUb2N/pmueQYH9bfqFioWxzYCZXSfF8Jw03O5sjqA=
@@ -580,6 +593,7 @@ github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27k
github.com/mattn/go-isatty v0.0.19 h1:JITubQf0MOLdlGRuRq+jtsDlekdYPia9ZFsB8h/APPA=
github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.8 h1:3tS41NlGYSmhhe/8fhGRzc+z3AYCw1Fe1WAyLuujKs0=
+github.com/mattn/go-runewidth v0.0.8/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/goveralls v0.0.2/go.mod h1:8d1ZMHsd7fW6IRPKQh46F2WRpyib5/X4FOpevwGNQEw=
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo=
@@ -589,6 +603,8 @@ github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQ
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/microcosm-cc/bluemonday v1.0.2/go.mod h1:iVP4YcDBq+n/5fb23BhYFvIMq/leAFZyRl6bYmGDlGc=
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
+github.com/milvus-io/blobloom v0.0.0-20240603110411-471ae49f3b93 h1:xnIeuG1nuTEHKbbv51OwNGO82U+d6ut08ppTmZVm+VY=
+github.com/milvus-io/blobloom v0.0.0-20240603110411-471ae49f3b93/go.mod h1:mjMJ1hh1wjGVfr93QIHJ6FfDNVrA0IELv8OvMHJxHKs=
github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b h1:TfeY0NxYxZzUfIfYe5qYDBzt4ZYRqzUjTR6CvUzjat8=
github.com/milvus-io/gorocksdb v0.0.0-20220624081344-8c5f4212846b/go.mod h1:iwW+9cWfIzzDseEBCCeDSN5SD16Tidvy8cwQ7ZY8Qj4=
github.com/milvus-io/milvus-proto/go-api/v2 v2.3.4-0.20240430035521-259ae1d10016 h1:8WV4maXLeGEyJCCYIc1DmZ18H+VFAjMrwXJg5iI2nX4=
@@ -651,6 +667,7 @@ github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE=
github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU=
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
github.com/olekukonko/tablewriter v0.0.1 h1:b3iUnf1v+ppJiOfNX4yxxqfWKMQPZR5yoh8urCTFX88=
+github.com/olekukonko/tablewriter v0.0.1/go.mod h1:vsDQFd/mU46D+Z4whnwzcISnGGzXWMclvtLoiIKAKIo=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.10.3/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk=
@@ -713,6 +730,7 @@ github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndr
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=
github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=
+github.com/prashantv/gostub v1.1.0/go.mod h1:A5zLQHz7ieHGG7is6LLXLz7I8+3LZzsrV0P1IAHhP5U=
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
@@ -840,6 +858,7 @@ github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.865 h1:LcUqBlKC4j15LhT303yQDX/XxyHG4haEQqbHgZZA4SY=
github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.865/go.mod h1:r5r4xbfxSaeR04b166HGsBa/R4U3SueirEUpXGuw+Q0=
github.com/thoas/go-funk v0.9.1 h1:O549iLZqPpTUQ10ykd26sZhzD+rmR5pWhuElrhbC20M=
+github.com/thoas/go-funk v0.9.1/go.mod h1:+IWnUfUmFO1+WVYQWQtIJHeRRdaIyyYglZN7xzUPe4Q=
github.com/tiancaiamao/gp v0.0.0-20221230034425-4025bc8a4d4a h1:J/YdBZ46WKpXsxsW93SG+q0F8KI+yFrcIDT4c/RNoc4=
github.com/tiancaiamao/gp v0.0.0-20221230034425-4025bc8a4d4a/go.mod h1:h4xBhSNtOeEosLJ4P7JyKXX7Cabg7AVkWCK5gV2vOrM=
github.com/tidwall/gjson v1.14.4 h1:uo0p8EbA09J7RQaflQ1aBRffTR7xedD2bcIVSYxLnkM=
@@ -899,6 +918,7 @@ github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1
github.com/yusufpapurcu/wmi v1.2.2 h1:KBNDSne4vP5mbSWnJbO+51IMOXJB67QiYCSBrubbPRg=
github.com/yusufpapurcu/wmi v1.2.2/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=
github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ=
+github.com/zeebo/assert v1.3.0/go.mod h1:Pq9JiuJQpG8JLJdtkwrJESF0Foym2/D9XMU5ciN/wJ0=
github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
@@ -969,6 +989,7 @@ go.uber.org/automaxprocs v1.5.3/go.mod h1:eRbA25aqJrxAbsLO0xy5jVwPt7FQnRgjW+efnw
go.uber.org/goleak v1.1.10/go.mod h1:8a7PlsEVH3e/a/GLqe5IIrQx6GzcnRmZEufDUTk4A7A=
go.uber.org/goleak v1.1.11/go.mod h1:cwTWslyiVhfpKIDGSZEM2HlOvcqm+tG4zioyIeLoqMQ=
go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A=
+go.uber.org/goleak v1.2.1/go.mod h1:qlT2yGI9QafXHhZZLxlSuNsMw3FFLxBr+tBRlmO1xH4=
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU=
go.uber.org/multierr v1.7.0/go.mod h1:7EAYxJLBy9rStEaz58O2t4Uvip6FSURkq8/ppBp95ak=
@@ -1456,6 +1477,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8
gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
+gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/errgo.v1 v1.0.0/go.mod h1:CxwszS/Xz1C49Ucd2i6Zil5UToP1EmyrFhKaMVbg1mk=
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
@@ -1512,3 +1534,4 @@ sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc=
sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo=
sigs.k8s.io/yaml v1.3.0/go.mod h1:GeOyir5tyXNByN85N/dRIT9es5UQNerPYEKK56eTBm8=
stathat.com/c/consistent v1.0.0 h1:ezyc51EGcRPJUxfHGSgJjWzJdj3NiMU9pNfLNGiXV0c=
+stathat.com/c/consistent v1.0.0/go.mod h1:QkzMWzcbB+yQBL2AttO6sgsQS/JSTapcDISJalmCDS0=
diff --git a/internal/core/src/bitset/detail/platform/arm/sve-impl.h b/internal/core/src/bitset/detail/platform/arm/sve-impl.h
index 18433402d04d9..dfc84f2824d8a 100644
--- a/internal/core/src/bitset/detail/platform/arm/sve-impl.h
+++ b/internal/core/src/bitset/detail/platform/arm/sve-impl.h
@@ -42,63 +42,6 @@ namespace {
//
constexpr size_t MAX_SVE_WIDTH = 2048;
-constexpr uint8_t SVE_LANES_8[MAX_SVE_WIDTH / 8] = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
- 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
- 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
- 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,
- 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
- 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
-
- 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
- 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
- 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60,
- 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B,
- 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
- 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F,
-
- 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A,
- 0x8B, 0x8C, 0x8D, 0x8E, 0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,
- 0x96, 0x97, 0x98, 0x99, 0x9A, 0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0,
- 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xAB,
- 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6,
- 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE, 0xBF,
-
- 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA,
- 0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5,
- 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0,
- 0xE1, 0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB,
- 0xEC, 0xED, 0xEE, 0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
- 0xF7, 0xF8, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF};
-
-constexpr uint16_t SVE_LANES_16[MAX_SVE_WIDTH / 16] = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
- 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
- 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
- 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,
- 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
- 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F,
-
- 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x4A,
- 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,
- 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E, 0x5F, 0x60,
- 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A, 0x6B,
- 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
- 0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F};
-
-constexpr uint32_t SVE_LANES_32[MAX_SVE_WIDTH / 32] = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
- 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
- 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20,
- 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B,
- 0x2C, 0x2D, 0x2E, 0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36,
- 0x37, 0x38, 0x39, 0x3A, 0x3B, 0x3C, 0x3D, 0x3E, 0x3F};
-
-constexpr uint64_t SVE_LANES_64[MAX_SVE_WIDTH / 64] = {
- 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
- 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,
- 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F};
-
/*
// debugging facilities
@@ -131,179 +74,28 @@ void print_svuint8_t(const svuint8_t value) {
///////////////////////////////////////////////////////////////////////////
-// todo: replace with pext whenever available
-
-// generate 16-bit bitmask from 8 serialized 16-bit svbool_t values
-void
-write_bitmask_16_8x(uint8_t* const __restrict res_u8,
- const svbool_t pred_op,
- const svbool_t pred_write,
- const uint8_t* const __restrict pred_buf) {
- // perform parallel pext
- // 2048b -> 32 bytes mask -> 256 bytes total, 128 uint16_t values
- // 512b -> 8 bytes mask -> 64 bytes total, 32 uint16_t values
- // 256b -> 4 bytes mask -> 32 bytes total, 16 uint16_t values
- // 128b -> 2 bytes mask -> 16 bytes total, 8 uint16_t values
-
- // this code does reduction of 16-bit 0b0A0B0C0D0E0F0G0H words into
- // uint8_t values 0bABCDEFGH, then writes ones to the memory
-
- // we need to operate in uint8_t
- const svuint8_t mask_8b = svld1_u8(pred_op, pred_buf);
-
- const svuint8_t mask_04_8b = svand_n_u8_z(pred_op, mask_8b, 0x01);
- const svuint8_t mask_15_8b = svand_n_u8_z(pred_op, mask_8b, 0x04);
- const svuint8_t mask_15s_8b = svlsr_n_u8_z(pred_op, mask_15_8b, 1);
- const svuint8_t mask_26_8b = svand_n_u8_z(pred_op, mask_8b, 0x10);
- const svuint8_t mask_26s_8b = svlsr_n_u8_z(pred_op, mask_26_8b, 2);
- const svuint8_t mask_37_8b = svand_n_u8_z(pred_op, mask_8b, 0x40);
- const svuint8_t mask_37s_8b = svlsr_n_u8_z(pred_op, mask_37_8b, 3);
-
- const svuint8_t mask_0347_8b = svorr_u8_z(pred_op, mask_04_8b, mask_37s_8b);
- const svuint8_t mask_1256_8b =
- svorr_u8_z(pred_op, mask_15s_8b, mask_26s_8b);
- const svuint8_t mask_cmb_8b =
- svorr_u8_z(pred_op, mask_0347_8b, mask_1256_8b);
-
- //
- const svuint16_t shifts_16b = svdup_u16(0x0400UL);
- const svuint8_t shifts_8b = svreinterpret_u8_u16(shifts_16b);
- const svuint8_t shifted_8b_m0 = svlsl_u8_z(pred_op, mask_cmb_8b, shifts_8b);
-
- const svuint8_t zero_8b = svdup_n_u8(0);
-
- const svuint8_t shifted_8b_m3 =
- svorr_u8_z(pred_op,
- svuzp1_u8(shifted_8b_m0, zero_8b),
- svuzp2_u8(shifted_8b_m0, zero_8b));
-
- // write a finished bitmask
- svst1_u8(pred_write, res_u8, shifted_8b_m3);
-}
-
-// generate 32-bit bitmask from 8 serialized 32-bit svbool_t values
-void
-write_bitmask_32_8x(uint8_t* const __restrict res_u8,
- const svbool_t pred_op,
- const svbool_t pred_write,
- const uint8_t* const __restrict pred_buf) {
- // perform parallel pext
- // 2048b -> 32 bytes mask -> 256 bytes total, 64 uint32_t values
- // 512b -> 8 bytes mask -> 64 bytes total, 16 uint32_t values
- // 256b -> 4 bytes mask -> 32 bytes total, 8 uint32_t values
- // 128b -> 2 bytes mask -> 16 bytes total, 4 uint32_t values
-
- // this code does reduction of 32-bit 0b000A000B000C000D... dwords into
- // uint8_t values 0bABCDEFGH, then writes ones to the memory
-
- // we need to operate in uint8_t
- const svuint8_t mask_8b = svld1_u8(pred_op, pred_buf);
-
- const svuint8_t mask_024_8b = svand_n_u8_z(pred_op, mask_8b, 0x01);
- const svuint8_t mask_135s_8b = svlsr_n_u8_z(pred_op, mask_8b, 3);
- const svuint8_t mask_cmb_8b =
- svorr_u8_z(pred_op, mask_024_8b, mask_135s_8b);
-
- //
- const svuint32_t shifts_32b = svdup_u32(0x06040200UL);
- const svuint8_t shifts_8b = svreinterpret_u8_u32(shifts_32b);
- const svuint8_t shifted_8b_m0 = svlsl_u8_z(pred_op, mask_cmb_8b, shifts_8b);
-
- const svuint8_t zero_8b = svdup_n_u8(0);
-
- const svuint8_t shifted_8b_m2 =
- svorr_u8_z(pred_op,
- svuzp1_u8(shifted_8b_m0, zero_8b),
- svuzp2_u8(shifted_8b_m0, zero_8b));
- const svuint8_t shifted_8b_m3 =
- svorr_u8_z(pred_op,
- svuzp1_u8(shifted_8b_m2, zero_8b),
- svuzp2_u8(shifted_8b_m2, zero_8b));
-
- // write a finished bitmask
- svst1_u8(pred_write, res_u8, shifted_8b_m3);
-}
-
-// generate 64-bit bitmask from 8 serialized 64-bit svbool_t values
-void
-write_bitmask_64_8x(uint8_t* const __restrict res_u8,
- const svbool_t pred_op,
- const svbool_t pred_write,
- const uint8_t* const __restrict pred_buf) {
- // perform parallel pext
- // 2048b -> 32 bytes mask -> 256 bytes total, 32 uint64_t values
- // 512b -> 8 bytes mask -> 64 bytes total, 4 uint64_t values
- // 256b -> 4 bytes mask -> 32 bytes total, 2 uint64_t values
- // 128b -> 2 bytes mask -> 16 bytes total, 1 uint64_t values
-
- // this code does reduction of 64-bit 0b0000000A0000000B... qwords into
- // uint8_t values 0bABCDEFGH, then writes ones to the memory
-
- // we need to operate in uint8_t
- const svuint8_t mask_8b = svld1_u8(pred_op, pred_buf);
- const svuint64_t shifts_64b = svdup_u64(0x706050403020100ULL);
- const svuint8_t shifts_8b = svreinterpret_u8_u64(shifts_64b);
- const svuint8_t shifted_8b_m0 = svlsl_u8_z(pred_op, mask_8b, shifts_8b);
-
- const svuint8_t zero_8b = svdup_n_u8(0);
-
- const svuint8_t shifted_8b_m1 =
- svorr_u8_z(pred_op,
- svuzp1_u8(shifted_8b_m0, zero_8b),
- svuzp2_u8(shifted_8b_m0, zero_8b));
- const svuint8_t shifted_8b_m2 =
- svorr_u8_z(pred_op,
- svuzp1_u8(shifted_8b_m1, zero_8b),
- svuzp2_u8(shifted_8b_m1, zero_8b));
- const svuint8_t shifted_8b_m3 =
- svorr_u8_z(pred_op,
- svuzp1_u8(shifted_8b_m2, zero_8b),
- svuzp2_u8(shifted_8b_m2, zero_8b));
-
- // write a finished bitmask
- svst1_u8(pred_write, res_u8, shifted_8b_m3);
-}
-
-///////////////////////////////////////////////////////////////////////////
-
//
inline svbool_t
get_pred_op_8(const size_t n_elements) {
- const svbool_t pred_all_8 = svptrue_b8();
- const svuint8_t lanes_8 = svld1_u8(pred_all_8, SVE_LANES_8);
- const svuint8_t leftovers_op = svdup_n_u8(n_elements);
- const svbool_t pred_op = svcmpgt_u8(pred_all_8, leftovers_op, lanes_8);
- return pred_op;
+ return svwhilelt_b8(uint32_t(0), uint32_t(n_elements));
}
//
inline svbool_t
get_pred_op_16(const size_t n_elements) {
- const svbool_t pred_all_16 = svptrue_b16();
- const svuint16_t lanes_16 = svld1_u16(pred_all_16, SVE_LANES_16);
- const svuint16_t leftovers_op = svdup_n_u16(n_elements);
- const svbool_t pred_op = svcmpgt_u16(pred_all_16, leftovers_op, lanes_16);
- return pred_op;
+ return svwhilelt_b16(uint32_t(0), uint32_t(n_elements));
}
//
inline svbool_t
get_pred_op_32(const size_t n_elements) {
- const svbool_t pred_all_32 = svptrue_b32();
- const svuint32_t lanes_32 = svld1_u32(pred_all_32, SVE_LANES_32);
- const svuint32_t leftovers_op = svdup_n_u32(n_elements);
- const svbool_t pred_op = svcmpgt_u32(pred_all_32, leftovers_op, lanes_32);
- return pred_op;
+ return svwhilelt_b32(uint32_t(0), uint32_t(n_elements));
}
//
inline svbool_t
get_pred_op_64(const size_t n_elements) {
- const svbool_t pred_all_64 = svptrue_b64();
- const svuint64_t lanes_64 = svld1_u64(pred_all_64, SVE_LANES_64);
- const svuint64_t leftovers_op = svdup_n_u64(n_elements);
- const svbool_t pred_op = svcmpgt_u64(pred_all_64, leftovers_op, lanes_64);
- return pred_op;
+ return svwhilelt_b64(uint32_t(0), uint32_t(n_elements));
}
//
@@ -579,7 +371,7 @@ struct SVEVector {
using sve_type = svint8_t;
// measured in the number of elements that an SVE register can hold
- static inline size_t
+ static inline uint64_t
width() {
return svcntb();
}
@@ -606,7 +398,7 @@ struct SVEVector {
using sve_type = svint16_t;
// measured in the number of elements that an SVE register can hold
- static inline size_t
+ static inline uint64_t
width() {
return svcnth();
}
@@ -633,7 +425,7 @@ struct SVEVector {
using sve_type = svint32_t;
// measured in the number of elements that an SVE register can hold
- static inline size_t
+ static inline uint64_t
width() {
return svcntw();
}
@@ -660,7 +452,7 @@ struct SVEVector {
using sve_type = svint64_t;
// measured in the number of elements that an SVE register can hold
- static inline size_t
+ static inline uint64_t
width() {
return svcntd();
}
@@ -687,7 +479,7 @@ struct SVEVector {
using sve_type = svfloat32_t;
// measured in the number of elements that an SVE register can hold
- static inline size_t
+ static inline uint64_t
width() {
return svcntw();
}
@@ -714,7 +506,7 @@ struct SVEVector {
using sve_type = svfloat64_t;
// measured in the number of elements that an SVE register can hold
- static inline size_t
+ static inline uint64_t
width() {
return svcntd();
}
@@ -737,159 +529,262 @@ struct SVEVector {
///////////////////////////////////////////////////////////////////////////
-// an interesting discussion here:
-// https://stackoverflow.com/questions/77834169/what-is-a-fast-fallback-algorithm-which-emulates-pdep-and-pext-in-software
-
-// SVE2 has bitperm, which contains the implementation of pext
-
-// todo: replace with pext whenever available
-
-//
+// NBYTES is the size of the underlying datatype in bytes.
+// So, for example, for i8/u8 use 1, for i64/u64/f64 use 8/
template
struct MaskHelper {};
template <>
struct MaskHelper<1> {
static inline void
- write(uint8_t* const __restrict bitmask,
- const size_t size,
- const svbool_t pred0,
- const svbool_t pred1,
- const svbool_t pred2,
- const svbool_t pred3,
- const svbool_t pred4,
- const svbool_t pred5,
- const svbool_t pred6,
- const svbool_t pred7) {
- const size_t sve_width = svcntb();
- if (sve_width == 8 * sve_width) {
- // perform a full write
- *((svbool_t*)(bitmask + 0 * sve_width / 8)) = pred0;
- *((svbool_t*)(bitmask + 1 * sve_width / 8)) = pred1;
- *((svbool_t*)(bitmask + 2 * sve_width / 8)) = pred2;
- *((svbool_t*)(bitmask + 3 * sve_width / 8)) = pred3;
- *((svbool_t*)(bitmask + 4 * sve_width / 8)) = pred4;
- *((svbool_t*)(bitmask + 5 * sve_width / 8)) = pred5;
- *((svbool_t*)(bitmask + 6 * sve_width / 8)) = pred6;
- *((svbool_t*)(bitmask + 7 * sve_width / 8)) = pred7;
- } else {
- // perform a partial write
-
- // this is the buffer for the maximum possible case of 2048 bits
- uint8_t pred_buf[MAX_SVE_WIDTH / 8];
- *((volatile svbool_t*)(pred_buf + 0 * sve_width / 8)) = pred0;
- *((volatile svbool_t*)(pred_buf + 1 * sve_width / 8)) = pred1;
- *((volatile svbool_t*)(pred_buf + 2 * sve_width / 8)) = pred2;
- *((volatile svbool_t*)(pred_buf + 3 * sve_width / 8)) = pred3;
- *((volatile svbool_t*)(pred_buf + 4 * sve_width / 8)) = pred4;
- *((volatile svbool_t*)(pred_buf + 5 * sve_width / 8)) = pred5;
- *((volatile svbool_t*)(pred_buf + 6 * sve_width / 8)) = pred6;
- *((volatile svbool_t*)(pred_buf + 7 * sve_width / 8)) = pred7;
-
- // make the write mask
- const svbool_t pred_write = get_pred_op_8(size / 8);
-
- // load the buffer
- const svuint8_t mask_u8 = svld1_u8(pred_write, pred_buf);
- // write it to the bitmask
- svst1_u8(pred_write, bitmask, mask_u8);
- }
+ write_full(uint8_t* const __restrict bitmask,
+ const svbool_t pred0,
+ const svbool_t pred1,
+ const svbool_t pred2,
+ const svbool_t pred3,
+ const svbool_t pred4,
+ const svbool_t pred5,
+ const svbool_t pred6,
+ const svbool_t pred7) {
+ const uint64_t sve_width = svcntb();
+
+ // perform a full write
+ *((svbool_t*)(bitmask + 0 * sve_width / 8)) = pred0;
+ *((svbool_t*)(bitmask + 1 * sve_width / 8)) = pred1;
+ *((svbool_t*)(bitmask + 2 * sve_width / 8)) = pred2;
+ *((svbool_t*)(bitmask + 3 * sve_width / 8)) = pred3;
+ *((svbool_t*)(bitmask + 4 * sve_width / 8)) = pred4;
+ *((svbool_t*)(bitmask + 5 * sve_width / 8)) = pred5;
+ *((svbool_t*)(bitmask + 6 * sve_width / 8)) = pred6;
+ *((svbool_t*)(bitmask + 7 * sve_width / 8)) = pred7;
+ }
+
+ static inline void
+ write_partial(uint8_t* const __restrict bitmask,
+ const size_t size,
+ const svbool_t pred_0,
+ const svbool_t pred_1,
+ const svbool_t pred_2,
+ const svbool_t pred_3,
+ const svbool_t pred_4,
+ const svbool_t pred_5,
+ const svbool_t pred_6,
+ const svbool_t pred_7) {
+ const uint64_t sve_width = svcntb();
+
+ // perform a partial write
+
+ // this is a temporary buffer for the maximum possible case of 2048 bits
+ uint8_t pred_buf[MAX_SVE_WIDTH / 8];
+ // write to the temporary buffer
+ *((volatile svbool_t*)(pred_buf + 0 * sve_width / 8)) = pred_0;
+ *((volatile svbool_t*)(pred_buf + 1 * sve_width / 8)) = pred_1;
+ *((volatile svbool_t*)(pred_buf + 2 * sve_width / 8)) = pred_2;
+ *((volatile svbool_t*)(pred_buf + 3 * sve_width / 8)) = pred_3;
+ *((volatile svbool_t*)(pred_buf + 4 * sve_width / 8)) = pred_4;
+ *((volatile svbool_t*)(pred_buf + 5 * sve_width / 8)) = pred_5;
+ *((volatile svbool_t*)(pred_buf + 6 * sve_width / 8)) = pred_6;
+ *((volatile svbool_t*)(pred_buf + 7 * sve_width / 8)) = pred_7;
+
+ // make the write mask. (size % 8) == 0 is guaranteed by the caller.
+ const svbool_t pred_write =
+ svwhilelt_b8(uint32_t(0), uint32_t(size / 8));
+
+ // load the buffer
+ const svuint8_t mask_u8 = svld1_u8(pred_write, pred_buf);
+ // write it to the bitmask
+ svst1_u8(pred_write, bitmask, mask_u8);
}
};
template <>
struct MaskHelper<2> {
static inline void
- write(uint8_t* const __restrict bitmask,
- const size_t size,
- const svbool_t pred0,
- const svbool_t pred1,
- const svbool_t pred2,
- const svbool_t pred3,
- const svbool_t pred4,
- const svbool_t pred5,
- const svbool_t pred6,
- const svbool_t pred7) {
- const size_t sve_width = svcnth();
-
- // this is the buffer for the maximum possible case of 2048 bits
- uint8_t pred_buf[MAX_SVE_WIDTH / 8];
- *((volatile svbool_t*)(pred_buf + 0 * sve_width / 4)) = pred0;
- *((volatile svbool_t*)(pred_buf + 1 * sve_width / 4)) = pred1;
- *((volatile svbool_t*)(pred_buf + 2 * sve_width / 4)) = pred2;
- *((volatile svbool_t*)(pred_buf + 3 * sve_width / 4)) = pred3;
- *((volatile svbool_t*)(pred_buf + 4 * sve_width / 4)) = pred4;
- *((volatile svbool_t*)(pred_buf + 5 * sve_width / 4)) = pred5;
- *((volatile svbool_t*)(pred_buf + 6 * sve_width / 4)) = pred6;
- *((volatile svbool_t*)(pred_buf + 7 * sve_width / 4)) = pred7;
-
- const svbool_t pred_op_8 = get_pred_op_8(size / 4);
- const svbool_t pred_write_8 = get_pred_op_8(size / 8);
- write_bitmask_16_8x(bitmask, pred_op_8, pred_write_8, pred_buf);
+ write_full(uint8_t* const __restrict bitmask,
+ const svbool_t pred_0,
+ const svbool_t pred_1,
+ const svbool_t pred_2,
+ const svbool_t pred_3,
+ const svbool_t pred_4,
+ const svbool_t pred_5,
+ const svbool_t pred_6,
+ const svbool_t pred_7) {
+ const uint64_t sve_width = svcntb();
+
+ // compact predicates
+ const svbool_t pred_01 = svuzp1_b8(pred_0, pred_1);
+ const svbool_t pred_23 = svuzp1_b8(pred_2, pred_3);
+ const svbool_t pred_45 = svuzp1_b8(pred_4, pred_5);
+ const svbool_t pred_67 = svuzp1_b8(pred_6, pred_7);
+
+ // perform a full write
+ *((svbool_t*)(bitmask + 0 * sve_width / 8)) = pred_01;
+ *((svbool_t*)(bitmask + 1 * sve_width / 8)) = pred_23;
+ *((svbool_t*)(bitmask + 2 * sve_width / 8)) = pred_45;
+ *((svbool_t*)(bitmask + 3 * sve_width / 8)) = pred_67;
+ }
+
+ static inline void
+ write_partial(uint8_t* const __restrict bitmask,
+ const size_t size,
+ const svbool_t pred_0,
+ const svbool_t pred_1,
+ const svbool_t pred_2,
+ const svbool_t pred_3,
+ const svbool_t pred_4,
+ const svbool_t pred_5,
+ const svbool_t pred_6,
+ const svbool_t pred_7) {
+ const uint64_t sve_width = svcntb();
+
+ // compact predicates
+ const svbool_t pred_01 = svuzp1_b8(pred_0, pred_1);
+ const svbool_t pred_23 = svuzp1_b8(pred_2, pred_3);
+ const svbool_t pred_45 = svuzp1_b8(pred_4, pred_5);
+ const svbool_t pred_67 = svuzp1_b8(pred_6, pred_7);
+
+ // this is a temporary buffer for the maximum possible case of 1024 bits
+ uint8_t pred_buf[MAX_SVE_WIDTH / 16];
+ // write to the temporary buffer
+ *((volatile svbool_t*)(pred_buf + 0 * sve_width / 8)) = pred_01;
+ *((volatile svbool_t*)(pred_buf + 1 * sve_width / 8)) = pred_23;
+ *((volatile svbool_t*)(pred_buf + 2 * sve_width / 8)) = pred_45;
+ *((volatile svbool_t*)(pred_buf + 3 * sve_width / 8)) = pred_67;
+
+ // make the write mask. (size % 8) == 0 is guaranteed by the caller.
+ const svbool_t pred_write =
+ svwhilelt_b8(uint32_t(0), uint32_t(size / 8));
+
+ // load the buffer
+ const svuint8_t mask_u8 = svld1_u8(pred_write, pred_buf);
+ // write it to the bitmask
+ svst1_u8(pred_write, bitmask, mask_u8);
}
};
template <>
struct MaskHelper<4> {
static inline void
- write(uint8_t* const __restrict bitmask,
- const size_t size,
- const svbool_t pred0,
- const svbool_t pred1,
- const svbool_t pred2,
- const svbool_t pred3,
- const svbool_t pred4,
- const svbool_t pred5,
- const svbool_t pred6,
- const svbool_t pred7) {
- const size_t sve_width = svcntw();
-
- // this is the buffer for the maximum possible case of 2048 bits
- uint8_t pred_buf[MAX_SVE_WIDTH / 8];
- *((volatile svbool_t*)(pred_buf + 0 * sve_width / 2)) = pred0;
- *((volatile svbool_t*)(pred_buf + 1 * sve_width / 2)) = pred1;
- *((volatile svbool_t*)(pred_buf + 2 * sve_width / 2)) = pred2;
- *((volatile svbool_t*)(pred_buf + 3 * sve_width / 2)) = pred3;
- *((volatile svbool_t*)(pred_buf + 4 * sve_width / 2)) = pred4;
- *((volatile svbool_t*)(pred_buf + 5 * sve_width / 2)) = pred5;
- *((volatile svbool_t*)(pred_buf + 6 * sve_width / 2)) = pred6;
- *((volatile svbool_t*)(pred_buf + 7 * sve_width / 2)) = pred7;
-
- const svbool_t pred_op_8 = get_pred_op_8(size / 2);
- const svbool_t pred_write_8 = get_pred_op_8(size / 8);
- write_bitmask_32_8x(bitmask, pred_op_8, pred_write_8, pred_buf);
+ write_full(uint8_t* const __restrict bitmask,
+ const svbool_t pred_0,
+ const svbool_t pred_1,
+ const svbool_t pred_2,
+ const svbool_t pred_3,
+ const svbool_t pred_4,
+ const svbool_t pred_5,
+ const svbool_t pred_6,
+ const svbool_t pred_7) {
+ const uint64_t sve_width = svcntb();
+
+ // compact predicates
+ const svbool_t pred_01 = svuzp1_b16(pred_0, pred_1);
+ const svbool_t pred_23 = svuzp1_b16(pred_2, pred_3);
+ const svbool_t pred_45 = svuzp1_b16(pred_4, pred_5);
+ const svbool_t pred_67 = svuzp1_b16(pred_6, pred_7);
+ const svbool_t pred_0123 = svuzp1_b8(pred_01, pred_23);
+ const svbool_t pred_4567 = svuzp1_b8(pred_45, pred_67);
+
+ // perform a full write
+ *((svbool_t*)(bitmask + 0 * sve_width / 8)) = pred_0123;
+ *((svbool_t*)(bitmask + 1 * sve_width / 8)) = pred_4567;
+ }
+
+ static inline void
+ write_partial(uint8_t* const __restrict bitmask,
+ const size_t size,
+ const svbool_t pred_0,
+ const svbool_t pred_1,
+ const svbool_t pred_2,
+ const svbool_t pred_3,
+ const svbool_t pred_4,
+ const svbool_t pred_5,
+ const svbool_t pred_6,
+ const svbool_t pred_7) {
+ const uint64_t sve_width = svcntb();
+
+ // compact predicates
+ const svbool_t pred_01 = svuzp1_b16(pred_0, pred_1);
+ const svbool_t pred_23 = svuzp1_b16(pred_2, pred_3);
+ const svbool_t pred_45 = svuzp1_b16(pred_4, pred_5);
+ const svbool_t pred_67 = svuzp1_b16(pred_6, pred_7);
+ const svbool_t pred_0123 = svuzp1_b8(pred_01, pred_23);
+ const svbool_t pred_4567 = svuzp1_b8(pred_45, pred_67);
+
+ // this is a temporary buffer for the maximum possible case of 512 bits
+ uint8_t pred_buf[MAX_SVE_WIDTH / 32];
+ // write to the temporary buffer
+ *((volatile svbool_t*)(pred_buf + 0 * sve_width / 8)) = pred_0123;
+ *((volatile svbool_t*)(pred_buf + 1 * sve_width / 8)) = pred_4567;
+
+ // make the write mask. (size % 8) == 0 is guaranteed by the caller.
+ const svbool_t pred_write =
+ svwhilelt_b8(uint32_t(0), uint32_t(size / 8));
+
+ // load the buffer
+ const svuint8_t mask_u8 = svld1_u8(pred_write, pred_buf);
+ // write it to the bitmask
+ svst1_u8(pred_write, bitmask, mask_u8);
}
};
template <>
struct MaskHelper<8> {
static inline void
- write(uint8_t* const __restrict bitmask,
- const size_t size,
- const svbool_t pred0,
- const svbool_t pred1,
- const svbool_t pred2,
- const svbool_t pred3,
- const svbool_t pred4,
- const svbool_t pred5,
- const svbool_t pred6,
- const svbool_t pred7) {
- const size_t sve_width = svcntd();
-
- // this is the buffer for the maximum possible case of 2048 bits
- uint8_t pred_buf[MAX_SVE_WIDTH / 8];
- *((volatile svbool_t*)(pred_buf + 0 * sve_width)) = pred0;
- *((volatile svbool_t*)(pred_buf + 1 * sve_width)) = pred1;
- *((volatile svbool_t*)(pred_buf + 2 * sve_width)) = pred2;
- *((volatile svbool_t*)(pred_buf + 3 * sve_width)) = pred3;
- *((volatile svbool_t*)(pred_buf + 4 * sve_width)) = pred4;
- *((volatile svbool_t*)(pred_buf + 5 * sve_width)) = pred5;
- *((volatile svbool_t*)(pred_buf + 6 * sve_width)) = pred6;
- *((volatile svbool_t*)(pred_buf + 7 * sve_width)) = pred7;
-
- const svbool_t pred_op_8 = get_pred_op_8(size / 1);
- const svbool_t pred_write_8 = get_pred_op_8(size / 8);
- write_bitmask_64_8x(bitmask, pred_op_8, pred_write_8, pred_buf);
+ write_full(uint8_t* const __restrict bitmask,
+ const svbool_t pred_0,
+ const svbool_t pred_1,
+ const svbool_t pred_2,
+ const svbool_t pred_3,
+ const svbool_t pred_4,
+ const svbool_t pred_5,
+ const svbool_t pred_6,
+ const svbool_t pred_7) {
+ // compact predicates
+ const svbool_t pred_01 = svuzp1_b32(pred_0, pred_1);
+ const svbool_t pred_23 = svuzp1_b32(pred_2, pred_3);
+ const svbool_t pred_45 = svuzp1_b32(pred_4, pred_5);
+ const svbool_t pred_67 = svuzp1_b32(pred_6, pred_7);
+ const svbool_t pred_0123 = svuzp1_b16(pred_01, pred_23);
+ const svbool_t pred_4567 = svuzp1_b16(pred_45, pred_67);
+ const svbool_t pred_01234567 = svuzp1_b8(pred_0123, pred_4567);
+
+ // perform a full write
+ *((svbool_t*)bitmask) = pred_01234567;
+ }
+
+ static inline void
+ write_partial(uint8_t* const __restrict bitmask,
+ const size_t size,
+ const svbool_t pred_0,
+ const svbool_t pred_1,
+ const svbool_t pred_2,
+ const svbool_t pred_3,
+ const svbool_t pred_4,
+ const svbool_t pred_5,
+ const svbool_t pred_6,
+ const svbool_t pred_7) {
+ // compact predicates
+ const svbool_t pred_01 = svuzp1_b32(pred_0, pred_1);
+ const svbool_t pred_23 = svuzp1_b32(pred_2, pred_3);
+ const svbool_t pred_45 = svuzp1_b32(pred_4, pred_5);
+ const svbool_t pred_67 = svuzp1_b32(pred_6, pred_7);
+ const svbool_t pred_0123 = svuzp1_b16(pred_01, pred_23);
+ const svbool_t pred_4567 = svuzp1_b16(pred_45, pred_67);
+ const svbool_t pred_01234567 = svuzp1_b8(pred_0123, pred_4567);
+
+ // this is a temporary buffer for the maximum possible case of 256 bits
+ uint8_t pred_buf[MAX_SVE_WIDTH / 64];
+ // write to the temporary buffer
+ *((volatile svbool_t*)(pred_buf)) = pred_01234567;
+
+ // make the write mask. (size % 8) == 0 is guaranteed by the caller.
+ const svbool_t pred_write =
+ svwhilelt_b8(uint32_t(0), uint32_t(size / 8));
+
+ // load the buffer
+ const svuint8_t mask_u8 = svld1_u8(pred_write, pred_buf);
+ // write it to the bitmask
+ svst1_u8(pred_write, bitmask, mask_u8);
}
};
@@ -924,16 +819,8 @@ op_mask_helper(uint8_t* const __restrict res_u8, const size_t size, Func func) {
const svbool_t cmp6 = func(pred_all, i + 6 * sve_width);
const svbool_t cmp7 = func(pred_all, i + 7 * sve_width);
- MaskHelper::write(res_u8 + i / 8,
- sve_width * 8,
- cmp0,
- cmp1,
- cmp2,
- cmp3,
- cmp4,
- cmp5,
- cmp6,
- cmp7);
+ MaskHelper::write_full(
+ res_u8 + i / 8, cmp0, cmp1, cmp2, cmp3, cmp4, cmp5, cmp6, cmp7);
}
}
@@ -985,16 +872,16 @@ op_mask_helper(uint8_t* const __restrict res_u8, const size_t size, Func func) {
cmp7 = func(get_partial_pred(7), size_sve8 + 7 * sve_width);
}
- MaskHelper::write(res_u8 + size_sve8 / 8,
- size - size_sve8,
- cmp0,
- cmp1,
- cmp2,
- cmp3,
- cmp4,
- cmp5,
- cmp6,
- cmp7);
+ MaskHelper::write_partial(res_u8 + size_sve8 / 8,
+ size - size_sve8,
+ cmp0,
+ cmp1,
+ cmp2,
+ cmp3,
+ cmp4,
+ cmp5,
+ cmp6,
+ cmp7);
}
return true;
diff --git a/internal/core/src/common/Channel.h b/internal/core/src/common/Channel.h
index f042945432935..1dead8324791c 100644
--- a/internal/core/src/common/Channel.h
+++ b/internal/core/src/common/Channel.h
@@ -14,6 +14,7 @@
#include
#include
#include
+#include "Exception.h"
namespace milvus {
template
@@ -55,7 +56,7 @@ class Channel {
}
void
- close(std::optional ex = std::nullopt) {
+ close(std::optional ex = std::nullopt) {
if (ex.has_value()) {
ex_ = std::move(ex);
}
@@ -64,6 +65,6 @@ class Channel {
private:
oneapi::tbb::concurrent_bounded_queue> inner_{};
- std::optional ex_{};
+ std::optional ex_{};
};
} // namespace milvus
diff --git a/internal/core/src/common/Consts.h b/internal/core/src/common/Consts.h
index 65e6795b16e66..44d7d5559ca81 100644
--- a/internal/core/src/common/Consts.h
+++ b/internal/core/src/common/Consts.h
@@ -61,3 +61,5 @@ constexpr const char* RANGE_FILTER = knowhere::meta::RANGE_FILTER;
const int64_t DEFAULT_MAX_OUTPUT_SIZE = 67108864; // bytes, 64MB
const int64_t DEFAULT_CHUNK_MANAGER_REQUEST_TIMEOUT_MS = 10000;
+
+const int64_t DEFAULT_BITMAP_INDEX_CARDINALITY_BOUND = 500;
diff --git a/internal/core/src/common/Exception.h b/internal/core/src/common/Exception.h
index 68941ba56716c..d4f3863b9df25 100644
--- a/internal/core/src/common/Exception.h
+++ b/internal/core/src/common/Exception.h
@@ -20,6 +20,22 @@
namespace milvus {
+class MilvusException : public std::exception {
+ public:
+ explicit MilvusException(const std::string& msg)
+ : std::exception(), exception_message_(msg) {
+ }
+ const char*
+ what() const noexcept {
+ return exception_message_.c_str();
+ }
+ virtual ~MilvusException() {
+ }
+
+ private:
+ std::string exception_message_;
+};
+
class NotImplementedException : public std::exception {
public:
explicit NotImplementedException(const std::string& msg)
diff --git a/internal/core/src/common/QueryResult.h b/internal/core/src/common/QueryResult.h
index 9fd2d13d7776b..4cb7fef00e5dd 100644
--- a/internal/core/src/common/QueryResult.h
+++ b/internal/core/src/common/QueryResult.h
@@ -228,6 +228,7 @@ struct RetrieveResult {
void* segment_;
std::vector result_offsets_;
std::vector field_data_;
+ bool has_more_result = true;
};
using RetrieveResultPtr = std::shared_ptr;
diff --git a/internal/core/src/common/Tracer.cpp b/internal/core/src/common/Tracer.cpp
index 4711ef76ae3ef..d80dd301215e9 100644
--- a/internal/core/src/common/Tracer.cpp
+++ b/internal/core/src/common/Tracer.cpp
@@ -55,13 +55,13 @@ initTelemetry(const TraceConfig& cfg) {
opts.transport_format = jaeger::TransportFormat::kThriftHttp;
opts.endpoint = cfg.jaegerURL;
exporter = jaeger::JaegerExporterFactory::Create(opts);
- LOG_INFO("init jaeger exporter, endpoint:", opts.endpoint);
+ LOG_INFO("init jaeger exporter, endpoint: {}", opts.endpoint);
} else if (cfg.exporter == "otlp") {
auto opts = otlp::OtlpGrpcExporterOptions{};
opts.endpoint = cfg.otlpEndpoint;
opts.use_ssl_credentials = cfg.oltpSecure;
exporter = otlp::OtlpGrpcExporterFactory::Create(opts);
- LOG_INFO("init otlp exporter, endpoint:", opts.endpoint);
+ LOG_INFO("init otlp exporter, endpoint: {}", opts.endpoint);
} else {
LOG_INFO("Empty Trace");
enable_trace = false;
diff --git a/internal/core/src/config/ConfigKnowhere.h b/internal/core/src/config/ConfigKnowhere.h
index eff8be76f9c28..57a0713014d6b 100644
--- a/internal/core/src/config/ConfigKnowhere.h
+++ b/internal/core/src/config/ConfigKnowhere.h
@@ -15,6 +15,7 @@
// limitations under the License.
#pragma once
+#include
#include
namespace milvus::config {
diff --git a/internal/core/src/exec/expression/JsonContainsExpr.cpp b/internal/core/src/exec/expression/JsonContainsExpr.cpp
index 72251c301fb14..bbcc852c2a8e2 100644
--- a/internal/core/src/exec/expression/JsonContainsExpr.cpp
+++ b/internal/core/src/exec/expression/JsonContainsExpr.cpp
@@ -23,7 +23,14 @@ namespace exec {
void
PhyJsonContainsFilterExpr::Eval(EvalCtx& context, VectorPtr& result) {
switch (expr_->column_.data_type_) {
- case DataType::ARRAY:
+ case DataType::ARRAY: {
+ if (is_index_mode_) {
+ result = EvalArrayContainsForIndexSegment();
+ } else {
+ result = EvalJsonContainsForDataSegment();
+ }
+ break;
+ }
case DataType::JSON: {
if (is_index_mode_) {
PanicInfo(
@@ -94,7 +101,6 @@ PhyJsonContainsFilterExpr::EvalJsonContainsForDataSegment() {
return ExecJsonContainsWithDiffType();
}
}
- break;
}
case proto::plan::JSONContainsExpr_JSONOp_ContainsAll: {
if (IsArrayDataType(data_type)) {
@@ -145,7 +151,6 @@ PhyJsonContainsFilterExpr::EvalJsonContainsForDataSegment() {
return ExecJsonContainsAllWithDiffType();
}
}
- break;
}
default:
PanicInfo(ExprInvalid,
@@ -748,5 +753,92 @@ PhyJsonContainsFilterExpr::ExecJsonContainsWithDiffType() {
return res_vec;
}
+VectorPtr
+PhyJsonContainsFilterExpr::EvalArrayContainsForIndexSegment() {
+ switch (expr_->column_.element_type_) {
+ case DataType::BOOL: {
+ return ExecArrayContainsForIndexSegmentImpl();
+ }
+ case DataType::INT8: {
+ return ExecArrayContainsForIndexSegmentImpl();
+ }
+ case DataType::INT16: {
+ return ExecArrayContainsForIndexSegmentImpl();
+ }
+ case DataType::INT32: {
+ return ExecArrayContainsForIndexSegmentImpl();
+ }
+ case DataType::INT64: {
+ return ExecArrayContainsForIndexSegmentImpl();
+ }
+ case DataType::FLOAT: {
+ return ExecArrayContainsForIndexSegmentImpl();
+ }
+ case DataType::DOUBLE: {
+ return ExecArrayContainsForIndexSegmentImpl();
+ }
+ case DataType::VARCHAR:
+ case DataType::STRING: {
+ return ExecArrayContainsForIndexSegmentImpl();
+ }
+ default:
+ PanicInfo(DataTypeInvalid,
+ fmt::format("unsupported data type for "
+ "ExecArrayContainsForIndexSegmentImpl: {}",
+ expr_->column_.element_type_));
+ }
+}
+
+template
+VectorPtr
+PhyJsonContainsFilterExpr::ExecArrayContainsForIndexSegmentImpl() {
+ typedef std::conditional_t,
+ std::string,
+ ExprValueType>
+ GetType;
+ using Index = index::ScalarIndex;
+ auto real_batch_size = GetNextBatchSize();
+ if (real_batch_size == 0) {
+ return nullptr;
+ }
+
+ std::unordered_set elements;
+ for (auto const& element : expr_->vals_) {
+ elements.insert(GetValueFromProto(element));
+ }
+ boost::container::vector elems(elements.begin(), elements.end());
+ auto execute_sub_batch =
+ [this](Index* index_ptr,
+ const boost::container::vector& vals) {
+ switch (expr_->op_) {
+ case proto::plan::JSONContainsExpr_JSONOp_Contains:
+ case proto::plan::JSONContainsExpr_JSONOp_ContainsAny: {
+ return index_ptr->In(vals.size(), vals.data());
+ }
+ case proto::plan::JSONContainsExpr_JSONOp_ContainsAll: {
+ TargetBitmap result(index_ptr->Count());
+ result.set();
+ for (size_t i = 0; i < vals.size(); i++) {
+ auto sub = index_ptr->In(1, &vals[i]);
+ result &= sub;
+ }
+ return result;
+ }
+ default:
+ PanicInfo(
+ ExprInvalid,
+ "unsupported array contains type {}",
+ proto::plan::JSONContainsExpr_JSONOp_Name(expr_->op_));
+ }
+ };
+ auto res = ProcessIndexChunks(execute_sub_batch, elems);
+ AssertInfo(res.size() == real_batch_size,
+ "internal error: expr processed rows {} not equal "
+ "expect batch size {}",
+ res.size(),
+ real_batch_size);
+ return std::make_shared(std::move(res));
+}
+
} //namespace exec
} // namespace milvus
diff --git a/internal/core/src/exec/expression/JsonContainsExpr.h b/internal/core/src/exec/expression/JsonContainsExpr.h
index c757dc0d3fb92..a0cfdfdea0841 100644
--- a/internal/core/src/exec/expression/JsonContainsExpr.h
+++ b/internal/core/src/exec/expression/JsonContainsExpr.h
@@ -80,6 +80,13 @@ class PhyJsonContainsFilterExpr : public SegmentExpr {
VectorPtr
ExecJsonContainsWithDiffType();
+ VectorPtr
+ EvalArrayContainsForIndexSegment();
+
+ template
+ VectorPtr
+ ExecArrayContainsForIndexSegmentImpl();
+
private:
std::shared_ptr expr_;
};
diff --git a/internal/core/src/expr/ITypeExpr.h b/internal/core/src/expr/ITypeExpr.h
index 102709aa16b83..6716f8af2f66f 100644
--- a/internal/core/src/expr/ITypeExpr.h
+++ b/internal/core/src/expr/ITypeExpr.h
@@ -113,11 +113,13 @@ IsMaterializedViewSupported(const DataType& data_type) {
struct ColumnInfo {
FieldId field_id_;
DataType data_type_;
+ DataType element_type_;
std::vector nested_path_;
ColumnInfo(const proto::plan::ColumnInfo& column_info)
: field_id_(column_info.field_id()),
data_type_(static_cast(column_info.data_type())),
+ element_type_(static_cast(column_info.element_type())),
nested_path_(column_info.nested_path().begin(),
column_info.nested_path().end()) {
}
@@ -127,6 +129,7 @@ struct ColumnInfo {
std::vector nested_path = {})
: field_id_(field_id),
data_type_(data_type),
+ element_type_(DataType::NONE),
nested_path_(std::move(nested_path)) {
}
@@ -140,6 +143,10 @@ struct ColumnInfo {
return false;
}
+ if (element_type_ != other.element_type_) {
+ return false;
+ }
+
for (int i = 0; i < nested_path_.size(); ++i) {
if (nested_path_[i] != other.nested_path_[i]) {
return false;
@@ -151,10 +158,12 @@ struct ColumnInfo {
std::string
ToString() const {
- return fmt::format("[FieldId:{}, data_type:{}, nested_path:{}]",
- std::to_string(field_id_.get()),
- data_type_,
- milvus::Join(nested_path_, ","));
+ return fmt::format(
+ "[FieldId:{}, data_type:{}, element_type:{}, nested_path:{}]",
+ std::to_string(field_id_.get()),
+ data_type_,
+ element_type_,
+ milvus::Join(nested_path_, ","));
}
};
diff --git a/internal/core/src/index/BitmapIndex.cpp b/internal/core/src/index/BitmapIndex.cpp
index 5d0a4aabec3cd..3e63763dd2b51 100644
--- a/internal/core/src/index/BitmapIndex.cpp
+++ b/internal/core/src/index/BitmapIndex.cpp
@@ -15,10 +15,12 @@
// limitations under the License.
#include
+#include
#include "index/BitmapIndex.h"
#include "common/Slice.h"
+#include "common/Common.h"
#include "index/Meta.h"
#include "index/ScalarIndex.h"
#include "index/Utils.h"
@@ -105,8 +107,13 @@ BitmapIndex::Build(size_t n, const T* data) {
}
total_num_rows_ = n;
- for (auto it = data_.begin(); it != data_.end(); ++it) {
- bitsets_[it->first] = ConvertRoaringToBitset(it->second);
+ if (data_.size() < DEFAULT_BITMAP_INDEX_CARDINALITY_BOUND) {
+ for (auto it = data_.begin(); it != data_.end(); ++it) {
+ bitsets_[it->first] = ConvertRoaringToBitset(it->second);
+ }
+ build_mode_ = BitmapIndexBuildMode::BITSET;
+ } else {
+ build_mode_ = BitmapIndexBuildMode::ROARING;
}
is_built_ = true;
@@ -134,6 +141,13 @@ BitmapIndex::BuildV2(const Config& config) {
field_datas.push_back(field_data);
}
+ BuildWithFieldData(field_datas);
+}
+
+template
+void
+BitmapIndex::BuildWithFieldData(
+ const std::vector& field_datas) {
int total_num_rows = 0;
for (auto& field_data : field_datas) {
total_num_rows += field_data->get_num_rows();
@@ -142,7 +156,6 @@ BitmapIndex::BuildV2(const Config& config) {
throw SegcoreError(DataIsEmpty,
"scalar bitmap index can not build null values");
}
-
total_num_rows_ = total_num_rows;
int64_t offset = 0;
@@ -154,6 +167,7 @@ BitmapIndex::BuildV2(const Config& config) {
offset++;
}
}
+
is_built_ = true;
}
@@ -190,6 +204,22 @@ BitmapIndex::SerializeIndexData(uint8_t* data_ptr) {
}
}
+template
+std::pair, size_t>
+BitmapIndex::SerializeIndexMeta() {
+ YAML::Node node;
+ node[BITMAP_INDEX_LENGTH] = data_.size();
+ node[BITMAP_INDEX_NUM_ROWS] = total_num_rows_;
+
+ std::stringstream ss;
+ ss << node;
+ auto json_string = ss.str();
+ auto str_size = json_string.size();
+ std::shared_ptr res(new uint8_t[str_size]);
+ memcpy(res.get(), json_string.data(), str_size);
+ return std::make_pair(res, str_size);
+}
+
template <>
void
BitmapIndex::SerializeIndexData(uint8_t* data_ptr) {
@@ -217,21 +247,17 @@ BitmapIndex::Serialize(const Config& config) {
uint8_t* data_ptr = index_data.get();
SerializeIndexData(data_ptr);
- std::shared_ptr index_length(new uint8_t[sizeof(size_t)]);
- auto index_size = data_.size();
- memcpy(index_length.get(), &index_size, sizeof(size_t));
-
- std::shared_ptr num_rows(new uint8_t[sizeof(size_t)]);
- memcpy(num_rows.get(), &total_num_rows_, sizeof(size_t));
+ auto index_meta = SerializeIndexMeta();
BinarySet ret_set;
ret_set.Append(BITMAP_INDEX_DATA, index_data, index_data_size);
- ret_set.Append(BITMAP_INDEX_LENGTH, index_length, sizeof(size_t));
- ret_set.Append(BITMAP_INDEX_NUM_ROWS, num_rows, sizeof(size_t));
+ ret_set.Append(BITMAP_INDEX_META, index_meta.first, index_meta.second);
LOG_INFO("build bitmap index with cardinality = {}, num_rows = {}",
- index_size,
+ Cardinality(),
total_num_rows_);
+
+ Disassemble(ret_set);
return ret_set;
}
@@ -283,6 +309,29 @@ BitmapIndex::ConvertRoaringToBitset(const roaring::Roaring& values) {
return res;
}
+template
+std::pair
+BitmapIndex::DeserializeIndexMeta(const uint8_t* data_ptr,
+ size_t data_size) {
+ YAML::Node node = YAML::Load(
+ std::string(reinterpret_cast(data_ptr), data_size));
+
+ auto index_length = node[BITMAP_INDEX_LENGTH].as();
+ auto index_num_rows = node[BITMAP_INDEX_NUM_ROWS].as();
+
+ return std::make_pair(index_length, index_num_rows);
+}
+
+template
+void
+BitmapIndex::ChooseIndexBuildMode() {
+ if (data_.size() <= DEFAULT_BITMAP_INDEX_CARDINALITY_BOUND) {
+ build_mode_ = BitmapIndexBuildMode::BITSET;
+ } else {
+ build_mode_ = BitmapIndexBuildMode::ROARING;
+ }
+}
+
template
void
BitmapIndex::DeserializeIndexData(const uint8_t* data_ptr,
@@ -296,7 +345,12 @@ BitmapIndex::DeserializeIndexData(const uint8_t* data_ptr,
value = roaring::Roaring::read(reinterpret_cast(data_ptr));
data_ptr += value.getSizeInBytes();
- bitsets_[key] = ConvertRoaringToBitset(value);
+ ChooseIndexBuildMode();
+
+ if (build_mode_ == BitmapIndexBuildMode::BITSET) {
+ bitsets_[key] = ConvertRoaringToBitset(value);
+ data_.erase(key);
+ }
}
}
@@ -324,21 +378,14 @@ template
void
BitmapIndex::LoadWithoutAssemble(const BinarySet& binary_set,
const Config& config) {
- size_t index_length;
- auto index_length_buffer = binary_set.GetByName(BITMAP_INDEX_LENGTH);
- memcpy(&index_length,
- index_length_buffer->data.get(),
- (size_t)index_length_buffer->size);
-
- auto num_rows_buffer = binary_set.GetByName(BITMAP_INDEX_NUM_ROWS);
- memcpy(&total_num_rows_,
- num_rows_buffer->data.get(),
- (size_t)num_rows_buffer->size);
+ auto index_meta_buffer = binary_set.GetByName(BITMAP_INDEX_META);
+ auto index_meta = DeserializeIndexMeta(index_meta_buffer->data.get(),
+ index_meta_buffer->size);
+ auto index_length = index_meta.first;
+ total_num_rows_ = index_meta.second;
auto index_data_buffer = binary_set.GetByName(BITMAP_INDEX_DATA);
- const uint8_t* data_ptr = index_data_buffer->data.get();
-
- DeserializeIndexData(data_ptr, index_length);
+ DeserializeIndexData(index_data_buffer->data.get(), index_length);
LOG_INFO("load bitmap index with cardinality = {}, num_rows = {}",
Cardinality(),
@@ -416,26 +463,24 @@ BitmapIndex::In(const size_t n, const T* values) {
AssertInfo(is_built_, "index has not been built");
TargetBitmap res(total_num_rows_, false);
-#if 0
- roaring::Roaring result;
- for (size_t i = 0; i < n; ++i) {
- auto val = values[i];
- auto it = data_.find(val);
- if (it != data_.end()) {
- result |= it->second;
+ if (build_mode_ == BitmapIndexBuildMode::ROARING) {
+ for (size_t i = 0; i < n; ++i) {
+ auto val = values[i];
+ auto it = data_.find(val);
+ if (it != data_.end()) {
+ for (const auto& v : it->second) {
+ res.set(v);
+ }
+ }
}
- }
- for (auto& val : result) {
- res.set(val);
- }
-#else
- for (size_t i = 0; i < n; ++i) {
- auto val = values[i];
- if (bitsets_.find(val) != bitsets_.end()) {
- res |= bitsets_.at(val);
+ } else {
+ for (size_t i = 0; i < n; ++i) {
+ auto val = values[i];
+ if (bitsets_.find(val) != bitsets_.end()) {
+ res |= bitsets_.at(val);
+ }
}
}
-#endif
return res;
}
@@ -443,36 +488,35 @@ template
const TargetBitmap
BitmapIndex::NotIn(const size_t n, const T* values) {
AssertInfo(is_built_, "index has not been built");
- TargetBitmap res(total_num_rows_, false);
-#if 0
- roaring::Roaring result;
- for (int i = 0; i < n; ++i) {
- auto val = values[i];
- auto it = data_.find(val);
- if (it != data_.end()) {
- result |= it->second;
+ if (build_mode_ == BitmapIndexBuildMode::ROARING) {
+ TargetBitmap res(total_num_rows_, true);
+ for (int i = 0; i < n; ++i) {
+ auto val = values[i];
+ auto it = data_.find(val);
+ if (it != data_.end()) {
+ for (const auto& v : it->second) {
+ res.reset(v);
+ }
+ }
}
- }
-
- for (auto& val : result) {
- bitset.reset(val);
- }
-#else
- for (size_t i = 0; i < n; ++i) {
- auto val = values[i];
- if (bitsets_.find(val) != bitsets_.end()) {
- res |= bitsets_.at(val);
+ return res;
+ } else {
+ TargetBitmap res(total_num_rows_, false);
+ for (size_t i = 0; i < n; ++i) {
+ auto val = values[i];
+ if (bitsets_.find(val) != bitsets_.end()) {
+ res |= bitsets_.at(val);
+ }
}
+ res.flip();
+ return res;
}
-#endif
- res.flip();
- return res;
}
template
-const TargetBitmap
-BitmapIndex::Range(const T value, const OpType op) {
+TargetBitmap
+BitmapIndex::RangeForBitset(const T value, const OpType op) {
AssertInfo(is_built_, "index has not been built");
TargetBitmap res(total_num_rows_, false);
if (ShouldSkip(value, value, op)) {
@@ -532,10 +576,82 @@ BitmapIndex::Range(const T value, const OpType op) {
template
const TargetBitmap
-BitmapIndex::Range(const T lower_value,
- bool lb_inclusive,
- const T upper_value,
- bool ub_inclusive) {
+BitmapIndex::Range(const T value, OpType op) {
+ if (build_mode_ == BitmapIndexBuildMode::ROARING) {
+ return std::move(RangeForRoaring(value, op));
+ } else {
+ return std::move(RangeForBitset(value, op));
+ }
+}
+
+template
+TargetBitmap
+BitmapIndex::RangeForRoaring(const T value, const OpType op) {
+ AssertInfo(is_built_, "index has not been built");
+ TargetBitmap res(total_num_rows_, false);
+ if (ShouldSkip(value, value, op)) {
+ return res;
+ }
+ auto lb = data_.begin();
+ auto ub = data_.end();
+
+ switch (op) {
+ case OpType::LessThan: {
+ ub = std::lower_bound(data_.begin(),
+ data_.end(),
+ std::make_pair(value, TargetBitmap()),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.first < rhs.first;
+ });
+ break;
+ }
+ case OpType::LessEqual: {
+ ub = std::upper_bound(data_.begin(),
+ data_.end(),
+ std::make_pair(value, TargetBitmap()),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.first < rhs.first;
+ });
+ break;
+ }
+ case OpType::GreaterThan: {
+ lb = std::upper_bound(data_.begin(),
+ data_.end(),
+ std::make_pair(value, TargetBitmap()),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.first < rhs.first;
+ });
+ break;
+ }
+ case OpType::GreaterEqual: {
+ lb = std::lower_bound(data_.begin(),
+ data_.end(),
+ std::make_pair(value, TargetBitmap()),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.first < rhs.first;
+ });
+ break;
+ }
+ default: {
+ throw SegcoreError(OpTypeInvalid,
+ fmt::format("Invalid OperatorType: {}", op));
+ }
+ }
+
+ for (; lb != ub; lb++) {
+ for (const auto& v : lb->second) {
+ res.set(v);
+ }
+ }
+ return res;
+}
+
+template
+TargetBitmap
+BitmapIndex::RangeForBitset(const T lower_value,
+ bool lb_inclusive,
+ const T upper_value,
+ bool ub_inclusive) {
AssertInfo(is_built_, "index has not been built");
TargetBitmap res(total_num_rows_, false);
if (lower_value > upper_value ||
@@ -587,15 +703,99 @@ BitmapIndex::Range(const T lower_value,
return res;
}
+template
+const TargetBitmap
+BitmapIndex::Range(const T lower_value,
+ bool lb_inclusive,
+ const T upper_value,
+ bool ub_inclusive) {
+ if (build_mode_ == BitmapIndexBuildMode::ROARING) {
+ return RangeForRoaring(
+ lower_value, lb_inclusive, upper_value, ub_inclusive);
+ } else {
+ return RangeForBitset(
+ lower_value, lb_inclusive, upper_value, ub_inclusive);
+ }
+}
+
+template
+TargetBitmap
+BitmapIndex::RangeForRoaring(const T lower_value,
+ bool lb_inclusive,
+ const T upper_value,
+ bool ub_inclusive) {
+ AssertInfo(is_built_, "index has not been built");
+ TargetBitmap res(total_num_rows_, false);
+ if (lower_value > upper_value ||
+ (lower_value == upper_value && !(lb_inclusive && ub_inclusive))) {
+ return res;
+ }
+ if (ShouldSkip(lower_value, upper_value, OpType::Range)) {
+ return res;
+ }
+
+ auto lb = data_.begin();
+ auto ub = data_.end();
+
+ if (lb_inclusive) {
+ lb = std::lower_bound(data_.begin(),
+ data_.end(),
+ std::make_pair(lower_value, TargetBitmap()),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.first < rhs.first;
+ });
+ } else {
+ lb = std::upper_bound(data_.begin(),
+ data_.end(),
+ std::make_pair(lower_value, TargetBitmap()),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.first < rhs.first;
+ });
+ }
+
+ if (ub_inclusive) {
+ ub = std::upper_bound(data_.begin(),
+ data_.end(),
+ std::make_pair(upper_value, TargetBitmap()),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.first < rhs.first;
+ });
+ } else {
+ ub = std::lower_bound(data_.begin(),
+ data_.end(),
+ std::make_pair(upper_value, TargetBitmap()),
+ [](const auto& lhs, const auto& rhs) {
+ return lhs.first < rhs.first;
+ });
+ }
+
+ for (; lb != ub; lb++) {
+ for (const auto& v : lb->second) {
+ res.set(v);
+ }
+ }
+ return res;
+}
+
template
T
BitmapIndex::Reverse_Lookup(size_t idx) const {
AssertInfo(is_built_, "index has not been built");
AssertInfo(idx < total_num_rows_, "out of range of total coun");
- for (auto it = bitsets_.begin(); it != bitsets_.end(); it++) {
- if (it->second[idx]) {
- return it->first;
+ if (build_mode_ == BitmapIndexBuildMode::ROARING) {
+ for (auto it = data_.begin(); it != data_.end(); it++) {
+ for (const auto& v : it->second) {
+ if (v == idx) {
+ return it->first;
+ }
+ }
+ }
+ } else {
+ for (auto it = bitsets_.begin(); it != bitsets_.end(); it++) {
+ if (it->second[idx]) {
+ return it->first;
+ }
}
}
throw SegcoreError(
@@ -610,9 +810,7 @@ bool
BitmapIndex::ShouldSkip(const T lower_value,
const T upper_value,
const OpType op) {
- if (!bitsets_.empty()) {
- auto lower_bound = bitsets_.begin()->first;
- auto upper_bound = bitsets_.rbegin()->first;
+ auto skip = [&](OpType op, T lower_bound, T upper_bound) -> bool {
bool should_skip = false;
switch (op) {
case OpType::LessThan: {
@@ -649,6 +847,22 @@ BitmapIndex::ShouldSkip(const T lower_value,
op));
}
return should_skip;
+ };
+
+ if (build_mode_ == BitmapIndexBuildMode::ROARING) {
+ if (!data_.empty()) {
+ auto lower_bound = data_.begin()->first;
+ auto upper_bound = data_.rbegin()->first;
+ bool should_skip = skip(op, lower_bound, upper_bound);
+ return should_skip;
+ }
+ } else {
+ if (!bitsets_.empty()) {
+ auto lower_bound = bitsets_.begin()->first;
+ auto upper_bound = bitsets_.rbegin()->first;
+ bool should_skip = skip(op, lower_bound, upper_bound);
+ return should_skip;
+ }
}
return true;
}
diff --git a/internal/core/src/index/BitmapIndex.h b/internal/core/src/index/BitmapIndex.h
index 38ea6004495ff..2ead42d5de545 100644
--- a/internal/core/src/index/BitmapIndex.h
+++ b/internal/core/src/index/BitmapIndex.h
@@ -30,6 +30,11 @@
namespace milvus {
namespace index {
+enum class BitmapIndexBuildMode {
+ ROARING,
+ BITSET,
+};
+
/*
* @brief Implementation of Bitmap Index
* @details This index only for scalar Integral type.
@@ -45,6 +50,17 @@ class BitmapIndex : public ScalarIndex {
const storage::FileManagerContext& file_manager_context,
std::shared_ptr space);
+ explicit BitmapIndex(
+ const std::shared_ptr& file_manager)
+ : file_manager_(file_manager) {
+ }
+
+ explicit BitmapIndex(
+ const std::shared_ptr& file_manager,
+ std::shared_ptr space)
+ : file_manager_(file_manager), space_(space) {
+ }
+
~BitmapIndex() override = default;
BinarySet
@@ -61,7 +77,7 @@ class BitmapIndex : public ScalarIndex {
int64_t
Count() override {
- return bitsets_.begin()->second.size();
+ return total_num_rows_;
}
void
@@ -70,6 +86,9 @@ class BitmapIndex : public ScalarIndex {
void
Build(const Config& config = {}) override;
+ void
+ BuildWithFieldData(const std::vector& datas) override;
+
void
BuildV2(const Config& config = {}) override;
@@ -108,9 +127,17 @@ class BitmapIndex : public ScalarIndex {
int64_t
Cardinality() {
- return bitsets_.size();
+ if (build_mode_ == BitmapIndexBuildMode::ROARING) {
+ return data_.size();
+ } else {
+ return bitsets_.size();
+ }
}
+ void
+ LoadWithoutAssemble(const BinarySet& binary_set,
+ const Config& config) override;
+
private:
size_t
GetIndexDataSize();
@@ -118,24 +145,49 @@ class BitmapIndex : public ScalarIndex {
void
SerializeIndexData(uint8_t* index_data_ptr);
+ std::pair, size_t>
+ SerializeIndexMeta();
+
+ std::pair
+ DeserializeIndexMeta(const uint8_t* data_ptr, size_t data_size);
+
void
DeserializeIndexData(const uint8_t* data_ptr, size_t index_length);
+ void
+ ChooseIndexBuildMode();
+
bool
ShouldSkip(const T lower_value, const T upper_value, const OpType op);
TargetBitmap
ConvertRoaringToBitset(const roaring::Roaring& values);
- void
- LoadWithoutAssemble(const BinarySet& binary_set, const Config& config);
+ TargetBitmap
+ RangeForRoaring(T value, OpType op);
- private:
- bool is_built_;
+ TargetBitmap
+ RangeForBitset(T value, OpType op);
+
+ TargetBitmap
+ RangeForRoaring(T lower_bound_value,
+ bool lb_inclusive,
+ T upper_bound_value,
+ bool ub_inclusive);
+
+ TargetBitmap
+ RangeForBitset(T lower_bound_value,
+ bool lb_inclusive,
+ T upper_bound_value,
+ bool ub_inclusive);
+
+ public:
+ bool is_built_{false};
Config config_;
+ BitmapIndexBuildMode build_mode_;
std::map data_;
std::map bitsets_;
- size_t total_num_rows_;
+ size_t total_num_rows_{0};
std::shared_ptr file_manager_;
std::shared_ptr space_;
};
diff --git a/internal/core/src/index/CMakeLists.txt b/internal/core/src/index/CMakeLists.txt
index ed0f600587bd2..3256ab63a08c7 100644
--- a/internal/core/src/index/CMakeLists.txt
+++ b/internal/core/src/index/CMakeLists.txt
@@ -20,6 +20,7 @@ set(INDEX_FILES
SkipIndex.cpp
InvertedIndexTantivy.cpp
BitmapIndex.cpp
+ HybridScalarIndex.cpp
)
milvus_add_pkg_config("milvus_index")
diff --git a/internal/core/src/index/HybridScalarIndex.cpp b/internal/core/src/index/HybridScalarIndex.cpp
new file mode 100644
index 0000000000000..518828ea7bac7
--- /dev/null
+++ b/internal/core/src/index/HybridScalarIndex.cpp
@@ -0,0 +1,402 @@
+// Licensed to the LF AI & Data foundation 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.
+
+#include
+
+#include "index/HybridScalarIndex.h"
+#include "common/Slice.h"
+#include "common/Common.h"
+#include "index/Meta.h"
+#include "index/ScalarIndex.h"
+#include "index/Utils.h"
+#include "storage/Util.h"
+#include "storage/space.h"
+
+namespace milvus {
+namespace index {
+
+template
+HybridScalarIndex::HybridScalarIndex(
+ const storage::FileManagerContext& file_manager_context)
+ : is_built_(false),
+ bitmap_index_cardinality_limit_(DEFAULT_BITMAP_INDEX_CARDINALITY_BOUND) {
+ if (file_manager_context.Valid()) {
+ file_manager_ =
+ std::make_shared(file_manager_context);
+ AssertInfo(file_manager_ != nullptr, "create file manager failed!");
+ }
+ internal_index_type_ = InternalIndexType::NONE;
+}
+
+template
+HybridScalarIndex::HybridScalarIndex(
+ const storage::FileManagerContext& file_manager_context,
+ std::shared_ptr space)
+ : is_built_(false),
+ bitmap_index_cardinality_limit_(DEFAULT_BITMAP_INDEX_CARDINALITY_BOUND),
+ space_(space) {
+ if (file_manager_context.Valid()) {
+ file_manager_ = std::make_shared(
+ file_manager_context, space);
+ AssertInfo(file_manager_ != nullptr, "create file manager failed!");
+ }
+ internal_index_type_ = InternalIndexType::NONE;
+}
+
+template
+InternalIndexType
+HybridScalarIndex::SelectIndexBuildType(size_t n, const T* values) {
+ std::set distinct_vals;
+ for (size_t i = 0; i < n; i++) {
+ distinct_vals.insert(values[i]);
+ }
+
+ // Decide whether to select bitmap index or stl sort
+ if (distinct_vals.size() >= bitmap_index_cardinality_limit_) {
+ internal_index_type_ = InternalIndexType::STLSORT;
+ } else {
+ internal_index_type_ = InternalIndexType::BITMAP;
+ }
+ return internal_index_type_;
+}
+
+template <>
+InternalIndexType
+HybridScalarIndex::SelectIndexBuildType(
+ size_t n, const std::string* values) {
+ std::set distinct_vals;
+ for (size_t i = 0; i < n; i++) {
+ distinct_vals.insert(values[i]);
+ if (distinct_vals.size() >= bitmap_index_cardinality_limit_) {
+ break;
+ }
+ }
+
+ // Decide whether to select bitmap index or marisa index
+ if (distinct_vals.size() >= bitmap_index_cardinality_limit_) {
+ internal_index_type_ = InternalIndexType::MARISA;
+ } else {
+ internal_index_type_ = InternalIndexType::BITMAP;
+ }
+ return internal_index_type_;
+}
+
+template
+InternalIndexType
+HybridScalarIndex::SelectIndexBuildType(
+ const std::vector& field_datas) {
+ std::set distinct_vals;
+ for (const auto& data : field_datas) {
+ auto slice_row_num = data->get_num_rows();
+ for (size_t i = 0; i < slice_row_num; ++i) {
+ auto val = reinterpret_cast(data->RawValue(i));
+ distinct_vals.insert(*val);
+ if (distinct_vals.size() >= bitmap_index_cardinality_limit_) {
+ break;
+ }
+ }
+ }
+
+ // Decide whether to select bitmap index or stl sort
+ if (distinct_vals.size() >= bitmap_index_cardinality_limit_) {
+ internal_index_type_ = InternalIndexType::STLSORT;
+ } else {
+ internal_index_type_ = InternalIndexType::BITMAP;
+ }
+ return internal_index_type_;
+}
+
+template <>
+InternalIndexType
+HybridScalarIndex::SelectIndexBuildType(
+ const std::vector& field_datas) {
+ std::set distinct_vals;
+ for (const auto& data : field_datas) {
+ auto slice_row_num = data->get_num_rows();
+ for (size_t i = 0; i < slice_row_num; ++i) {
+ auto val = reinterpret_cast(data->RawValue(i));
+ distinct_vals.insert(*val);
+ if (distinct_vals.size() >= bitmap_index_cardinality_limit_) {
+ break;
+ }
+ }
+ }
+
+ // Decide whether to select bitmap index or marisa sort
+ if (distinct_vals.size() >= bitmap_index_cardinality_limit_) {
+ internal_index_type_ = InternalIndexType::MARISA;
+ } else {
+ internal_index_type_ = InternalIndexType::BITMAP;
+ }
+ return internal_index_type_;
+}
+
+template
+std::shared_ptr>
+HybridScalarIndex::GetInternalIndex() {
+ if (internal_index_ != nullptr) {
+ return internal_index_;
+ }
+ if (internal_index_type_ == InternalIndexType::BITMAP) {
+ internal_index_ = std::make_shared>(file_manager_);
+ } else if (internal_index_type_ == InternalIndexType::STLSORT) {
+ internal_index_ = std::make_shared>(file_manager_);
+ } else {
+ PanicInfo(UnexpectedError,
+ "unknown index type when get internal index");
+ }
+ return internal_index_;
+}
+
+template <>
+std::shared_ptr>
+HybridScalarIndex::GetInternalIndex() {
+ if (internal_index_ != nullptr) {
+ return internal_index_;
+ }
+
+ if (internal_index_type_ == InternalIndexType::BITMAP) {
+ internal_index_ =
+ std::make_shared>(file_manager_);
+ } else if (internal_index_type_ == InternalIndexType::MARISA) {
+ internal_index_ = std::make_shared(file_manager_);
+ } else {
+ PanicInfo(UnexpectedError,
+ "unknown index type when get internal index");
+ }
+ return internal_index_;
+}
+
+template
+void
+HybridScalarIndex::BuildInternal(
+ const std::vector& field_datas) {
+ auto index = GetInternalIndex();
+ index->BuildWithFieldData(field_datas);
+}
+
+template
+void
+HybridScalarIndex::Build(const Config& config) {
+ if (is_built_) {
+ return;
+ }
+
+ bitmap_index_cardinality_limit_ =
+ GetBitmapCardinalityLimitFromConfig(config);
+ LOG_INFO("config bitmap cardinality limit to {}",
+ bitmap_index_cardinality_limit_);
+
+ auto insert_files =
+ GetValueFromConfig>(config, "insert_files");
+ AssertInfo(insert_files.has_value(),
+ "insert file paths is empty when build index");
+
+ auto field_datas =
+ file_manager_->CacheRawDataToMemory(insert_files.value());
+
+ SelectIndexBuildType(field_datas);
+ BuildInternal(field_datas);
+ is_built_ = true;
+}
+
+template
+void
+HybridScalarIndex::BuildV2(const Config& config) {
+ if (is_built_) {
+ return;
+ }
+ bitmap_index_cardinality_limit_ =
+ GetBitmapCardinalityLimitFromConfig(config);
+ LOG_INFO("config bitmap cardinality limit to {}",
+ bitmap_index_cardinality_limit_);
+
+ auto field_name = file_manager_->GetIndexMeta().field_name;
+ auto reader = space_->ScanData();
+ std::vector field_datas;
+ for (auto rec = reader->Next(); rec != nullptr; rec = reader->Next()) {
+ if (!rec.ok()) {
+ PanicInfo(DataFormatBroken, "failed to read data");
+ }
+ auto data = rec.ValueUnsafe();
+ auto total_num_rows = data->num_rows();
+ auto col_data = data->GetColumnByName(field_name);
+ auto field_data = storage::CreateFieldData(
+ DataType(GetDType()), 0, total_num_rows);
+ field_data->FillFieldData(col_data);
+ field_datas.push_back(field_data);
+ }
+
+ SelectIndexBuildType(field_datas);
+ BuildInternal(field_datas);
+ is_built_ = true;
+}
+
+template
+BinarySet
+HybridScalarIndex::Serialize(const Config& config) {
+ AssertInfo(is_built_, "index has not been built yet");
+
+ auto ret_set = internal_index_->Serialize(config);
+
+ // Add index type info to storage for future restruct index
+ std::shared_ptr index_type_buf(new uint8_t[sizeof(uint8_t)]);
+ index_type_buf[0] = static_cast(internal_index_type_);
+ ret_set.Append(INDEX_TYPE, index_type_buf, sizeof(uint8_t));
+
+ return ret_set;
+}
+
+template
+BinarySet
+HybridScalarIndex::Upload(const Config& config) {
+ auto binary_set = Serialize(config);
+ file_manager_->AddFile(binary_set);
+
+ auto remote_paths_to_size = file_manager_->GetRemotePathsToFileSize();
+ BinarySet ret;
+ for (auto& file : remote_paths_to_size) {
+ ret.Append(file.first, nullptr, file.second);
+ }
+
+ return ret;
+}
+
+template
+BinarySet
+HybridScalarIndex::UploadV2(const Config& config) {
+ auto binary_set = Serialize(config);
+ file_manager_->AddFileV2(binary_set);
+
+ auto remote_paths_to_size = file_manager_->GetRemotePathsToFileSize();
+ BinarySet ret;
+ for (auto& file : remote_paths_to_size) {
+ ret.Append(file.first, nullptr, file.second);
+ }
+
+ return ret;
+}
+
+template
+void
+HybridScalarIndex::DeserializeIndexType(const BinarySet& binary_set) {
+ uint8_t index_type;
+ auto index_type_buffer = binary_set.GetByName(INDEX_TYPE);
+ memcpy(&index_type, index_type_buffer->data.get(), index_type_buffer->size);
+ internal_index_type_ = static_cast(index_type);
+}
+
+template
+void
+HybridScalarIndex::LoadInternal(const BinarySet& binary_set,
+ const Config& config) {
+ auto index = GetInternalIndex();
+ index->LoadWithoutAssemble(binary_set, config);
+}
+
+template
+void
+HybridScalarIndex::Load(const BinarySet& binary_set, const Config& config) {
+ milvus::Assemble(const_cast(binary_set));
+ DeserializeIndexType(binary_set);
+
+ LoadInternal(binary_set, config);
+ is_built_ = true;
+}
+
+template
+void
+HybridScalarIndex::LoadV2(const Config& config) {
+ auto blobs = space_->StatisticsBlobs();
+ std::vector index_files;
+ auto prefix = file_manager_->GetRemoteIndexObjectPrefixV2();
+ for (auto& b : blobs) {
+ if (b.name.rfind(prefix, 0) == 0) {
+ index_files.push_back(b.name);
+ }
+ }
+ std::map index_datas{};
+ for (auto& file_name : index_files) {
+ auto res = space_->GetBlobByteSize(file_name);
+ if (!res.ok()) {
+ PanicInfo(S3Error, "unable to read index blob");
+ }
+ auto index_blob_data =
+ std::shared_ptr(new uint8_t[res.value()]);
+ auto status = space_->ReadBlob(file_name, index_blob_data.get());
+ if (!status.ok()) {
+ PanicInfo(S3Error, "unable to read index blob");
+ }
+ auto raw_index_blob =
+ storage::DeserializeFileData(index_blob_data, res.value());
+ auto key = file_name.substr(file_name.find_last_of('/') + 1);
+ index_datas[key] = raw_index_blob->GetFieldData();
+ }
+ AssembleIndexDatas(index_datas);
+
+ BinarySet binary_set;
+ for (auto& [key, data] : index_datas) {
+ auto size = data->Size();
+ auto deleter = [&](uint8_t*) {}; // avoid repeated deconstruction
+ auto buf = std::shared_ptr(
+ (uint8_t*)const_cast(data->Data()), deleter);
+ binary_set.Append(key, buf, size);
+ }
+
+ DeserializeIndexType(binary_set);
+
+ LoadInternal(binary_set, config);
+
+ is_built_ = true;
+}
+
+template
+void
+HybridScalarIndex::Load(milvus::tracer::TraceContext ctx,
+ const Config& config) {
+ auto index_files =
+ GetValueFromConfig>(config, "index_files");
+ AssertInfo(index_files.has_value(),
+ "index file paths is empty when load bitmap index");
+ auto index_datas = file_manager_->LoadIndexToMemory(index_files.value());
+ AssembleIndexDatas(index_datas);
+ BinarySet binary_set;
+ for (auto& [key, data] : index_datas) {
+ auto size = data->Size();
+ auto deleter = [&](uint8_t*) {}; // avoid repeated deconstruction
+ auto buf = std::shared_ptr(
+ (uint8_t*)const_cast(data->Data()), deleter);
+ binary_set.Append(key, buf, size);
+ }
+
+ DeserializeIndexType(binary_set);
+
+ LoadInternal(binary_set, config);
+
+ is_built_ = true;
+}
+
+template class HybridScalarIndex;
+template class HybridScalarIndex;
+template class HybridScalarIndex;
+template class HybridScalarIndex;
+template class HybridScalarIndex;
+template class HybridScalarIndex;
+template class HybridScalarIndex;
+template class HybridScalarIndex;
+
+} // namespace index
+} // namespace milvus
\ No newline at end of file
diff --git a/internal/core/src/index/HybridScalarIndex.h b/internal/core/src/index/HybridScalarIndex.h
new file mode 100644
index 0000000000000..c3c44630bf846
--- /dev/null
+++ b/internal/core/src/index/HybridScalarIndex.h
@@ -0,0 +1,166 @@
+// Licensed to the LF AI & Data foundation 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.
+
+#pragma once
+
+#include