Skip to content

Commit a2ff385

Browse files
committed
Add object ttl
1 parent db9d4c2 commit a2ff385

File tree

8 files changed

+189
-58
lines changed

8 files changed

+189
-58
lines changed

integration/conftest.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import weaviate
2222
from weaviate.collections import Collection, CollectionAsync
2323
from weaviate.collections.classes.config import (
24+
_ObjectTTLCreate,
2425
Configure,
2526
DataType,
2627
Property,
@@ -66,6 +67,7 @@ def __call__(
6667
vector_config: Optional[
6768
Optional[Union[_VectorConfigCreate, List[_VectorConfigCreate]]]
6869
] = None,
70+
object_ttl: Optional[_ObjectTTLCreate] = None,
6971
) -> Collection[Any, Any]:
7072
"""Typing for fixture."""
7173
...
@@ -140,6 +142,7 @@ def _factory(
140142
vector_config: Optional[
141143
Optional[Union[_VectorConfigCreate, List[_VectorConfigCreate]]]
142144
] = None,
145+
object_ttl: Optional[_ObjectTTLCreate] = None,
143146
) -> Collection[Any, Any]:
144147
try:
145148
nonlocal client_fixture, name_fixtures, call_counter # noqa: F824
@@ -172,6 +175,7 @@ def _factory(
172175
vector_index_config=vector_index_config,
173176
reranker_config=reranker_config,
174177
vector_config=vector_config,
178+
object_ttl_config=object_ttl,
175179
)
176180
return collection
177181
except Exception as e:

integration/test_collection_config.py

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import datetime
12
from typing import Generator, List, Optional, Union
23

34
import pytest as pytest
@@ -1831,3 +1832,63 @@ def test_uncompressed_quantitizer(collection_factory: CollectionFactory) -> None
18311832
assert config.vector_index_config is not None
18321833
assert isinstance(config.vector_index_config, _VectorIndexConfigHNSW)
18331834
assert config.vector_index_config.quantizer is None
1835+
1836+
1837+
def test_object_ttl_creation(collection_factory: CollectionFactory) -> None:
1838+
dummy = collection_factory("dummy")
1839+
if dummy._connection._weaviate_version.is_lower_than(1, 35, 0):
1840+
pytest.skip("object ttl is not supported in Weaviate versions lower than 1.35.0")
1841+
1842+
collection = collection_factory(
1843+
object_ttl=Configure.ObjectTTL.delete_by_creation_time(
1844+
time_to_live=datetime.timedelta(days=30),
1845+
post_search_filter=True,
1846+
),
1847+
inverted_index_config=Configure.inverted_index(index_timestamps=True),
1848+
)
1849+
1850+
config = collection.config.get()
1851+
assert config.object_ttl_config is not None
1852+
assert config.object_ttl_config.delete_on == "creationTime"
1853+
assert config.object_ttl_config.time_to_live == datetime.timedelta(days=30)
1854+
1855+
1856+
def test_object_ttl_update(collection_factory: CollectionFactory) -> None:
1857+
dummy = collection_factory("dummy")
1858+
if dummy._connection._weaviate_version.is_lower_than(1, 35, 0):
1859+
pytest.skip("object ttl is not supported in Weaviate versions lower than 1.35.0")
1860+
1861+
collection = collection_factory(
1862+
object_ttl=Configure.ObjectTTL.delete_by_update_time(
1863+
time_to_live=datetime.timedelta(days=30),
1864+
post_search_filter=True,
1865+
),
1866+
inverted_index_config=Configure.inverted_index(index_timestamps=True),
1867+
)
1868+
1869+
config = collection.config.get()
1870+
assert config.object_ttl_config is not None
1871+
assert config.object_ttl_config.delete_on == "updateTime"
1872+
assert config.object_ttl_config.post_search_filter
1873+
assert config.object_ttl_config.time_to_live == datetime.timedelta(days=30)
1874+
1875+
1876+
def test_object_ttl_custom(collection_factory: CollectionFactory) -> None:
1877+
dummy = collection_factory("dummy")
1878+
if dummy._connection._weaviate_version.is_lower_than(1, 35, 0):
1879+
pytest.skip("object ttl is not supported in Weaviate versions lower than 1.35.0")
1880+
1881+
collection = collection_factory(
1882+
properties=[wvc.config.Property(name="customDate", data_type=DataType.DATE)],
1883+
object_ttl=Configure.ObjectTTL.delete_by_date_property(
1884+
date_property="customDate",
1885+
post_search_filter=False,
1886+
),
1887+
inverted_index_config=Configure.inverted_index(index_timestamps=True),
1888+
)
1889+
1890+
config = collection.config.get()
1891+
assert config.object_ttl_config is not None
1892+
assert config.object_ttl_config.delete_on == "customDate"
1893+
# assert config.object_ttl_config.time_to_live is None
1894+
assert not config.object_ttl_config.post_search_filter

weaviate/collections/classes/config.py

Lines changed: 17 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import datetime
12
from dataclasses import dataclass
23
from typing import (
34
Any,
@@ -31,6 +32,7 @@
3132
_NamedVectors,
3233
_NamedVectorsUpdate,
3334
)
35+
from weaviate.collections.classes.config_object_ttl import _ObjectTTL, _ObjectTTLCreate
3436
from weaviate.collections.classes.config_vector_index import (
3537
PQEncoderDistribution,
3638
PQEncoderType,
@@ -1136,64 +1138,6 @@ def contextualai(
11361138
return _RerankerContextualAIConfig(model=model, instruction=instruction, topN=top_n)
11371139

11381140

1139-
class _CollectionConfigCreateBase(_ConfigCreateModel):
1140-
description: Optional[str] = Field(default=None)
1141-
invertedIndexConfig: Optional[_InvertedIndexConfigCreate] = Field(
1142-
default=None, alias="inverted_index_config"
1143-
)
1144-
multiTenancyConfig: Optional[_MultiTenancyConfigCreate] = Field(
1145-
default=None, alias="multi_tenancy_config"
1146-
)
1147-
replicationConfig: Optional[_ReplicationConfigCreate] = Field(
1148-
default=None, alias="replication_config"
1149-
)
1150-
shardingConfig: Optional[_ShardingConfigCreate] = Field(default=None, alias="sharding_config")
1151-
vectorIndexConfig: Optional[_VectorIndexConfigCreate] = Field(
1152-
default=None, alias="vector_index_config"
1153-
)
1154-
moduleConfig: _VectorizerConfigCreate = Field(
1155-
default=_Vectorizer.none(), alias="vectorizer_config"
1156-
)
1157-
generativeSearch: Optional[_GenerativeProvider] = Field(default=None, alias="generative_config")
1158-
rerankerConfig: Optional[_RerankerProvider] = Field(default=None, alias="reranker_config")
1159-
1160-
def _to_dict(self) -> Dict[str, Any]:
1161-
ret_dict: Dict[str, Any] = {}
1162-
1163-
for cls_field in type(self).model_fields:
1164-
val = getattr(self, cls_field)
1165-
if cls_field in ["name", "model", "properties", "references"] or val is None:
1166-
continue
1167-
elif isinstance(val, (bool, float, str, int)):
1168-
ret_dict[cls_field] = str(val)
1169-
elif isinstance(val, _GenerativeProvider):
1170-
self.__add_to_module_config(ret_dict, val.generative.value, val._to_dict())
1171-
elif isinstance(val, _RerankerProvider):
1172-
self.__add_to_module_config(ret_dict, val.reranker.value, val._to_dict())
1173-
elif isinstance(val, _VectorizerConfigCreate):
1174-
ret_dict["vectorizer"] = val.vectorizer.value
1175-
if val.vectorizer != Vectorizers.NONE:
1176-
self.__add_to_module_config(ret_dict, val.vectorizer.value, val._to_dict())
1177-
elif isinstance(val, _VectorIndexConfigCreate):
1178-
ret_dict["vectorIndexType"] = val.vector_index_type()
1179-
ret_dict[cls_field] = val._to_dict()
1180-
else:
1181-
assert isinstance(val, _ConfigCreateModel)
1182-
ret_dict[cls_field] = val._to_dict()
1183-
if self.vectorIndexConfig is None:
1184-
ret_dict["vectorIndexType"] = VectorIndexType.HNSW.value
1185-
return ret_dict
1186-
1187-
@staticmethod
1188-
def __add_to_module_config(
1189-
return_dict: Dict[str, Any], addition_key: str, addition_val: Dict[str, Any]
1190-
) -> None:
1191-
if "moduleConfig" not in return_dict:
1192-
return_dict["moduleConfig"] = {addition_key: addition_val}
1193-
else:
1194-
return_dict["moduleConfig"][addition_key] = addition_val
1195-
1196-
11971141
class _CollectionConfigUpdate(_ConfigUpdateModel):
11981142
description: Optional[str] = Field(default=None)
11991143
property_descriptions: Optional[Dict[str, str]] = Field(default=None)
@@ -1764,13 +1708,25 @@ def to_dict(self) -> Dict:
17641708
NamedVectorConfig = _NamedVectorConfig
17651709

17661710

1711+
@dataclass
1712+
class _ObjectTTLConfig(_ConfigBase):
1713+
enabled: bool
1714+
time_to_live: Optional[datetime.timedelta]
1715+
post_search_filter: bool
1716+
delete_on: Union[str, Literal["updateTime"], Literal["creationTime"]]
1717+
1718+
1719+
ObjectTTLConfig = _ObjectTTLConfig
1720+
1721+
17671722
@dataclass
17681723
class _CollectionConfig(_ConfigBase):
17691724
name: str
17701725
description: Optional[str]
17711726
generative_config: Optional[GenerativeConfig]
17721727
inverted_index_config: InvertedIndexConfig
17731728
multi_tenancy_config: MultiTenancyConfig
1729+
object_ttl_config: Optional[ObjectTTLConfig]
17741730
properties: List[PropertyConfig]
17751731
references: List[ReferencePropertyConfig]
17761732
replication_config: ReplicationConfig
@@ -1841,6 +1797,7 @@ class _CollectionConfigSimple(_ConfigBase):
18411797
vectorizer_config: Optional[VectorizerConfig]
18421798
vectorizer: Optional[Union[Vectorizers, str]]
18431799
vector_config: Optional[Dict[str, _NamedVectorConfig]]
1800+
object_ttl_config: Optional[ObjectTTLConfig]
18441801

18451802

18461803
CollectionConfigSimple = _CollectionConfigSimple
@@ -1992,6 +1949,7 @@ class _CollectionConfigCreate(_ConfigCreateModel):
19921949
multiTenancyConfig: Optional[_MultiTenancyConfigCreate] = Field(
19931950
default=None, alias="multi_tenancy_config"
19941951
)
1952+
objectTtlConfig: Optional[_ObjectTTLCreate] = Field(default=None, alias="object_ttl_config")
19951953
replicationConfig: Optional[_ReplicationConfigCreate] = Field(
19961954
default=None, alias="replication_config"
19971955
)
@@ -2155,6 +2113,7 @@ class Configure:
21552113
NamedVectors = _NamedVectors
21562114
Vectors = _Vectors
21572115
MultiVectors = _MultiVectors
2116+
ObjectTTL = _ObjectTTL
21582117

21592118
@staticmethod
21602119
def inverted_index(

weaviate/collections/classes/config_methods.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import datetime
12
from typing import Any, Dict, List, Optional, Union, cast
23

34
from weaviate.collections.classes.config import (
@@ -25,6 +26,7 @@
2526
_NamedVectorConfig,
2627
_NamedVectorizerConfig,
2728
_NestedProperty,
29+
_ObjectTTLConfig,
2830
_PQConfig,
2931
_PQEncoderConfig,
3032
_Property,
@@ -293,6 +295,7 @@ def _collection_config_simple_from_json(schema: Dict[str, Any]) -> _CollectionCo
293295
name=schema["class"],
294296
description=schema.get("description"),
295297
generative_config=__get_generative_config(schema),
298+
object_ttl_config=_get_object_ttl_config(schema),
296299
properties=(
297300
_properties_from_config(schema) if schema.get("properties") is not None else []
298301
),
@@ -340,6 +343,7 @@ def _collection_config_from_json(schema: Dict[str, Any]) -> _CollectionConfig:
340343
"autoTenantActivation", False
341344
),
342345
),
346+
object_ttl_config=_get_object_ttl_config(schema),
343347
properties=(
344348
_properties_from_config(schema) if schema.get("properties") is not None else []
345349
),
@@ -378,6 +382,27 @@ def _collection_config_from_json(schema: Dict[str, Any]) -> _CollectionConfig:
378382
)
379383

380384

385+
def _get_object_ttl_config(schema: Dict[str, Any]) -> Optional[_ObjectTTLConfig]:
386+
if "objectTtlConfig" in schema and schema["objectTtlConfig"].get("enabled", False):
387+
time_to_live = schema["objectTtlConfig"].get("defaultTtl")
388+
if time_to_live is not None and isinstance(time_to_live, int):
389+
time_to_live = datetime.timedelta(seconds=time_to_live)
390+
delete_on = schema["objectTtlConfig"]["deleteOn"]
391+
if delete_on == "_lastUpdateTimeUnix":
392+
delete_on = "updateTime"
393+
elif delete_on == "_creationTimeUnix":
394+
delete_on = "creationTime"
395+
396+
return _ObjectTTLConfig(
397+
enabled=True,
398+
delete_on=delete_on,
399+
post_search_filter=schema["objectTtlConfig"]["postSearchFilter"],
400+
time_to_live=time_to_live,
401+
)
402+
else:
403+
return None
404+
405+
381406
def _collection_configs_from_json(schema: Dict[str, Any]) -> Dict[str, _CollectionConfig]:
382407
configs = {
383408
schema["class"]: _collection_config_from_json(schema) for schema in schema["classes"]
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
import datetime
2+
from typing import Optional
3+
4+
from weaviate.collections.classes.config_base import _ConfigCreateModel
5+
6+
7+
class _ObjectTTLCreate(_ConfigCreateModel):
8+
enabled: bool = True
9+
postSearchFilter: Optional[bool]
10+
deleteOn: Optional[str]
11+
defaultTtl: Optional[int]
12+
13+
14+
class _ObjectTTL:
15+
"""Configuration class for Weaviate's object time-to-live (TTL) feature."""
16+
17+
@staticmethod
18+
def delete_by_update_time(
19+
time_to_live: int | datetime.timedelta,
20+
post_search_filter: Optional[bool] = None,
21+
) -> _ObjectTTLCreate:
22+
"""Create an `ObjectTimeToLiveConfig` object to be used when defining the object time-to-live configuration of Weaviate.
23+
24+
Args:
25+
time_to_live: The time-to-live for objects in seconds.
26+
post_search_filter: If enabled search results will be filtered to remove expired objects that have not yet been deleted.
27+
"""
28+
if isinstance(time_to_live, datetime.timedelta):
29+
time_to_live = int(time_to_live.total_seconds())
30+
return _ObjectTTLCreate(
31+
deleteOn="_lastUpdateTimeUnix",
32+
postSearchFilter=post_search_filter,
33+
defaultTtl=time_to_live,
34+
)
35+
36+
@staticmethod
37+
def delete_by_creation_time(
38+
time_to_live: int | datetime.timedelta,
39+
post_search_filter: Optional[bool] = None,
40+
) -> _ObjectTTLCreate:
41+
"""Create an `ObjectTimeToLiveConfig` object to be used when defining the object time-to-live configuration of Weaviate.
42+
43+
Args:
44+
time_to_live: The time-to-live for objects in seconds.
45+
post_search_filter: If enabled search results will be filtered to remove expired objects that have not yet been deleted.
46+
"""
47+
if isinstance(time_to_live, datetime.timedelta):
48+
time_to_live = int(time_to_live.total_seconds())
49+
return _ObjectTTLCreate(
50+
deleteOn="_creationTimeUnix",
51+
postSearchFilter=post_search_filter,
52+
defaultTtl=time_to_live,
53+
)
54+
55+
@staticmethod
56+
def delete_by_date_property(
57+
date_property: str,
58+
post_search_filter: Optional[bool] = None,
59+
) -> _ObjectTTLCreate:
60+
"""Create an Object ttl config for a custom date property.
61+
62+
Args:
63+
date_property: The name of the date property to use for object expiration.
64+
post_search_filter: If enabled search results will be filtered to remove expired objects that have not yet been deleted.
65+
"""
66+
return _ObjectTTLCreate(
67+
deleteOn=date_property,
68+
postSearchFilter=post_search_filter,
69+
defaultTtl=None,
70+
)

weaviate/collections/collections/async_.pyi

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ from weaviate.collections.classes.config import (
1818
_VectorIndexConfigCreate,
1919
_VectorizerConfigCreate,
2020
)
21+
from weaviate.collections.classes.config_object_ttl import _ObjectTTLCreate
2122
from weaviate.collections.classes.internal import References
2223
from weaviate.collections.classes.types import (
2324
Properties,
@@ -36,6 +37,7 @@ class _CollectionsAsync(_CollectionsBase[ConnectionAsync]):
3637
generative_config: Optional[_GenerativeProvider] = None,
3738
inverted_index_config: Optional[_InvertedIndexConfigCreate] = None,
3839
multi_tenancy_config: Optional[_MultiTenancyConfigCreate] = None,
40+
object_ttl_config: Optional[_ObjectTTLCreate] = None,
3941
properties: Optional[Sequence[Property]] = None,
4042
references: Optional[List[_ReferencePropertyBase]] = None,
4143
replication_config: Optional[_ReplicationConfigCreate] = None,
@@ -62,6 +64,7 @@ class _CollectionsAsync(_CollectionsBase[ConnectionAsync]):
6264
generative_config: Optional[_GenerativeProvider] = None,
6365
inverted_index_config: Optional[_InvertedIndexConfigCreate] = None,
6466
multi_tenancy_config: Optional[_MultiTenancyConfigCreate] = None,
67+
object_ttl_config: Optional[_ObjectTTLCreate] = None,
6568
properties: Optional[Sequence[Property]] = None,
6669
references: Optional[List[_ReferencePropertyBase]] = None,
6770
replication_config: Optional[_ReplicationConfigCreate] = None,
@@ -88,6 +91,7 @@ class _CollectionsAsync(_CollectionsBase[ConnectionAsync]):
8891
generative_config: Optional[_GenerativeProvider] = None,
8992
inverted_index_config: Optional[_InvertedIndexConfigCreate] = None,
9093
multi_tenancy_config: Optional[_MultiTenancyConfigCreate] = None,
94+
object_ttl_config: Optional[_ObjectTTLCreate] = None,
9195
properties: Optional[Sequence[Property]] = None,
9296
references: Optional[List[_ReferencePropertyBase]] = None,
9397
replication_config: Optional[_ReplicationConfigCreate] = None,

0 commit comments

Comments
 (0)