Typesense关联表搜索异常:按标签名与菜品名搜索失败求助
问题描述
需求:希望按标签名和菜品名进行搜索。
尝试了以下查询但未生效:
const data = await typesenseClient.collections("dishes_view").documents().search({ 'q': 'tag1', 'query_by': '*', "filter_by": "$dish_tags(id: *)", "include_fields": "$tags(*) as tagsData ,name" }) console.log(JSON.stringify(data, null, 2));
疑问:
- 查询语句是否存在错误?
- Typesense是否支持关联表搜索?
- 若查询有误请提供正确语句,是否有其他实现方式?
集合Schema:
// dishes集合 const schema={ "name": "dishes", "num_documents": 0, "fields": [ { "name": "id", "type": "string", "facet": false }, { "name": "name", "type": "string", "facet": false }, { "name": "unit", "type": "int32", "facet": false, "optional": true }, { "name": "calories", "type": "int32", "facet": false, "optional": true }, { "name": "price", "type": "int32", "facet": false }, { "name": "is_active", "type": "bool", "facet": false }, { "name": "size", "type": "int32", "facet": false, "optional": true }, ], "default_sorting_field": "created_at", "enable_nested_fields": true } // tags集合 const schema = { "name": "tags", "num_documents": 0, "fields": [ { "name": "id", "type": "string", "facet": false }, { "name": "name", "type": "string", "facet": false } ], "enable_nested_fields": true } // dish_tags集合 const schema = { "name": "dish_tags", "num_documents": 0, "fields": [ { "name": "dish_id", "type": "string", "facet": false, "reference": "dishes_view.id" }, { "name": "tags_id", "type": "string", "facet": false, "reference": "tags.id" } ], "enable_nested_fields": true }
解答
1. 查询语句确实存在错误
具体问题如下:
filter_by语法错误:$dish_tags(id: *)不是合法的关联过滤写法,而且dish_tags集合的字段是dish_id和tags_id,并没有id字段。query_by: '*'虽会搜索所有可搜索字段,但如果dishes_view是视图,得保证关联的tags.name被设置为可搜索字段,否则无法通过标签名搜索。include_fields中$tags(*) as tagsData的写法需要关联关系完全正确,否则无法拉取到标签数据。
2. Typesense支持关联搜索,但不是SQL式的JOIN
Typesense靠**集合引用(References)**实现关联数据的搜索和过滤,但属于非规范化关联,需要先在集合间建立引用关系,要么用视图整合数据,要么查询时直接关联拉取。它不支持SQL那种多表JOIN的写法,是通过引用字段关联其他集合的文档。
3. 正确的查询语句(分两种场景)
场景1:用集合引用关联查询
假设dishes和tags已通过dish_tags建立引用关系,正确查询应针对dishes集合,指定搜索菜品名和标签名:
const data = await typesenseClient.collections("dishes").documents().search({ 'q': 'tag1', // 关键词会匹配菜品名和标签名 'query_by': 'name,$tags.name', // 指定搜索字段:菜品name + 关联标签的name 'include_fields': 'name,$tags(*) as tagsData', // 返回菜品名和关联的标签数据 // 如需精准按标签ID过滤,添加以下语句 // 'filter_by': '$tags.id: "你的标签ID"' }) console.log(JSON.stringify(data, null, 2));
场景2:预整合嵌套数据(更推荐)
Typesense是文档型搜索引擎,把标签直接嵌套到菜品文档里会更高效。先修改dishes的Schema:
const schema={ "name": "dishes", "num_documents": 0, "fields": [ { "name": "id", "type": "string", "facet": false }, { "name": "name", "type": "string", "facet": false }, { "name": "unit", "type": "int32", "facet": false, "optional": true }, { "name": "calories", "type": "int32", "facet": false, "optional": true }, { "name": "price", "type": "int32", "facet": false }, { "name": "is_active", "type": "bool", "facet": false }, { "name": "size", "type": "int32", "facet": false, "optional": true }, // 新增嵌套的tags字段 { "name": "tags", "type": "object[]", "facet": false, "fields": [ { "name": "id", "type": "string" }, { "name": "name", "type": "string" } ]} ], "default_sorting_field": "created_at", "enable_nested_fields": true }
之后查询会更简单:
const data = await typesenseClient.collections("dishes").documents().search({ 'q': 'tag1', 'query_by': 'name,tags.name', // 同时搜索菜品名和标签名 'include_fields': 'name,tags' }) console.log(JSON.stringify(data, null, 2));
4. 其他实现方式
- 使用Typesense视图:创建一个视图把
dishes、tags、dish_tags的数据整合到一起,直接在视图上搜索跨关联字段,前提是视图Schema要包含所有需要搜索的字段。 - 写入数据时预关联:存储菜品数据时,直接把对应的标签信息嵌套进去,避免查询时的关联操作,搜索速度更快,这也是Typesense官方推荐的方式。
内容的提问来源于stack exchange,提问作者Nisarg
相关产品推荐
相关产品推荐

