数据分析agent(十三_7):meta_demo:ES

3_es_demo.py:

python 复制代码
# es_demo.py
import json
from dataclasses import dataclass
from typing import Optional, List, Dict, Any
from elasticsearch import Elasticsearch
import uuid

from day05_Info_model.meta_demo.meta_config import TableConfig, MetaConfig, MetricConfig, ColumnConfig


# ========== 数据模型(与MySQL相同,略) ==========
# ...(同上的 ColumnConfig, TableConfig, MetricConfig, MetaConfig)

# ========== Elasticsearch 仓储实现 ==========
class ElasticsearchMetaRepository:
    def __init__(self, hosts: List[str] = ["http://localhost:9200"], index_name: str = "meta_configs_demo"):
        self.client = Elasticsearch(hosts=hosts)
        self.index_name = index_name
        self._init_index()

    def _init_index(self):
        """初始化索引"""
        if not self.client.indices.exists(index=self.index_name):
            mapping = {
                "mappings": {
                    "properties": {
                        "config_data": {
                            "type": "text",
                            "fields": {
                                "keyword": {"type": "keyword"}
                            }
                        },
                        "table_names": {
                            "type": "text",
                            "fields": {
                                "keyword": {"type": "keyword"}
                            }
                        },
                        "metric_names": {
                            "type": "text",
                            "fields": {
                                "keyword": {"type": "keyword"}
                            }
                        },
                        "has_tables": {"type": "boolean"},
                        "has_metrics": {"type": "boolean"},
                        "created_at": {"type": "date"},
                        "updated_at": {"type": "date"}
                    }
                }
            }
            self.client.indices.create(index=self.index_name, **mapping)
            print("✅ ES索引创建成功")
        else:
            print("✅ ES索引已存在")

    def _prepare_document(self, entity: MetaConfig, doc_id: Optional[str] = None) -> Dict[str, Any]:
        """准备ES文档"""
        doc = {
            "config_data": json.dumps(entity.to_dict(), ensure_ascii=False),
            "table_names": [t.name for t in entity.tables] if entity.tables else [],
            "metric_names": [m.name for m in entity.metrics] if entity.metrics else [],
            "has_tables": bool(entity.tables),
            "has_metrics": bool(entity.metrics)
        }
        if doc_id:
            doc["_id"] = doc_id
        return doc

    def _doc_to_entity(self, doc: Dict[str, Any]) -> MetaConfig:
        """ES文档转实体"""
        data = json.loads(doc["_source"]["config_data"])
        return MetaConfig.from_dict(data)

    # ========== CRUD 操作 ==========
    def create(self, entity: MetaConfig) -> str:
        """创建配置,返回ID"""
        doc_id = str(uuid.uuid4())
        doc = self._prepare_document(entity)

        response = self.client.index(
            index=self.index_name,
            id=doc_id,
            document=doc
        )
        print(f"✅ ES创建成功,ID: {doc_id}")
        return doc_id

    def get_by_id(self, id: str) -> Optional[MetaConfig]:
        """根据ID获取配置"""
        try:
            result = self.client.get(index=self.index_name, id=id)
            if result and result["found"]:
                return self._doc_to_entity(result)
        except Exception as e:
            print(f"❌ 查询失败: {e}")
        return None

    def get_all(self, size: int = 100) -> List[MetaConfig]:
        """获取所有配置"""
        response = self.client.search(
            index=self.index_name,
            query={"match_all": {}},
            size=size
        )

        return [self._doc_to_entity(hit) for hit in response["hits"]["hits"]]

    def update(self, id: str, entity: MetaConfig) -> bool:
        """更新配置"""
        try:
            # 检查是否存在
            existing = self.client.get(index=self.index_name, id=id)
            if not existing or not existing["found"]:
                print(f"❌ ID {id} 不存在")
                return False

            doc = self._prepare_document(entity)
            self.client.index(
                index=self.index_name,
                id=id,
                document=doc
            )
            print(f"✅ ES更新成功,ID: {id}")
            return True
        except Exception as e:
            print(f"❌ 更新失败: {e}")
            return False

    def delete(self, id: str) -> bool:
        """删除配置"""
        try:
            response = self.client.delete(index=self.index_name, id=id)
            success = response["result"] == "deleted"
            if success:
                print(f"✅ ES删除成功,ID: {id}")
            else:
                print(f"❌ ID {id} 不存在")
            return success
        except Exception as e:
            print(f"❌ 删除失败: {e}")
            return False

    def search(self, query: str, size: int = 100, **kwargs) -> List[MetaConfig]:
        """全文搜索"""
        search_fields = kwargs.get("fields", ["table_names", "metric_names", "config_data"])

        must = [
            {
                "multi_match": {
                    "query": query,
                    "fields": search_fields,
                    "operator": "or"
                }
            }
        ]

        # 添加过滤器
        if "has_tables" in kwargs:
            must.append({"term": {"has_tables": kwargs["has_tables"]}})
        if "has_metrics" in kwargs:
            must.append({"term": {"has_metrics": kwargs["has_metrics"]}})

        response = self.client.search(
            index=self.index_name,
            query={"bool": {"must": must}},
            size=size
        )

        return [self._doc_to_entity(hit) for hit in response["hits"]["hits"]]

    def search_tables(self, query: str) -> List[MetaConfig]:
        """搜索包含特定表的配置"""
        return self.search(query, fields=["table_names"])

    def search_metrics(self, query: str) -> List[MetaConfig]:
        """搜索包含特定指标的配置"""
        return self.search(query, fields=["metric_names"])

    def search_exact(self, field: str, value: str) -> List[MetaConfig]:
        """精确搜索"""
        response = self.client.search(
            index=self.index_name,
            query={"term": {field: value}},
            size=100
        )
        return [self._doc_to_entity(hit) for hit in response["hits"]["hits"]]


# ========== 演示函数 ==========
def es_demo():
    print("=" * 60)
    print("🔷 Elasticsearch 元数据管理演示")
    print("=" * 60)

    # 1. 初始化
    repo = ElasticsearchMetaRepository(
        hosts=["http://localhost:9200"],
        index_name="meta_configs"
    )

    # 2. 准备测试数据
    print("\n📦 准备测试数据...")

    columns = [
        ColumnConfig(
            name="order_id",
            role="primary_key",
            description="订单唯一标识",
            alias=["订单ID", "单号"],
            sync=False
        ),
        ColumnConfig(
            name="order_date",
            role="dimension",
            description="订单创建日期",
            alias=["日期", "下单日期"],
            sync=True
        ),
        ColumnConfig(
            name="order_status",
            role="dimension",
            description="订单状态,如已支付、已发货等",
            alias=["状态", "订单状态"],
            sync=True
        )
    ]

    tables = [
        TableConfig(
            name="fact_order",
            role="fact",
            description="订单事实表,存储所有订单交易数据",
            columns=columns
        )
    ]

    metrics = [
        MetricConfig(
            name="GMV",
            description="全称Gross Merchandise value,表示所有订单的成交金额总和",
            relevant_columns=["fact_order.order_amount"],
            alias=["成交总额", "订单总额"]
        ),
        MetricConfig(
            name="AOV",
            description="全称Average Order value,表示所有订单的成交金额总的平均值",
            relevant_columns=["fact_order.order_amount", "fact_order.order_id"],
            alias=["平均单价", "平均订单金额"]
        )
    ]

    meta_config = MetaConfig(tables=tables, metrics=metrics)

    # 3. 创建
    print("\n📝 创建配置...")
    config_id = repo.create(meta_config)

    # 4. 查询
    print("\n🔍 根据ID查询...")
    retrieved = repo.get_by_id(config_id)
    if retrieved:
        print(f"  查询成功!")
        print(f"  表数量: {len(retrieved.tables) if retrieved.tables else 0}")
        print(f"  指标数量: {len(retrieved.metrics) if retrieved.metrics else 0}")

    # 5. 全文搜索
    print("\n🔍 全文搜索 '订单 交易'...")
    search_results = repo.search("订单 交易")
    print(f"  找到 {len(search_results)} 个配置")

    # 显示结果详情
    for i, config in enumerate(search_results[:3], 1):
        table_names = [t.name for t in config.tables] if config.tables else []
        metric_names = [m.name for m in config.metrics] if config.metrics else []
        print(f"  {i}. 表: {table_names}, 指标: {metric_names}")

    # 6. 按表名搜索
    print("\n🔍 按表名搜索 'fact_order'...")
    table_results = repo.search_tables("fact_order")
    print(f"  找到 {len(table_results)} 个配置")

    # 7. 按指标名搜索
    print("\n🔍 按指标名搜索 'GMV'...")
    metric_results = repo.search_metrics("GMV")
    print(f"  找到 {len(metric_results)} 个配置")

    # 8. 创建第二个配置(用于演示多条件搜索)
    print("\n📝 创建第二个配置...")
    columns2 = [
        ColumnConfig(
            name="product_id",
            role="primary_key",
            description="商品唯一标识",
            alias=["商品ID", "产品ID"],
            sync=False
        ),
        ColumnConfig(
            name="product_name",
            role="dimension",
            description="商品名称",
            alias=["商品名", "产品名"],
            sync=True
        )
    ]
    tables2 = [
        TableConfig(
            name="dim_product",
            role="dim",
            description="商品维度表,存储商品信息",
            columns=columns2
        )
    ]
    meta_config2 = MetaConfig(tables=tables2, metrics=None)
    config_id2 = repo.create(meta_config2)

    # 9. 过滤搜索(只搜索有指标的配置)
    print("\n🔍 过滤搜索:只搜索有指标的配置...")
    filtered_results = repo.search("order", has_metrics=True)
    print(f"  找到 {len(filtered_results)} 个配置")

    # 10. 精确搜索
    print("\n🔍 精确搜索:表名 = 'dim_product'...")
    exact_results = repo.search_exact("table_names", "dim_product")
    print(f"  找到 {len(exact_results)} 个配置")

    # 11. 获取所有
    print("\n📋 获取所有配置...")
    all_configs = repo.get_all()
    print(f"  共有 {len(all_configs)} 个配置")

    # 12. 删除
    print("\n🗑️ 删除配置...")
    repo.delete(config_id)
    repo.delete(config_id2)
    print("  删除成功!")

    print("\n" + "=" * 60)
    print("✅ Elasticsearch演示完成!")
    print("=" * 60)


if __name__ == "__main__":
    es_demo()

'''
 ============================================================
🔷 Elasticsearch 元数据管理演示
============================================================
✅ ES索引创建成功

📦 准备测试数据...

📝 创建配置...
✅ ES创建成功,ID: 6a7522e1-c660-40b5-895e-0453c9a5fe7b

🔍 根据ID查询...
  查询成功!
  表数量: 1
  指标数量: 2

🔍 全文搜索 '订单 交易'...
  找到 0 个配置

🔍 按表名搜索 'fact_order'...
  找到 0 个配置

🔍 按指标名搜索 'GMV'...
  找到 0 个配置

📝 创建第二个配置...
✅ ES创建成功,ID: ae6624b5-9f30-4a8d-97b8-852eff053683

🔍 过滤搜索:只搜索有指标的配置...
  找到 0 个配置

🔍 精确搜索:表名 = 'dim_product'...
  找到 0 个配置

📋 获取所有配置...
  共有 0 个配置

🗑️ 删除配置...
✅ ES删除成功,ID: 6a7522e1-c660-40b5-895e-0453c9a5fe7b
✅ ES删除成功,ID: ae6624b5-9f30-4a8d-97b8-852eff053683
  删除成功!

============================================================
✅ Elasticsearch演示完成!
============================================================
 '''
相关推荐
Elasticsearch2 小时前
Elastic Observability 更新后的指标定价:业界领先的指标能力 —— 现在成本也更低!
elasticsearch
Elasticsearch2 小时前
Elastic 新的指标能力将显著提升公共部门 IT 的正常运行时间
elasticsearch
数智化管理手记3 小时前
主数据重复、错漏频发?一站式主数据管理平台如何落地?
大数据·运维·数据库·人工智能·数据挖掘
KKKlucifer5 小时前
多源异构通信数据统一识别:运营商分类分级平台关键技术与落地成
人工智能·分类·数据挖掘
Elasticsearch5 小时前
Elastic 作为创始成员加入 Open Secure AI Alliance,与 NVIDIA 及行业领导者共同推动 AI 安全与保障
elasticsearch
Elasticsearch5 小时前
你的 agents 一直在记录凭证:将 Elastic Agent Builder 内置的 OTel traces 转换为 Kibana 中的 token 成本
elasticsearch
Elasticsearch7 小时前
一个提示词,一个完整工作流:Elastic 的 AI agent 为你编写自动化流程
elasticsearch
ai_coder_ai8 小时前
数据挖掘方法及应用
人工智能·数据挖掘
Elasticsearch8 小时前
快 17% 的搜索,零配置:Elasticsearch 中的自动校准向量量化
elasticsearch