Skip to content

Commit f5b3360

Browse files
committed
check index schema for matching support
1 parent 1deb70f commit f5b3360

6 files changed

Lines changed: 97 additions & 27 deletions

File tree

lib/collection/src/collection/payload_index_schema.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ impl Collection {
9797
Ok(result)
9898
}
9999

100-
pub fn payload_key_is_indexed(&self, key: &JsonPath) -> bool {
101-
self.payload_index_schema.read().schema.contains_key(key)
100+
pub fn payload_key_index_schema(&self, key: &JsonPath) -> Option<PayloadFieldSchema> {
101+
self.payload_index_schema.read().schema.get(key).cloned()
102102
}
103103

104104
/// Returns an arbitrary payload key along with acceptable

lib/collection/src/operations/verification/mod.rs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ mod update;
1111

1212
use std::fmt::Display;
1313

14+
use segment::json_path::JsonPath;
1415
use segment::types::{Filter, SearchParams, StrictModeConfig};
1516

1617
use super::types::{CollectionError, CollectionResult};
@@ -295,6 +296,32 @@ impl StrictModeVerification for SearchParams {
295296
}
296297
}
297298

299+
pub fn check_grouping_field(
300+
group_by: &JsonPath,
301+
collection: &Collection,
302+
strict_mode_config: &StrictModeConfig,
303+
) -> CollectionResult<()> {
304+
// check for unindexed fields targeted by group_by
305+
if strict_mode_config.unindexed_filtering_retrieve == Some(false) {
306+
// check the group_by field is indexed and support `match` statement
307+
if let Some(schema) = collection.payload_key_index_schema(group_by) {
308+
let schema_kind = schema.kind();
309+
if !schema_kind.support_match() {
310+
return Err(CollectionError::strict_mode(
311+
format!("Index of type \"{schema_kind:?}\" found for \"{group_by}\""),
312+
"Create an index supporting `match` for this key.",
313+
));
314+
}
315+
} else {
316+
return Err(CollectionError::strict_mode(
317+
format!("Index required but not found for \"{group_by}\""),
318+
"Create an index supporting `match` for this key.",
319+
));
320+
}
321+
}
322+
Ok(())
323+
}
324+
298325
#[cfg(test)]
299326
mod test {
300327
use std::sync::Arc;

lib/collection/src/operations/verification/query.rs

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use segment::types::StrictModeConfig;
22

3-
use super::StrictModeVerification;
3+
use super::{StrictModeVerification, check_grouping_field};
44
use crate::collection::Collection;
55
use crate::operations::types::{CollectionError, CollectionResult};
66
use crate::operations::universal_query::collection_query::{
@@ -128,14 +128,7 @@ impl StrictModeVerification for CollectionQueryGroupsRequest {
128128
query.check_strict_mode(collection, strict_mode_config)?
129129
}
130130
// check for unindexed fields targeted by group_by
131-
if strict_mode_config.unindexed_filtering_retrieve == Some(false)
132-
&& !collection.payload_key_is_indexed(&self.group_by)
133-
{
134-
return Err(CollectionError::strict_mode(
135-
format!("Index required but not found for \"{}\"", self.group_by,),
136-
"Create an index for this key.",
137-
));
138-
}
131+
check_grouping_field(&self.group_by, collection, strict_mode_config)?;
139132
Ok(())
140133
}
141134

lib/collection/src/operations/verification/search.rs

Lines changed: 5 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
use api::rest::{SearchGroupsRequestInternal, SearchRequestInternal};
22
use segment::types::{Filter, SearchParams, StrictModeConfig};
33

4-
use super::StrictModeVerification;
4+
use super::{StrictModeVerification, check_grouping_field};
55
use crate::collection::Collection;
6-
use crate::operations::types::{CollectionError, CollectionResult, CoreSearchRequest, SearchRequestBatch};
6+
use crate::operations::types::{
7+
CollectionError, CollectionResult, CoreSearchRequest, SearchRequestBatch,
8+
};
79

810
impl StrictModeVerification for SearchRequestInternal {
911
fn indexed_filter_read(&self) -> Option<&Filter> {
@@ -92,17 +94,7 @@ impl StrictModeVerification for SearchGroupsRequestInternal {
9294
strict_mode_config: &StrictModeConfig,
9395
) -> CollectionResult<()> {
9496
// check for unindexed fields targeted by group_by
95-
if strict_mode_config.unindexed_filtering_retrieve == Some(false)
96-
&& !collection.payload_key_is_indexed(&self.group_request.group_by)
97-
{
98-
return Err(CollectionError::strict_mode(
99-
format!(
100-
"Index required but not found for \"{}\"",
101-
self.group_request.group_by
102-
),
103-
"Create an index for this key.",
104-
));
105-
}
97+
check_grouping_field(&self.group_request.group_by, collection, strict_mode_config)?;
10698
Ok(())
10799
}
108100

lib/segment/src/types.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1711,6 +1711,20 @@ impl PayloadSchemaType {
17111711
Self::Uuid => PayloadSchemaParams::Uuid(UuidIndexParams::default()),
17121712
}
17131713
}
1714+
1715+
/// Check if this type supports a `match` condition
1716+
pub fn support_match(&self) -> bool {
1717+
match self {
1718+
Self::Keyword => true,
1719+
Self::Integer => true,
1720+
Self::Bool => true,
1721+
Self::Uuid => true,
1722+
Self::Float => false,
1723+
Self::Geo => false,
1724+
Self::Text => false,
1725+
Self::Datetime => false,
1726+
}
1727+
}
17141728
}
17151729

17161730
/// Payload type with parameters

tests/openapi/test_strictmode.py

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1860,7 +1860,7 @@ def test_strict_mode_group_by_unindexed(collection_name):
18601860
)
18611861

18621862
assert not response.ok
1863-
assert "Forbidden: Index required but not found for \"docId\". Help: Create an index for this key." in response.json()['status']['error']
1863+
assert "Forbidden: Index required but not found for \"docId\". Help: Create an index supporting `match` for this key." in response.json()['status']['error']
18641864

18651865
response = request_with_validation(
18661866
api="/collections/{collection_name}/points/query/groups",
@@ -1875,9 +1875,53 @@ def test_strict_mode_group_by_unindexed(collection_name):
18751875
},
18761876
)
18771877
assert not response.ok
1878-
assert "Forbidden: Index required but not found for \"docId\". Help: Create an index for this key." in response.json()['status']['error']
1878+
assert "Forbidden: Index required but not found for \"docId\". Help: Create an index supporting `match` for this key." in response.json()['status']['error']
18791879

1880-
# create index
1880+
# create geo index
1881+
request_with_validation(
1882+
api='/collections/{collection_name}/index',
1883+
method="PUT",
1884+
path_params={'collection_name': collection_name},
1885+
query_params={'wait': 'true'},
1886+
body={
1887+
"field_name": "docId",
1888+
"field_schema": "geo"
1889+
}
1890+
).raise_for_status()
1891+
1892+
# try again with geo index
1893+
response = request_with_validation(
1894+
api="/collections/{collection_name}/points/search/groups",
1895+
method="POST",
1896+
path_params={"collection_name": collection_name},
1897+
body={
1898+
"vector": [1.0, 0.0, 0.0, 0.0],
1899+
"limit": 10,
1900+
"with_payload": True,
1901+
"group_by": "docId",
1902+
"group_size": 3,
1903+
},
1904+
)
1905+
1906+
assert not response.ok
1907+
assert "Forbidden: Index of type \"Geo\" found for \"docId\". Help: Create an index supporting `match` for this key." in response.json()['status']['error']
1908+
1909+
response = request_with_validation(
1910+
api="/collections/{collection_name}/points/query/groups",
1911+
method="POST",
1912+
path_params={"collection_name": collection_name},
1913+
body={
1914+
"query": [1.0, 0.0, 0.0, 0.0],
1915+
"limit": 10,
1916+
"with_payload": True,
1917+
"group_by": "docId",
1918+
"group_size": 3,
1919+
},
1920+
)
1921+
assert not response.ok
1922+
assert "Forbidden: Index of type \"Geo\" found for \"docId\". Help: Create an index supporting `match` for this key." in response.json()['status']['error']
1923+
1924+
# create keyword index (supporting match)
18811925
request_with_validation(
18821926
api='/collections/{collection_name}/index',
18831927
method="PUT",

0 commit comments

Comments
 (0)