Skip to content

Commit 81f5e22

Browse files
authored
Merge pull request #127 from weaviate/jose/add_support_for_named_vectors
Add support for named vectors.
2 parents 2b75028 + 89c146a commit 81f5e22

7 files changed

Lines changed: 607 additions & 99 deletions

File tree

Lines changed: 391 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,391 @@
1+
import pytest
2+
import weaviate
3+
import numpy as np
4+
from weaviate_cli.managers.collection_manager import CollectionManager
5+
from weaviate_cli.managers.config_manager import ConfigManager
6+
from weaviate_cli.managers.data_manager import DataManager
7+
import weaviate.classes.config as wvc
8+
9+
10+
@pytest.fixture
11+
def client() -> weaviate.Client:
12+
config = ConfigManager()
13+
return config.get_client()
14+
15+
16+
@pytest.fixture
17+
def collection_manager(client: weaviate.Client) -> CollectionManager:
18+
return CollectionManager(client)
19+
20+
21+
@pytest.fixture
22+
def data_manager(client: weaviate.Client) -> DataManager:
23+
return DataManager(client)
24+
25+
26+
@pytest.mark.parametrize("randomize", [False, True])
27+
@pytest.mark.parametrize("vectorizer", ["transformers", "contextionary"])
28+
def test_data_creation_with_different_configs(
29+
collection_manager: CollectionManager,
30+
data_manager: DataManager,
31+
randomize: bool,
32+
vectorizer: str,
33+
):
34+
"""Test data creation with different randomize and vectorizer configurations."""
35+
collection_name = f"TestData{randomize}{vectorizer.capitalize()[:7]}"
36+
# Contextionary does not know the word "contextionary", therefore we truncate it to "context" [0:7]
37+
38+
try:
39+
# Create collection with specified vectorizer
40+
collection_manager.create_collection(
41+
collection=collection_name,
42+
vectorizer=vectorizer,
43+
replication_factor=1,
44+
training_limit=1000,
45+
async_enabled=True,
46+
)
47+
48+
# Verify collection exists
49+
assert collection_manager.client.collections.exists(collection_name)
50+
51+
# Create data with specified randomize setting
52+
data_manager.create_data(
53+
collection=collection_name,
54+
limit=50,
55+
consistency_level="one",
56+
randomize=randomize,
57+
skip_seed=True,
58+
)
59+
60+
# Get the collection and verify data was created
61+
collection = collection_manager.client.collections.get(collection_name)
62+
63+
# Wait for indexing to complete
64+
collection.batch.wait_for_vector_indexing()
65+
66+
# Query objects to verify they have content and vectors
67+
objects = collection.query.fetch_objects(limit=50, include_vector=True)
68+
69+
# Verify we got the expected number of objects
70+
assert len(objects.objects) == 50
71+
72+
# Verify each object has content and vectors
73+
for obj in objects.objects:
74+
# Check that object has properties
75+
assert hasattr(obj, "properties")
76+
assert obj.properties is not None
77+
78+
# Check that object has a title (required field)
79+
assert "title" in obj.properties
80+
assert obj.properties["title"] is not None
81+
assert len(obj.properties["title"]) > 0
82+
83+
# Check that object has genres
84+
assert "genres" in obj.properties
85+
assert obj.properties["genres"] is not None
86+
87+
# Check that object has keywords
88+
assert "keywords" in obj.properties
89+
assert obj.properties["keywords"] is not None
90+
91+
# Verify vector was created
92+
assert hasattr(obj, "vector")
93+
assert obj.vector is not None
94+
95+
vector_dimensions = {
96+
"transformers": 384,
97+
"contextionary": 300,
98+
}
99+
100+
# Check vector dimensions (should be 1536 for default)
101+
assert len(obj.vector["default"]) == vector_dimensions[vectorizer]
102+
103+
# Verify vector is not all zeros (should have meaningful values)
104+
assert not np.allclose(
105+
obj.vector["default"], np.zeros(vector_dimensions[vectorizer])
106+
)
107+
108+
# Verify vector has finite values
109+
assert np.all(np.isfinite(obj.vector["default"]))
110+
111+
finally:
112+
# Clean up
113+
if collection_manager.client.collections.exists(collection_name):
114+
collection_manager.delete_collection(collection=collection_name)
115+
116+
117+
@pytest.mark.parametrize("vectorizer", ["transformers", "none"])
118+
@pytest.mark.parametrize(
119+
"named_vector_name", ["custom_vector", "movie_embedding", "content_vector"]
120+
)
121+
def test_data_creation_with_named_vectors(
122+
collection_manager: CollectionManager,
123+
data_manager: DataManager,
124+
named_vector_name: str,
125+
vectorizer: str,
126+
):
127+
"""Test data creation with named vectors and verify the correct vector name is set."""
128+
collection_name = f"TestNamedVector{named_vector_name}{vectorizer.capitalize()}"
129+
130+
try:
131+
# Create collection with named vector
132+
collection_manager.create_collection(
133+
collection=collection_name,
134+
vectorizer=vectorizer,
135+
replication_factor=1,
136+
training_limit=1000,
137+
async_enabled=True,
138+
named_vector=True,
139+
named_vector_name=named_vector_name,
140+
)
141+
142+
# Verify collection exists
143+
assert collection_manager.client.collections.exists(collection_name)
144+
145+
# Create data
146+
data_manager.create_data(
147+
collection=collection_name,
148+
limit=30,
149+
consistency_level="one",
150+
vector_dimensions=384 if vectorizer == "none" else None,
151+
randomize=True,
152+
skip_seed=True,
153+
)
154+
155+
# Get the collection and verify data was created
156+
collection = collection_manager.client.collections.get(collection_name)
157+
158+
# Wait for indexing to complete
159+
collection.batch.wait_for_vector_indexing()
160+
161+
# Query objects to verify they have content and named vectors
162+
objects = collection.query.fetch_objects(limit=30, include_vector=True)
163+
164+
# Verify we got the expected number of objects
165+
assert len(objects.objects) == 30
166+
167+
# Verify each object has content and the correct named vector
168+
for obj in objects.objects:
169+
# Check that object has properties
170+
assert hasattr(obj, "properties")
171+
assert obj.properties is not None
172+
173+
# Check that object has a title
174+
assert "title" in obj.properties
175+
assert obj.properties["title"] is not None
176+
177+
# Check that object has genres
178+
assert "genres" in obj.properties
179+
assert obj.properties["genres"] is not None
180+
181+
# Check that object has keywords
182+
assert "keywords" in obj.properties
183+
assert obj.properties["keywords"] is not None
184+
185+
# Verify named vector was created with correct name
186+
assert hasattr(obj, "vector")
187+
assert obj.vector is not None
188+
189+
# Check that the named vector exists
190+
assert named_vector_name in obj.vector
191+
192+
# Get the named vector
193+
named_vector = obj.vector[named_vector_name]
194+
assert named_vector is not None
195+
196+
# Check vector dimensions (should be 768 for transformers)
197+
assert len(named_vector) == 384
198+
199+
# Verify vector is not all zeros
200+
assert not np.allclose(named_vector, np.zeros(384))
201+
202+
# Verify vector has finite values
203+
assert np.all(np.isfinite(named_vector))
204+
205+
finally:
206+
# Clean up
207+
if collection_manager.client.collections.exists(collection_name):
208+
collection_manager.delete_collection(collection=collection_name)
209+
210+
211+
def test_data_creation_with_multi_vector(
212+
collection_manager: CollectionManager,
213+
data_manager: DataManager,
214+
):
215+
"""Test data creation with multi-vector enabled."""
216+
collection_name = "TestMultiVector"
217+
218+
try:
219+
# Create collection
220+
collection_manager.create_collection(
221+
collection=collection_name,
222+
vectorizer="none",
223+
replication_factor=1,
224+
training_limit=1000,
225+
async_enabled=True,
226+
vector_index="hnsw_multivector",
227+
named_vector=True,
228+
)
229+
230+
# Verify collection exists
231+
assert collection_manager.client.collections.exists(collection_name)
232+
233+
# Create data with multi-vector enabled
234+
data_manager.create_data(
235+
collection=collection_name,
236+
limit=25,
237+
consistency_level="one",
238+
randomize=True,
239+
skip_seed=True,
240+
multi_vector=True,
241+
vector_dimensions=1536,
242+
)
243+
244+
# Get the collection and verify data was created
245+
collection = collection_manager.client.collections.get(collection_name)
246+
247+
# Wait for indexing to complete
248+
collection.batch.wait_for_vector_indexing()
249+
250+
# Query objects to verify they have content and vectors
251+
objects = collection.query.fetch_objects(limit=25, include_vector=True)
252+
253+
# Verify we got the expected number of objects
254+
assert len(objects.objects) == 25
255+
256+
# Verify each object has content and vectors
257+
for obj in objects.objects:
258+
# Check that object has properties
259+
assert hasattr(obj, "properties")
260+
assert obj.properties is not None
261+
262+
# Check that object has a title
263+
assert "title" in obj.properties
264+
assert obj.properties["title"] is not None
265+
266+
# Check that object has genres
267+
assert "genres" in obj.properties
268+
assert obj.properties["genres"] is not None
269+
270+
# Check that object has keywords
271+
assert "keywords" in obj.properties
272+
assert obj.properties["keywords"] is not None
273+
274+
# Verify vector was created
275+
assert hasattr(obj, "vector")
276+
assert obj.vector is not None
277+
278+
# Check vector dimensions
279+
for vector in obj.vector["default"]:
280+
assert len(vector) == 1536
281+
282+
# Verify vector is not all zeros
283+
for vector in obj.vector["default"]:
284+
assert not np.allclose(vector, np.zeros(1536))
285+
286+
# Verify vector has finite values
287+
for vector in obj.vector["default"]:
288+
assert np.all(np.isfinite(vector))
289+
290+
finally:
291+
# Clean up
292+
if collection_manager.client.collections.exists(collection_name):
293+
collection_manager.delete_collection(collection=collection_name)
294+
295+
296+
def test_data_creation_with_custom_vector_dimensions(
297+
collection_manager: CollectionManager,
298+
data_manager: DataManager,
299+
):
300+
"""Test data creation with custom vector dimensions."""
301+
collection_name = "TestCustomDimensions"
302+
custom_dimensions = 768 # Different from default 1536
303+
304+
try:
305+
# Create collection
306+
collection_manager.create_collection(
307+
collection=collection_name,
308+
vectorizer="none",
309+
replication_factor=1,
310+
training_limit=1000,
311+
async_enabled=True,
312+
)
313+
314+
# Verify collection exists
315+
assert collection_manager.client.collections.exists(collection_name)
316+
317+
# Create data with custom vector dimensions
318+
data_manager.create_data(
319+
collection=collection_name,
320+
limit=20,
321+
consistency_level="one",
322+
randomize=True,
323+
skip_seed=True,
324+
vector_dimensions=custom_dimensions,
325+
)
326+
327+
# Get the collection and verify data was created
328+
collection = collection_manager.client.collections.get(collection_name)
329+
330+
# Wait for indexing to complete
331+
collection.batch.wait_for_vector_indexing()
332+
333+
# Query objects to verify they have content and vectors with custom dimensions
334+
objects = collection.query.fetch_objects(limit=20, include_vector=True)
335+
336+
# Verify we got the expected number of objects
337+
assert len(objects.objects) == 20
338+
339+
# Verify each object has content and vectors with correct dimensions
340+
for obj in objects.objects:
341+
# Check that object has properties
342+
assert hasattr(obj, "properties")
343+
assert obj.properties is not None
344+
345+
# Check that object has a title
346+
assert "title" in obj.properties
347+
assert obj.properties["title"] is not None
348+
349+
# Check that object has genres
350+
assert "genres" in obj.properties
351+
assert obj.properties["genres"] is not None
352+
353+
# Check that object has keywords
354+
assert "keywords" in obj.properties
355+
assert obj.properties["keywords"] is not None
356+
357+
# Verify vector was created with custom dimensions
358+
assert hasattr(obj, "vector")
359+
assert obj.vector is not None
360+
361+
# Check vector dimensions (should be custom_dimensions)
362+
assert len(obj.vector["default"]) == custom_dimensions
363+
364+
# Verify vector is not all zeros
365+
assert not np.allclose(obj.vector["default"], np.zeros(custom_dimensions))
366+
367+
# Verify vector has finite values
368+
assert np.all(np.isfinite(obj.vector["default"]))
369+
370+
finally:
371+
# Clean up
372+
if collection_manager.client.collections.exists(collection_name):
373+
collection_manager.delete_collection(collection=collection_name)
374+
375+
376+
def test_data_creation_error_handling(
377+
collection_manager: CollectionManager,
378+
data_manager: DataManager,
379+
):
380+
"""Test error handling when creating data in non-existent collection."""
381+
382+
# Try to create data in non-existent collection
383+
with pytest.raises(Exception) as exc_info:
384+
data_manager.create_data(
385+
collection="NonExistentCollection",
386+
limit=10,
387+
consistency_level="one",
388+
)
389+
390+
# Verify error message
391+
assert "does not exist in Weaviate" in str(exc_info.value)

0 commit comments

Comments
 (0)