Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/integration_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,13 @@ jobs:

- name: Install package and pytest
run: |
pip install pytest
pip install pytest pytest-timeout
pip install .

- name: Run integration tests
env:
MILVUS_URI: ${{ env.MILVUS_URI }}
run: pytest tests/ -v --tb=short
run: pytest tests/ -v --tb=short --timeout=60

- name: Print Docker logs on failure
if: failure()
Expand Down
29 changes: 28 additions & 1 deletion milvus_cli/scripts/alias_client_cli.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from .helper_client_cli import create, getList, delete, show
from .helper_client_cli import cli, create, getList, delete, show
import click


Expand Down Expand Up @@ -168,3 +168,30 @@ def show_alias(obj, aliasName):
click.echo(alias_info)
except Exception as e:
click.echo(message=e, err=True)


@cli.command("has_alias")
@click.option(
"-a",
"--alias-name",
"aliasName",
required=True,
help="The alias name to check.",
type=str,
)
@click.pass_obj
def has_alias(obj, aliasName):
"""
Check if alias exists.

USAGE:
milvus_cli > has_alias -a <alias_name>

EXAMPLES:
milvus_cli > has_alias -a carAlias1
"""
try:
result = obj.alias.has_alias(aliasName)
click.echo(f"Alias '{aliasName}' exists: {result}")
except Exception as e:
click.echo(message=e, err=True)
88 changes: 88 additions & 0 deletions milvus_cli/scripts/collection_client_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -1279,6 +1279,94 @@ def drop_collection_function(obj, collectionName, functionName):
click.echo(message=e, err=True)


@cli.command("alter_collection_function")
@click.option("-c", "--collection-name", "collectionName", required=True, help="Collection name.")
@click.option("-fn", "--function-name", "functionName", required=True, help="Function name.")
@click.option(
"-ft",
"--function-type",
"functionType",
required=True,
type=click.Choice(["BM25", "TextEmbedding", "OpenAI"], case_sensitive=True),
help="Function type.",
)
@click.option("-if", "--input-field", "inputField", required=True, help="Input field name.")
@click.option("-of", "--output-field", "outputField", required=True, help="Output field name.")
@click.pass_obj
def alter_collection_function(obj, collectionName, functionName, functionType, inputField, outputField):
"""
Alter a function in an existing collection.

USAGE:
milvus_cli > alter_collection_function -c <collection> -fn <name> -ft BM25 -if <input> -of <output>

EXAMPLES:
milvus_cli > alter_collection_function -c docs -fn bm25_fn -ft BM25 -if text -of embedding
"""
try:
function = Function(
name=functionName,
function_type=getattr(FunctionType, functionType),
input_field_names=[inputField],
output_field_names=[outputField],
)
result = obj.collection.alter_collection_function(collectionName, function)
click.echo(result)
except Exception as e:
click.echo(message=e, err=True)


@cli.command("create_field_schema")
@click.option("-f", "--field-name", "fieldName", required=True, help="Field name.")
@click.option(
"-dt",
"--data-type",
"dataType",
required=True,
type=click.Choice(FieldDataTypes),
help="Data type.",
)
@click.option("-d", "--dimension", "dimension", default=None, type=int, help="Dimension for vector fields.")
@click.option("--max-length", "maxLength", default=None, type=int, help="Max length for string fields.")
@click.option("-p", "--is-primary", "isPrimary", is_flag=True, default=False, help="Set as primary key field.")
@click.option("--auto-id", "autoId", is_flag=True, default=False, help="Enable auto ID.")
@click.pass_obj
def create_field_schema(obj, fieldName, dataType, dimension, maxLength, isPrimary, autoId):
"""
Create a field schema object.

USAGE:
milvus_cli > create_field_schema -f <name> -dt <type> [options]

EXAMPLES:
milvus_cli > create_field_schema -f id -dt INT64 -p --auto-id
milvus_cli > create_field_schema -f embedding -dt FLOAT_VECTOR -d 128
milvus_cli > create_field_schema -f text -dt VARCHAR --max-length 512
"""
try:
kwargs = {"name": fieldName, "dtype": getattr(DataType, dataType)}
if dimension is not None:
kwargs["dim"] = dimension
if maxLength is not None:
kwargs["max_length"] = maxLength
if isPrimary:
kwargs["is_primary"] = True
if autoId:
kwargs["auto_id"] = True
field_schema = FieldSchema(**kwargs)
click.echo(f"Field schema created: {field_schema}")
click.echo(f" Name: {field_schema.name}")
click.echo(f" Type: {field_schema.dtype}")
if hasattr(field_schema, "dim") and field_schema.dim:
click.echo(f" Dimension: {field_schema.dim}")
if hasattr(field_schema, "max_length") and field_schema.max_length:
click.echo(f" Max Length: {field_schema.max_length}")
if field_schema.is_primary:
click.echo(f" Primary Key: True")
except Exception as e:
click.echo(message=e, err=True)


@cli.command("add_collection_field")
@click.option("-c", "--collection-name", "collectionName", required=True, help="Collection name.")
@click.option("-f", "--field-name", "fieldName", required=True, help="Field name.")
Expand Down
30 changes: 29 additions & 1 deletion milvus_cli/scripts/database_client_cli.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from tabulate import tabulate
from .helper_cli import create, getList, delete, use, show, alter
from .helper_cli import cli, create, getList, delete, use, show, alter
import click


Expand Down Expand Up @@ -288,3 +288,31 @@ def drop_database_properties(obj, db_name, propertyKey):
click.echo(result)
except Exception as e:
click.echo(message=e, err=True)


@cli.command("has_database")
@click.option(
"-db",
"--db_name",
"db_name",
help="Database name.",
required=True,
type=str,
)
@click.pass_obj
def has_database(obj, db_name):
"""
Check if database exists.

USAGE:
milvus_cli > has_database -db <name>

EXAMPLES:
milvus_cli > has_database -db default
milvus_cli > has_database -db my_project
"""
try:
result = obj.database.has_database(db_name)
click.echo(f"Database '{db_name}' exists: {result}")
except Exception as e:
click.echo(message=e, err=True)
20 changes: 20 additions & 0 deletions milvus_cli/scripts/helper_client_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,26 @@ def server_version(obj):
except Exception as e:
click.echo(message=e, err=True)

@cli.command("server_type")
@click.pass_obj
def server_type(obj):
"""
Get Milvus server type.

Example:

milvus_cli > server_type
"""
try:
client = obj.connection.get_client()
if client is None:
click.echo("No connection. Use 'connect' first.", err=True)
return
stype = client.get_server_type()
click.echo(f"Server type: {stype}")
except Exception as e:
click.echo(message=e, err=True)

@cli.command()
def clear():
"""Clear screen."""
Expand Down
5 changes: 5 additions & 0 deletions milvus_cli/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,11 @@ class Completer(object):
"get_refresh_external_collection_progress": [],
"list_refresh_external_collection_jobs": [],
"has_collection": [],
"has_database": [],
"has_alias": [],
"alter_collection_function": [],
"create_field_schema": [],
"server_type": [],
"get_replicate_configuration": [],
"update_replicate_configuration": [],
}
Expand Down
17 changes: 17 additions & 0 deletions tests/test_alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,20 @@ def test_alter_alias(self, test_collection_for_alias, run_connected, unique_name
run_connected(f"delete alias -a {alias_name}")

assert code == 0, f"Failed to alter alias: {output}"

def test_has_alias_exists(self, test_collection_for_alias, run_connected, unique_name):
coll = test_collection_for_alias
alias_name = f"alias_{unique_name}"

run_connected(f"create alias -c {coll} -a {alias_name}")

output, code = run_connected(f"has_alias -a {alias_name}")
assert code == 0
assert "True" in output

run_connected(f"delete alias -a {alias_name}")

def test_has_alias_not_exists(self, run_connected, unique_name):
output, code = run_connected(f"has_alias -a nonexistent_{unique_name}")
assert code == 0
assert "False" in output
53 changes: 52 additions & 1 deletion tests/test_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,7 @@ def test_has_collection_not_exists(self, run_connected, unique_name):
assert code == 0
assert "False" in output

@pytest.mark.skip(reason="get_replicate_configuration hangs on Milvus standalone")
def test_get_replicate_configuration(self, run_connected, test_collection_with_index):
output, code = run_connected(f"get_replicate_configuration -c {test_collection_with_index}")
assert code == 0 or "error" in output.lower()
Expand All @@ -207,6 +208,7 @@ def test_add_and_drop_collection_function(self, run_connected, unique_name):
"fields": [
{"name": "id", "type": "INT64", "is_primary": True},
{"name": "text", "type": "VARCHAR", "max_length": 512},
{"name": "sparse", "type": "SPARSE_FLOAT_VECTOR"},
{"name": "embedding", "type": "FLOAT_VECTOR", "dim": 4}
]
}
Expand All @@ -221,7 +223,7 @@ def test_add_and_drop_collection_function(self, run_connected, unique_name):
if code != 0:
pytest.skip(f"Failed to create collection: {output}")
output, code = run_connected(
f"add_collection_function -c {coll} -fn bm25_fn -ft BM25 -if text -of text"
f"add_collection_function -c {coll} -fn bm25_fn -ft BM25 -if text -of sparse"
)
assert code == 0 or "error" in output.lower() or "not support" in output.lower()
if code == 0:
Expand Down Expand Up @@ -257,3 +259,52 @@ def test_add_and_drop_collection_field(self, run_connected, unique_name):
output, code = run_connected(f"drop_collection_field -c {coll} -f new_field")
assert code == 0 or "error" in output.lower()
run_connected(f"delete collection -c {coll} --yes")

def test_alter_collection_function(self, run_connected, unique_name):
coll = f"alterfn_{unique_name}"
schema = {
"collection_name": coll,
"auto_id": True,
"fields": [
{"name": "id", "type": "INT64", "is_primary": True},
{"name": "text", "type": "VARCHAR", "max_length": 512},
{"name": "sparse", "type": "SPARSE_FLOAT_VECTOR"},
{"name": "embedding", "type": "FLOAT_VECTOR", "dim": 4}
]
}
schema_file = f"/tmp/{coll}_schema.json"
with open(schema_file, "w") as f:
json.dump(schema, f)
output, code = run_connected(f"create collection --schema-file {schema_file}")
try:
os.remove(schema_file)
except OSError:
pass
if code != 0:
pytest.skip(f"Failed to create collection: {output}")
output, code = run_connected(
f"add_collection_function -c {coll} -fn bm25_fn -ft BM25 -if text -of sparse"
)
if code == 0:
output, code = run_connected(
f"alter_collection_function -c {coll} -fn bm25_fn -ft BM25 -if text -of sparse"
)
assert code == 0 or "error" in output.lower()
run_connected(f"drop_collection_function -c {coll} -fn bm25_fn")
run_connected(f"delete collection -c {coll} --yes")

def test_create_field_schema(self, run_connected):
output, code = run_connected("create_field_schema -f id -dt INT64 -p --auto-id")
assert code == 0
assert "id" in output

def test_create_field_schema_vector(self, run_connected):
output, code = run_connected("create_field_schema -f embedding -dt FLOAT_VECTOR -d 128")
assert code == 0
assert "embedding" in output
assert "128" in output

def test_create_field_schema_varchar(self, run_connected):
output, code = run_connected("create_field_schema -f text -dt VARCHAR --max-length 512")
assert code == 0
assert "text" in output
10 changes: 10 additions & 0 deletions tests/test_database.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,3 +81,13 @@ def test_delete_database_properties_command(self, cli_runner):
assert result.exit_code == 0
assert calls == [("test_db", ["database.replica.number"])]
assert "Drop database test_db properties successfully!" in result.output

def test_has_database_exists(self, run_connected):
output, code = run_connected("has_database -db default")
assert code == 0
assert "True" in output

def test_has_database_not_exists(self, run_connected, unique_name):
output, code = run_connected(f"has_database -db nonexistent_{unique_name}")
assert code == 0
assert "False" in output
Loading