test_multi_select_facet.py 10.1 KB
#!/usr/bin/env python3
"""
测试 Multi-Select Faceting 和 Selected 标记功能

验证:
1. multi_select=False 时,选中某个 facet 值后,该 facet 只返回一个值(标准模式)
2. multi_select=True 时,选中某个 facet 值后,该 facet 仍返回多个值(disjunctive 模式)
3. selected 字段正确标记
"""

import requests
import json

API_URL = "http://localhost:8000/api/search"
TENANT_ID = "test_tenant"

def test_standard_faceting():
    """测试标准 faceting(multi_select=False)"""
    print("\n" + "="*80)
    print("测试 1: 标准 Faceting (multi_select=False)")
    print("="*80)
    
    # 选择 category1_name = "玩具"
    payload = {
        "query": "*",
        "size": 5,
        "filters": {
            "category1_name": "玩具"
        },
        "facets": [
            {
                "field": "category1_name",
                "size": 10,
                "type": "terms",
                "multi_select": False  # 标准模式
            }
        ]
    }
    
    headers = {"X-Tenant-ID": TENANT_ID}
    
    try:
        response = requests.post(API_URL, json=payload, headers=headers)
        response.raise_for_status()
        result = response.json()
        
        print(f"\n✓ 请求成功 (HTTP {response.status_code})")
        print(f"  总结果数: {result.get('total', 0)}")
        
        if result.get('facets'):
            for facet in result['facets']:
                if facet['field'] == 'category1_name':
                    print(f"\n  Facet: {facet['field']}")
                    print(f"  Values 数量: {len(facet['values'])}")
                    for val in facet['values']:
                        selected_mark = "✓" if val.get('selected') else " "
                        print(f"    [{selected_mark}] {val['value']}: {val['count']}")
                    
                    # 验证
                    if len(facet['values']) == 1:
                        print("\n  ✓ 验证通过: multi_select=False 时,只返回选中的值")
                    else:
                        print("\n  ⚠ 警告: multi_select=False 时应只返回一个值")
                    
                    selected_values = [v['value'] for v in facet['values'] if v.get('selected')]
                    if selected_values == ['玩具']:
                        print("  ✓ 验证通过: selected 字段正确标记")
                    else:
                        print(f"  ✗ 错误: selected 标记不正确,期望 ['玩具'],实际 {selected_values}")
        else:
            print("\n  ⚠ 警告: 没有返回 facets")
            
    except requests.exceptions.RequestException as e:
        print(f"\n✗ 请求失败: {e}")
    except Exception as e:
        print(f"\n✗ 错误: {e}")


def test_multi_select_faceting():
    """测试 Multi-Select Faceting (multi_select=True)"""
    print("\n" + "="*80)
    print("测试 2: Multi-Select Faceting (multi_select=True)")
    print("="*80)
    
    # 选择 category1_name = "玩具"
    payload = {
        "query": "*",
        "size": 5,
        "filters": {
            "category1_name": "玩具"
        },
        "facets": [
            {
                "field": "category1_name",
                "size": 10,
                "type": "terms",
                "multi_select": True  # Multi-select 模式
            }
        ]
    }
    
    headers = {"X-Tenant-ID": TENANT_ID}
    
    try:
        response = requests.post(API_URL, json=payload, headers=headers)
        response.raise_for_status()
        result = response.json()
        
        print(f"\n✓ 请求成功 (HTTP {response.status_code})")
        print(f"  总结果数: {result.get('total', 0)}")
        
        if result.get('facets'):
            for facet in result['facets']:
                if facet['field'] == 'category1_name':
                    print(f"\n  Facet: {facet['field']}")
                    print(f"  Values 数量: {len(facet['values'])}")
                    for val in facet['values'][:5]:  # 只显示前5个
                        selected_mark = "✓" if val.get('selected') else " "
                        print(f"    [{selected_mark}] {val['value']}: {val['count']}")
                    
                    # 验证
                    if len(facet['values']) > 1:
                        print(f"\n  ✓ 验证通过: multi_select=True 时,返回多个值 ({len(facet['values'])} 个)")
                    else:
                        print("\n  ✗ 错误: multi_select=True 时应返回多个值")
                    
                    selected_values = [v['value'] for v in facet['values'] if v.get('selected')]
                    if selected_values == ['玩具']:
                        print("  ✓ 验证通过: selected 字段正确标记")
                    else:
                        print(f"  ⚠ 警告: selected 标记可能不正确,期望 ['玩具'],实际 {selected_values}")
        else:
            print("\n  ⚠ 警告: 没有返回 facets")
            
    except requests.exceptions.RequestException as e:
        print(f"\n✗ 请求失败: {e}")
    except Exception as e:
        print(f"\n✗ 错误: {e}")


def test_specifications_multi_select():
    """测试 Specifications 的 Multi-Select Faceting"""
    print("\n" + "="*80)
    print("测试 3: Specifications Multi-Select Faceting")
    print("="*80)
    
    # 选择 specifications.颜色 = "白色"
    payload = {
        "query": "*",
        "size": 5,
        "filters": {
            "specifications": {
                "name": "颜色",
                "value": "白色"
            }
        },
        "facets": [
            {
                "field": "specifications.颜色",
                "size": 10,
                "type": "terms",
                "multi_select": True
            },
            {
                "field": "specifications.尺寸",
                "size": 10,
                "type": "terms",
                "multi_select": False
            }
        ]
    }
    
    headers = {"X-Tenant-ID": TENANT_ID}
    
    try:
        response = requests.post(API_URL, json=payload, headers=headers)
        response.raise_for_status()
        result = response.json()
        
        print(f"\n✓ 请求成功 (HTTP {response.status_code})")
        print(f"  总结果数: {result.get('total', 0)}")
        
        if result.get('facets'):
            for facet in result['facets']:
                print(f"\n  Facet: {facet['field']} (multi_select={facet.get('multi_select', 'N/A')})")
                print(f"  Values 数量: {len(facet['values'])}")
                for val in facet['values'][:5]:
                    selected_mark = "✓" if val.get('selected') else " "
                    print(f"    [{selected_mark}] {val['value']}: {val['count']}")
                
                # 验证 specifications.颜色
                if facet['field'] == 'specifications.颜色':
                    if len(facet['values']) > 1:
                        print(f"  ✓ 验证通过: multi_select=True,返回多个颜色选项")
                    selected_values = [v['value'] for v in facet['values'] if v.get('selected')]
                    if '白色' in selected_values:
                        print("  ✓ 验证通过: '白色' 被正确标记为 selected")
                
                # 验证 specifications.尺寸(基于白色商品的尺寸分布)
                if facet['field'] == 'specifications.尺寸':
                    print(f"  ℹ 尺寸分布基于已选的颜色过滤器")
        else:
            print("\n  ⚠ 警告: 没有返回 facets")
            
    except requests.exceptions.RequestException as e:
        print(f"\n✗ 请求失败: {e}")
    except Exception as e:
        print(f"\n✗ 错误: {e}")


def test_es_query_structure():
    """测试 ES Query 结构(需要 debug=True)"""
    print("\n" + "="*80)
    print("测试 4: ES Query 结构验证 (debug=True)")
    print("="*80)
    
    payload = {
        "query": "手机",
        "size": 1,
        "filters": {
            "category1_name": "电子产品",
            "specifications": {"name": "颜色", "value": "白色"}
        },
        "facets": [
            {
                "field": "category1_name",
                "size": 5,
                "multi_select": True
            },
            {
                "field": "specifications.颜色",
                "size": 5,
                "multi_select": True
            }
        ],
        "debug": True
    }
    
    headers = {"X-Tenant-ID": TENANT_ID}
    
    try:
        response = requests.post(API_URL, json=payload, headers=headers)
        response.raise_for_status()
        result = response.json()
        
        print(f"\n✓ 请求成功")
        
        if result.get('debug_info') and result['debug_info'].get('es_query'):
            es_query = result['debug_info']['es_query']
            
            # 检查 post_filter
            if 'post_filter' in es_query:
                print("\n  ✓ ES Query 包含 post_filter:")
                print(f"    {json.dumps(es_query['post_filter'], indent=4, ensure_ascii=False)[:200]}...")
            else:
                print("\n  ℹ ES Query 不包含 post_filter(可能没有 multi-select 过滤器)")
            
            # 检查 query.bool.filter
            if 'query' in es_query and 'bool' in es_query['query']:
                filters = es_query['query']['bool'].get('filter', [])
                print(f"\n  ✓ Query filters 数量: {len(filters)}")
                
        else:
            print("\n  ⚠ 警告: debug_info 中没有 es_query")
            
    except requests.exceptions.RequestException as e:
        print(f"\n✗ 请求失败: {e}")
    except Exception as e:
        print(f"\n✗ 错误: {e}")


if __name__ == "__main__":
    print("\n" + "="*80)
    print("Multi-Select Faceting 功能测试")
    print("="*80)
    print(f"\nAPI URL: {API_URL}")
    print(f"Tenant ID: {TENANT_ID}")
    
    # 运行测试
    test_standard_faceting()
    test_multi_select_faceting()
    test_specifications_multi_select()
    test_es_query_structure()
    
    print("\n" + "="*80)
    print("测试完成")
    print("="*80)