Skip to content

Commit db77ebf

Browse files
committed
PYCBC-1753: Client is not passing scope_name or bucket_name when using scope search indexes
Changes ------- * Update scope implementations to add scope_name and bucket_name to request (these params are then passed into the C++ core). * Fix tests to properly load a scope-level search index and test scope-level search queries. * Update search tests to test both search_query (deprecated) and search(). * Update tracing/metrics streaming tests to verify bucket and scope are present when expected. Change-Id: I5988f8f7f528259c642a9f4d96a6c0b084f7fd0f Reviewed-on: https://review.couchbase.org/c/couchbase-python-client/+/243397 Reviewed-by: Dimitris Christodoulou <dimitris.christodoulou@couchbase.com> Tested-by: Build Bot <build@couchbase.com>
1 parent d7bfdba commit db77ebf

15 files changed

Lines changed: 117 additions & 116 deletions

File tree

‎acouchbase/logic/scope_impl.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,8 @@ def search(self, req: SearchQueryRequest) -> SearchResult:
155155
default_serializer=self.default_serializer, # noqa: E501
156156
streaming_timeout=streaming_timeout,
157157
obs_handler=req.obs_handler,
158+
bucket_name=req.bucket_name,
159+
scope_name=req.scope_name,
158160
num_workers=req.num_workers))
159161

160162
async def wait_until_bucket_connected(self) -> None:

‎acouchbase/scope.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,8 @@ def search_indexes(self) -> ScopeSearchIndexManager:
435435
436436
""" # noqa: E501
437437
return ScopeSearchIndexManager(self._impl._client_adapter,
438-
self.bucket_name, self.name,
438+
self.bucket_name,
439+
self.name,
439440
self._impl.observability_instruments)
440441

441442
def eventing_functions(self) -> ScopeEventingFunctionManager:

‎acouchbase/tests/metrics_tests/streaming_t.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -182,9 +182,10 @@ async def test_http_search_query_op(self, acb_env: AsyncMetricsEnvironment) -> N
182182
acb_env.mock_server_type):
183183
return # skip rest of test if collections not supported
184184

185-
# TODO(PYCBC-1753): The bucket and scope name are not passed when doing scope-level search queries.
186-
# This means we cannot validate the span attributes have db.namespace and couchbase.scope.name
187-
validator.reset(op_name=OpName.SearchQuery, validate_error=True)
185+
validator.reset(op_name=OpName.SearchQuery,
186+
bucket_name=acb_env.bucket.name,
187+
scope_name=acb_env.scope.name,
188+
validate_error=True)
188189
try:
189190
[r async for r in acb_env.scope.search('not-an-index', TermQuery('auto')).rows()]
190191
except Exception:

‎acouchbase/tests/search_t.py‎

Lines changed: 33 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -38,52 +38,42 @@
3838

3939
class SearchCollectionTestSuite:
4040
TEST_MANIFEST = [
41-
'test_cluster_query_collections',
4241
'test_scope_query_collections',
4342
'test_scope_search_fields',
4443
'test_scope_search_highlight',
4544
]
4645

46+
@pytest.mark.parametrize('query_op', ['search_query', 'search'])
4747
@pytest.mark.asyncio
48-
async def test_cluster_query_collections(self, cb_env):
48+
async def test_scope_query_collections(self, cb_env, query_op):
4949
q = search.TermQuery('auto')
50-
res = cb_env.cluster.search_query(cb_env.TEST_COLLECTION_INDEX_NAME,
51-
q,
52-
SearchOptions(limit=10,
53-
scope_name=cb_env.scope.name,
54-
collections=[cb_env.collection.name]))
50+
operation = getattr(cb_env.scope, query_op)
51+
res = operation(cb_env.TEST_COLLECTION_INDEX_NAME,
52+
q,
53+
SearchOptions(limit=10, collections=[cb_env.collection.name]))
5554
rows = await cb_env.assert_rows(res, 2, return_rows=True)
5655

5756
collections = list(map(lambda r: r.fields['_$c'], rows))
5857
assert all([c for c in collections if c == cb_env.collection.name]) is True
5958

60-
@pytest.mark.asyncio
61-
async def test_scope_query_collections(self, cb_env):
62-
q = search.TermQuery('auto')
63-
res = cb_env.scope.search_query(cb_env.TEST_COLLECTION_INDEX_NAME,
64-
q,
65-
SearchOptions(limit=10, collections=[cb_env.collection.name]))
66-
rows = await cb_env.assert_rows(res, 2, return_rows=True)
67-
68-
collections = list(map(lambda r: r.fields['_$c'], rows))
69-
assert all([c for c in collections if c == cb_env.collection.name]) is True
70-
71-
res = cb_env.scope.search_query(cb_env.TEST_COLLECTION_INDEX_NAME, q, SearchOptions(limit=10))
59+
res = operation(cb_env.TEST_COLLECTION_INDEX_NAME, q, SearchOptions(limit=10))
7260
rows = await cb_env.assert_rows(res, 2, return_rows=True)
7361

7462
collections = list(map(lambda r: r.fields['_$c'], rows))
7563
assert all([c for c in collections if c in [cb_env.collection.name, cb_env.OTHER_COLLECTION]]) is True
7664

65+
@pytest.mark.parametrize('query_op', ['search_query', 'search'])
7766
@pytest.mark.asyncio
78-
async def test_scope_search_fields(self, cb_env):
67+
async def test_scope_search_fields(self, cb_env, query_op):
7968
test_fields = ['make', 'model']
69+
operation = getattr(cb_env.scope, query_op)
8070
q = search.TermQuery('auto')
8171
# verify fields works w/in kwargs
82-
res = cb_env.scope.search_query(cb_env.TEST_COLLECTION_INDEX_NAME,
83-
q,
84-
SearchOptions(limit=10),
85-
fields=test_fields,
86-
collections=[cb_env.collection.name])
72+
res = operation(cb_env.TEST_COLLECTION_INDEX_NAME,
73+
q,
74+
SearchOptions(limit=10),
75+
fields=test_fields,
76+
collections=[cb_env.collection.name])
8777

8878
fields_with_col = copy(test_fields)
8979
fields_with_col.append('_$c')
@@ -97,11 +87,11 @@ async def test_scope_search_fields(self, cb_env):
9787
assert all([c for c in collections if c == cb_env.collection.name]) is True
9888

9989
# verify fields works w/in options
100-
res = cb_env.scope.search_query(cb_env.TEST_COLLECTION_INDEX_NAME,
101-
q,
102-
SearchOptions(limit=10,
103-
fields=test_fields,
104-
collections=[cb_env.collection.name]))
90+
res = operation(cb_env.TEST_COLLECTION_INDEX_NAME,
91+
q,
92+
SearchOptions(limit=10,
93+
fields=test_fields,
94+
collections=[cb_env.collection.name]))
10595

10696
rows = await cb_env.assert_rows(res, 1, return_rows=True)
10797
first_entry = rows[0]
@@ -112,14 +102,15 @@ async def test_scope_search_fields(self, cb_env):
112102
collections = list(map(lambda r: r.fields['_$c'], rows))
113103
assert all([c for c in collections if c == cb_env.collection.name]) is True
114104

105+
@pytest.mark.parametrize('query_op', ['search_query', 'search'])
115106
@pytest.mark.asyncio
116-
async def test_scope_search_highlight(self, cb_env):
117-
107+
async def test_scope_search_highlight(self, cb_env, query_op):
118108
q = search.TermQuery('auto')
109+
operation = getattr(cb_env.scope, query_op)
119110
# check w/in options
120-
res = cb_env.scope.search_query(cb_env.TEST_COLLECTION_INDEX_NAME,
121-
q,
122-
SearchOptions(limit=10, highlight_style=HighlightStyle.Html))
111+
res = operation(cb_env.TEST_COLLECTION_INDEX_NAME,
112+
q,
113+
SearchOptions(limit=10, highlight_style=HighlightStyle.Html))
123114
rows = await cb_env.assert_rows(res, 1, return_rows=True)
124115
locations = rows[0].locations
125116
fragments = rows[0].fragments
@@ -130,8 +121,11 @@ async def test_scope_search_highlight(self, cb_env):
130121
assert all([c for c in collections if c == cb_env.collection.name]) is True
131122

132123
# check w/in kwargs
133-
res = cb_env.scope.search_query(cb_env.TEST_COLLECTION_INDEX_NAME, q, SearchOptions(
134-
limit=10), highlight_style=HighlightStyle.Html, collections=[cb_env.collection.name])
124+
res = operation(cb_env.TEST_COLLECTION_INDEX_NAME,
125+
q,
126+
SearchOptions(limit=10),
127+
highlight_style=HighlightStyle.Html,
128+
collections=[cb_env.collection.name])
135129
rows = await cb_env.assert_rows(res, 1, return_rows=True)
136130
locations = rows[0].locations
137131
fragments = rows[0].fragments
@@ -753,7 +747,7 @@ async def couchbase_test_environment(self, acb_base_env, test_manifest_validated
753747
pytest.fail(f'Test manifest not validated. Missing tests: {test_manifest_validated}.')
754748

755749
acb_env = AsyncSearchTestEnvironment.from_environment(acb_base_env)
756-
acb_env.enable_search_mgmt()
750+
# acb_env.enable_search_mgmt()
757751
await acb_env.setup(request.param)
758752
yield acb_env
759753
await acb_env.teardown(request.param)
@@ -775,7 +769,7 @@ async def couchbase_test_environment(self, acb_base_env, test_manifest_validated
775769
pytest.fail(f'Test manifest not validated. Missing tests: {test_manifest_validated}.')
776770

777771
acb_env = AsyncSearchTestEnvironment.from_environment(acb_base_env)
778-
acb_env.enable_search_mgmt()
772+
# acb_env.enable_search_mgmt()
779773
await acb_env.setup(request.param)
780774
yield acb_env
781775
await acb_env.teardown(request.param)

‎acouchbase/tests/tracing_tests/streaming_t.py‎

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -252,9 +252,11 @@ async def test_http_search_query_op(self, acb_env: AsyncBaseTracingEnvironment)
252252
acb_env.mock_server_type):
253253
return # skip rest of test if collections not supported
254254

255-
# TODO(PYCBC-1753): The bucket and scope name are not passed when doing scope-level search queries.
256-
# This means we cannot validate the span attributes have db.namespace and couchbase.scope.name
257-
validator.reset(op_name=OpName.SearchQuery, clear_statement=True, validate_error=True)
255+
validator.reset(op_name=OpName.SearchQuery,
256+
clear_statement=True,
257+
bucket_name=acb_env.bucket.name,
258+
scope_name=acb_env.scope.name,
259+
validate_error=True)
258260
try:
259261
[r async for r in acb_env.scope.search('not-an-index', TermQuery('auto')).rows()]
260262
except Exception:
@@ -263,12 +265,12 @@ async def test_http_search_query_op(self, acb_env: AsyncBaseTracingEnvironment)
263265

264266
acb_env.tracer.clear_spans()
265267
parent_span = acb_env.tracer.request_span(f'parent_{OpName.SearchQuery.value}_span')
266-
# TODO(PYCBC-1753): The bucket and scope name are not passed when doing scope-level search queries.
267-
# This means we cannot validate the span attributes have db.namespace and couchbase.scope.name
268268
validator.reset(do_not_clear_spans=True,
269269
op_name=OpName.SearchQuery,
270270
parent_span=parent_span,
271271
clear_statement=True,
272+
bucket_name=acb_env.bucket.name,
273+
scope_name=acb_env.scope.name,
272274
validate_error=True)
273275
try:
274276
[r async for r in acb_env.scope.search('not-an-index',

‎couchbase/logic/scope_impl.py‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,4 +137,6 @@ def search(self, req: SearchQueryRequest) -> SearchResult:
137137
req.query_builder.as_encodable(),
138138
default_serializer=self.default_serializer,
139139
streaming_timeout=streaming_timeout,
140-
obs_handler=req.obs_handler))
140+
obs_handler=req.obs_handler,
141+
bucket_name=req.bucket_name,
142+
scope_name=req.scope_name))

‎couchbase/logic/scope_req_builder.py‎

Lines changed: 7 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,7 @@
2323
QueryRequest,
2424
SearchQueryRequest)
2525
from couchbase.n1ql import N1QLQuery
26-
from couchbase.options import (AnalyticsOptions,
27-
QueryOptions,
28-
SearchOptions)
26+
from couchbase.options import AnalyticsOptions, QueryOptions
2927
from couchbase.search import (SearchQuery,
3028
SearchQueryBuilder,
3129
SearchRequest)
@@ -90,22 +88,14 @@ def build_search_request(self,
9088
*options: object,
9189
**kwargs: object) -> SearchQueryRequest:
9290
num_workers = kwargs.pop('num_workers', None)
91+
9392
if isinstance(query, SearchQuery):
94-
opt = SearchOptions()
95-
opts = list(options)
96-
for o in opts:
97-
if isinstance(o, SearchOptions):
98-
opt = o
99-
opts.remove(o)
100-
101-
# set the scope_name as this scope if not provided
102-
if not ('scope_name' in opt or 'scope_name' in kwargs):
103-
kwargs['scope_name'] = f'{self._scope_name}'
104-
req = SearchQueryRequest(
105-
SearchQueryBuilder.create_search_query_object(index, query, *options, **kwargs), obs_handler)
93+
query_builder = SearchQueryBuilder.create_search_query_object(index, query, *options, **kwargs)
10694
else:
107-
req = SearchQueryRequest(
108-
SearchQueryBuilder.create_search_query_from_request(index, query, *options, **kwargs), obs_handler)
95+
query_builder = SearchQueryBuilder.create_search_query_from_request(index, query, *options, **kwargs)
96+
97+
scope_name = query_builder.scope_name if query_builder.scope_name else self._scope_name
98+
req = SearchQueryRequest(query_builder, obs_handler, self._bucket_name, scope_name)
10999

110100
# since query is lazy executed, we wait until we submit the query to create the span
111101
if num_workers:

‎couchbase/logic/scope_types.py‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,8 @@ def op_name(self) -> str:
5353
class SearchQueryRequest:
5454
query_builder: SearchQueryBuilder
5555
obs_handler: ObservableRequestHandler
56+
bucket_name: str
57+
scope_name: str
5658
num_workers: Optional[int] = None
5759

5860
@property

‎couchbase/tests/metrics_tests/streaming_t.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -178,9 +178,10 @@ def test_http_search_query_op(self, cb_env: MetricsEnvironment) -> None:
178178
cb_env.mock_server_type):
179179
return # skip rest of test if collections not supported
180180

181-
# TODO(PYCBC-1753): The bucket and scope name are not passed when doing scope-level search queries.
182-
# This means we cannot validate the span attributes have db.namespace and couchbase.scope.name
183-
validator.reset(op_name=OpName.SearchQuery, validate_error=True)
181+
validator.reset(op_name=OpName.SearchQuery,
182+
bucket_name=cb_env.bucket.name,
183+
scope_name=cb_env.scope.name,
184+
validate_error=True)
184185
try:
185186
[r for r in cb_env.scope.search('not-an-index', TermQuery('auto')).rows()]
186187
except Exception:

0 commit comments

Comments
 (0)