Skip to content

Commit ea99850

Browse files
committed
Merge branch 'main' into DE-756
2 parents 2d65097 + 09ec84e commit ea99850

15 files changed

+173
-16
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ db = client.db("test", username="root", password="passwd")
5252
# Create a new collection named "students".
5353
students = db.create_collection("students")
5454

55-
# Add a hash index to the collection.
56-
students.add_hash_index(fields=["name"], unique=True)
55+
# Add a persistent index to the collection.
56+
students.add_persistent_index(fields=["name"], unique=True)
5757

5858
# Insert new documents into the collection.
5959
students.insert({"name": "jane", "age": 39})

arango/collection.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1293,6 +1293,13 @@ def add_hash_index(
12931293
) -> Result[Json]:
12941294
"""Create a new hash index.
12951295
1296+
.. warning::
1297+
1298+
The index types `hash` and `skiplist` are aliases for the persistent
1299+
index type and should no longer be used to create new indexes. The
1300+
aliases will be removed in a future version. Use
1301+
:func:`arango.collection.Collection.add_persistent_index` instead.
1302+
12961303
:param fields: Document fields to index.
12971304
:type fields: [str]
12981305
:param unique: Whether the index is unique.
@@ -1337,6 +1344,13 @@ def add_skiplist_index(
13371344
) -> Result[Json]:
13381345
"""Create a new skiplist index.
13391346
1347+
.. warning::
1348+
1349+
The index types `hash` and `skiplist` are aliases for the persistent
1350+
index type and should no longer be used to create new indexes. The
1351+
aliases will be removed in a future version. Use
1352+
:func:`arango.collection.Collection.add_persistent_index` instead.
1353+
13401354
:param fields: Document fields to index.
13411355
:type fields: [str]
13421356
:param unique: Whether the index is unique.

arango/database.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@
4242
PermissionListError,
4343
PermissionResetError,
4444
PermissionUpdateError,
45+
ServerAvailableOptionsGetError,
46+
ServerCurrentOptionsGetError,
4547
ServerDetailsError,
4648
ServerEchoError,
4749
ServerEncryptionError,
@@ -1118,6 +1120,58 @@ def response_handler(resp: Response) -> Json:
11181120

11191121
return self._execute(request, response_handler)
11201122

1123+
def options(self) -> Result[Json]:
1124+
"""Return the currently-set server options (ArangoDB 3.12+)
1125+
1126+
As this API may reveal sensitive data about the deployment, it can only
1127+
be accessed from inside the _system database. In addition, there is a
1128+
policy control startup option --server.options-api that determines if and
1129+
to whom the API is made available. This option can have the following
1130+
values:
1131+
- disabled: API is disabled.
1132+
- jwt: API can only be accessed via superuser JWT.
1133+
- admin: API can be accessed by admin users in the _system database only.
1134+
- public: everyone with access to _system database can access the API.
1135+
1136+
:return: Server options.
1137+
:rtype: dict
1138+
"""
1139+
request = Request(method="get", endpoint="/_admin/options")
1140+
1141+
def response_handler(resp: Response) -> Json:
1142+
if resp.is_success:
1143+
result: Json = resp.body
1144+
return result
1145+
raise ServerCurrentOptionsGetError(resp, request)
1146+
1147+
return self._execute(request, response_handler)
1148+
1149+
def options_available(self) -> Result[Json]:
1150+
"""Return a description of all available server options (ArangoDB 3.12+)
1151+
1152+
As this API may reveal sensitive data about the deployment, it can only
1153+
be accessed from inside the _system database. In addition, there is a
1154+
policy control startup option --server.options-api that determines if and
1155+
to whom the API is made available. This option can have the following
1156+
values:
1157+
- disabled: API is disabled.
1158+
- jwt: API can only be accessed via superuser JWT.
1159+
- admin: API can be accessed by admin users in the _system database only.
1160+
- public: everyone with access to _system database can access the options API.
1161+
1162+
:return: Server options.
1163+
:rtype: dict
1164+
"""
1165+
request = Request(method="get", endpoint="/_admin/options-description")
1166+
1167+
def response_handler(resp: Response) -> Json:
1168+
if resp.is_success:
1169+
result: Json = resp.body
1170+
return result
1171+
raise ServerAvailableOptionsGetError(resp, request)
1172+
1173+
return self._execute(request, response_handler)
1174+
11211175
#######################
11221176
# Database Management #
11231177
#######################

arango/exceptions.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -722,6 +722,14 @@ class ServerEncryptionError(ArangoServerError):
722722
"""Failed to reload user-defined encryption keys."""
723723

724724

725+
class ServerCurrentOptionsGetError(ArangoServerError):
726+
"""Failed to retrieve currently-set server options."""
727+
728+
729+
class ServerAvailableOptionsGetError(ArangoServerError):
730+
"""Failed to retrieve available server options."""
731+
732+
725733
class ServerExecuteError(ArangoServerError):
726734
"""Failed to execute raw JavaScript command."""
727735

arango/formatter.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,10 @@ def format_index(body: Json) -> Json:
104104
if "fieldValueTypes" in body:
105105
result["field_value_types"] = body["fieldValueTypes"]
106106

107+
# Introduced in 3.12 EE
108+
if "optimizeTopK" in body:
109+
result["optimizeTopK"] = body["optimizeTopK"]
110+
107111
return verify_format(body, result)
108112

109113

docs/indexes.rst

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,15 +27,15 @@ on fields ``_from`` and ``_to``. For more information on indexes, refer to
2727
# List the indexes in the collection.
2828
cities.indexes()
2929

30-
# Add a new hash index on document fields "continent" and "country".
31-
index = cities.add_hash_index(fields=['continent', 'country'], unique=True)
30+
# Add a new persistent index on document fields "continent" and "country".
31+
index = cities.add_persistent_index(fields=['continent', 'country'], unique=True)
3232

3333
# Add new fulltext indexes on fields "continent" and "country".
3434
index = cities.add_fulltext_index(fields=['continent'])
3535
index = cities.add_fulltext_index(fields=['country'])
3636

37-
# Add a new skiplist index on field 'population'.
38-
index = cities.add_skiplist_index(fields=['population'], sparse=False)
37+
# Add a new persistent index on field 'population'.
38+
index = cities.add_persistent_index(fields=['population'], sparse=False)
3939

4040
# Add a new geo-spatial index on field 'coordinates'.
4141
index = cities.add_geo_index(fields=['coordinates'])
@@ -47,7 +47,7 @@ on fields ``_from`` and ``_to``. For more information on indexes, refer to
4747
index = cities.add_ttl_index(fields=['currency'], expiry_time=200)
4848

4949
# Indexes may be added with a name that can be referred to in AQL queries.
50-
index = cities.add_hash_index(fields=['country'], name='my_hash_index')
50+
index = cities.add_persistent_index(fields=['country'], name='my_persistent_index')
5151

5252
# Delete the last index from the collection.
5353
cities.delete_index(index['id'])

starter.sh

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,12 @@ else
3232
exit 1
3333
fi
3434

35-
conf_file=""
36-
if [[ "${version%.*}" == "3.10" ]]; then
37-
conf_file="${setup}-3.10"
35+
if [ "$version" == "latest" ]; then
36+
conf_file="${setup}-3.12"
37+
elif [[ "$version" == *.*.* ]]; then
38+
conf_file="${setup}-${version%.*}"
3839
else
39-
conf_file="${setup}"
40+
conf_file="${setup}-${version}"
4041
fi
4142

4243
docker run -d \
File renamed without changes.

tests/static/cluster-3.12.conf

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
[starter]
2+
mode = cluster
3+
local = true
4+
address = 0.0.0.0
5+
port = 8528
6+
7+
[auth]
8+
jwt-secret = /tests/static/keyfile
9+
10+
[args]
11+
all.database.password = passwd
12+
all.database.extended-names = true
13+
all.log.api-enabled = true
14+
all.javascript.allow-admin-execute = true
15+
all.server.options-api = admin
File renamed without changes.

tests/static/single-3.12.conf

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
[starter]
2+
mode = single
3+
address = 0.0.0.0
4+
port = 8528
5+
6+
[auth]
7+
jwt-secret = /tests/static/keyfile
8+
9+
[args]
10+
all.database.password = passwd
11+
all.database.extended-names = true
12+
all.javascript.allow-admin-execute = true
13+
all.server.options-api = admin

tests/test_analyzer.py

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from packaging import version
2+
13
from arango.exceptions import (
24
AnalyzerCreateError,
35
AnalyzerDeleteError,
@@ -7,18 +9,28 @@
79
from tests.helpers import assert_raises, generate_analyzer_name
810

911

10-
def test_analyzer_management(db, bad_db, cluster, enterprise):
12+
def test_analyzer_management(db, bad_db, cluster, enterprise, db_version):
1113
analyzer_name = generate_analyzer_name()
1214
full_analyzer_name = db.name + "::" + analyzer_name
1315
bad_analyzer_name = generate_analyzer_name()
1416

15-
# Test create analyzer
17+
# Test create identity analyzer
1618
result = db.create_analyzer(analyzer_name, "identity", {})
1719
assert result["name"] == full_analyzer_name
1820
assert result["type"] == "identity"
1921
assert result["properties"] == {}
2022
assert result["features"] == []
2123

24+
# Test create delimiter analyzer
25+
result = db.create_analyzer(
26+
name=generate_analyzer_name(),
27+
analyzer_type="delimiter",
28+
properties={"delimiter": ","},
29+
)
30+
assert result["type"] == "delimiter"
31+
assert result["properties"] == {"delimiter": ","}
32+
assert result["features"] == []
33+
2234
# Test create duplicate with bad database
2335
with assert_raises(AnalyzerCreateError) as err:
2436
bad_db.create_analyzer(analyzer_name, "identity", {}, [])
@@ -69,3 +81,22 @@ def test_analyzer_management(db, bad_db, cluster, enterprise):
6981
"format": "latLngDouble",
7082
}
7183
assert db.delete_analyzer(analyzer_name)
84+
85+
# Test create delimieter analyzer with multiple delimiters
86+
if db_version >= version.parse("3.12.0"):
87+
result = db.create_analyzer(
88+
name=generate_analyzer_name(),
89+
analyzer_type="multi_delimiter",
90+
properties={"delimiters": [",", "."]},
91+
)
92+
93+
assert result["type"] == "multi_delimiter"
94+
assert result["properties"] == {"delimiters": [",", "."]}
95+
assert result["features"] == []
96+
97+
if db_version >= version.parse("3.12.0"):
98+
analyzer_name = generate_analyzer_name()
99+
result = db.create_analyzer(analyzer_name, "wildcard", {"ngramSize": 4})
100+
assert result["type"] == "wildcard"
101+
assert result["features"] == []
102+
assert result["properties"] == {"ngramSize": 4}

tests/test_collection.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,6 @@ def test_collection_management(db, bad_db, cluster):
209209
shard_count=2,
210210
shard_fields=["test_attr:"],
211211
replication_factor=1,
212-
shard_like="",
213212
sync_replication=False,
214213
enforce_replication_factor=False,
215214
sharding_strategy="community-compat",
@@ -236,6 +235,14 @@ def test_collection_management(db, bad_db, cluster):
236235
assert properties["computedValues"] == computed_values
237236
col.configure(computed_values=[])
238237

238+
if cluster:
239+
# Create distribute-shards-like collection
240+
shard_like_name = col_name + "_shards_like"
241+
shard_like_col = db.create_collection(name=shard_like_name, shard_like=col_name)
242+
assert shard_like_col.properties()["shard_like"] == col_name
243+
assert db.has_collection(shard_like_name) is True
244+
assert db.delete_collection(shard_like_name, system=False) is True
245+
239246
# Test create duplicate collection
240247
with assert_raises(CollectionCreateError) as err:
241248
db.create_collection(col_name)

tests/test_database.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
from datetime import datetime
22

33
import pytest
4+
from packaging import version
45

56
from arango.aql import AQL
67
from arango.backup import Backup
@@ -438,3 +439,12 @@ def test_license(sys_db, enterprise):
438439
assert license == {"license": "none"}
439440
with pytest.raises(ServerLicenseSetError):
440441
sys_db.set_license("abc")
442+
443+
444+
def test_options(sys_db, db_version):
445+
# Skip if below 3.12
446+
if db_version < version.parse("3.12.0"):
447+
pytest.skip("Database options require ArangoDB 3.12+")
448+
449+
assert sys_db.options()
450+
assert sys_db.options_available()

tests/test_transaction.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ def test_transaction_commit(db, col, docs):
9797
sync=True,
9898
allow_implicit=False,
9999
lock_timeout=1000,
100-
max_size=10000,
100+
max_size=1024 * 1024, # 1MB
101101
)
102102
txn_col = txn_db.collection(col.name)
103103

@@ -126,7 +126,7 @@ def test_transaction_fetch_existing(db, col, docs):
126126
sync=True,
127127
allow_implicit=False,
128128
lock_timeout=1000,
129-
max_size=10000,
129+
max_size=1024 * 1024, # 1MB
130130
)
131131
txn_col = original_txn.collection(col.name)
132132

0 commit comments

Comments
 (0)