typesense / typesense/typesense

Facet by a nested join field leads to issues

Open
#2,863 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

pre-triage
Dominant language
C++
Stars
26.6k
Forks
973
Avg merge
18h 45m
Merged PRs (30d)
4

Description

Bug Description

When you have a data model with multiple joins (product -> variant -> stock), faceting on the nested joins (e.g. a property 'inStock' on the Stock level) leads to timeouts/cancelled requests.

Reproduction Steps

@hostname = localhost:46675
@apiKey = typesense-api-key

DELETE http://{{hostname}}/collections/products
X-TYPESENSE-API-KEY: {{ApiKey}}

###

DELETE http://{{hostname}}/collections/variants
X-TYPESENSE-API-KEY: {{ApiKey}}

###

DELETE http://{{hostname}}/collections/stock
X-TYPESENSE-API-KEY: {{ApiKey}}

### Create Products Collection.

POST http://{{hostname}}/collections
X-TYPESENSE-API-KEY: {{apiKey}}
Content-Type: application/json

{
  "name": "products",
  "fields": [
    {"name": "name", "type": "string", "facet": false, "sort": true},
    {"name": "brand", "type": "string", "facet": true, "sort": true}
  ],
  "metadata": {"revision": "d9765d97-91a8-4042-bbdf-50697f0876ed"}
}

### Create Alias for Products.

PUT http://{{hostname}}/aliases/productsalias
X-TYPESENSE-API-KEY: {{apiKey}}
Content-Type: application/json

{
  "collection_name": "products"
}

### Create Variants Collection.

POST http://{{hostname}}/collections
X-TYPESENSE-API-KEY: {{apiKey}}
Content-Type: application/json

{
  "name": "variants",
  "fields": [
    {"name": "product_id", "type": "string", "facet": true, "reference": "productsalias.id", "async_reference": true},
    {"name": "color", "type": "string", "facet": true, "sort": true}
  ],
  "metadata": {"revision": "8a8793bf-7592-48fc-ac14-ab0896bc9ba0"}
}

### Create Alias for Variants.

PUT http://{{hostname}}/aliases/variantsalias
X-TYPESENSE-API-KEY: {{apiKey}}
Content-Type: application/json

{
  "collection_name": "variants"
}

### Create Stock Collection.

POST http://{{hostname}}/collections
X-TYPESENSE-API-KEY: {{apiKey}}
Content-Type: application/json

{
  "name": "stock",
  "fields": [
    {"name": "variant_id", "type": "string", "reference": "variantsalias.id"},
    {"name": "inStock", "type": "bool", "facet": true, "sort": true}
  ]
}

### Create Alias for Stock.

PUT http://{{hostname}}/aliases/stockalias
X-TYPESENSE-API-KEY: {{apiKey}}
Content-Type: application/json

{
  "collection_name": "stock"
}

### Index Products.

POST http://{{hostname}}/collections/productsalias/documents/import?action=upsert
X-TYPESENSE-API-KEY: {{apiKey}}
Content-Type: application/json

{"id":"p-1","name":"Product 1","brand":"A"}
{"id":"p-2","name":"Product 2","brand":"A"}
{"id":"p-3","name":"Product 3","brand":"B"}
{"id":"p-4","name":"Product 4","brand":"C"}

### Index Variants.

POST http://{{hostname}}/collections/variantsalias/documents/import?action=upsert
X-TYPESENSE-API-KEY: {{apiKey}}
Content-Type: application/json

{"id":"p-1-v-1","product_id":"p-1","color":"Red"}
{"id":"p-1-v-2","product_id":"p-1","color":"Green"}
{"id":"p-1-v-3","product_id":"p-1","color":"Blue"}
{"id":"p-2-v-1","product_id":"p-2","color":"Pink"}
{"id":"p-2-v-2","product_id":"p-2","color":"Red"}
{"id":"p-3-v-1","product_id":"p-3","color":"Red"}
{"id":"p-3-v-2","product_id":"p-3","color":"Blue"}
{"id":"p-4-v-1","product_id":"p-4","color":"Red"}

### Index Stock.

POST http://{{hostname}}/collections/stockalias/documents/import?action=upsert
X-TYPESENSE-API-KEY: {{apiKey}}
Content-Type: application/json

{"id":"p-1-v-1-s-1","variant_id":"p-1-v-1","inStock":true}
{"id":"p-1-v-2-s-2","variant_id":"p-1-v-2","inStock":false}
{"id":"p-4-v-1-s-1","variant_id":"p-4-v-1","inStock":true}

### Facet on 'inStock'

POST http://{{hostname}}/multi_search
X-TYPESENSE-API-KEY: {{apiKey}}
Content-Type: application/json

{
  "searches": [
    {
        "collection": "productsalias",
        "q": "*",
        "filter_by": "id:* || $variantsalias(id:*) || $variantsalias($stockalias(id:*))",
        "facet_by": "brand, $variantsalias(color), $variantsalias($stockalias(inStock))",
        "per_page": 5
    }
  ]
}

The last '$variantsalias($stockalias(inStock))' in facet_by is the part which leads to issues.

As an alternative I tested the following query, which returns no facet values for 'inStock':

{
  "searches": [
    {
        "collection": "productsalias",
        "q": "*",
        "filter_by": "id:* || $variantsalias(id:*) || $variantsalias($stockalias(id:*))",
        "facet_by": "brand, $variantsalias(color), $stockalias(inStock)",
        "per_page": 5
    }
  ]
}

Expected vs Actual

Expected behavior
The correct facet values and facet counts should be returned.

Actual behavior
Typesense does not respond.

Environment

  • Typesense version: e.g. v30.1

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by running the provided multi_search reproduction against Typesense v30.1, focusing on nested facet handling for $variantsalias($stockalias(inStock)). Trace the nested-join faceting path and compare it with the alternative query. Done means the request responds and returns the correct inStock facet values and counts.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp
Domain
search
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
48/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.