wesleysbmd 0.1.3
A powerful block-based markup language parser and query API, fully compatible with Markdown
License: MIT
Requires Python: >=3.10
Keywords: ast, blockmd, documentation, markdown, parser, query

# BlockMD 查询 API

一个功能强大的 BlockMD 文档链式查询 API，支持过滤、排序和多种视图格式。

## 特性

✅ **链式 API** - 流畅的方法链式调用，支持复杂查询  
✅ **文件/目录加载** - 从单个文件或整个目录加载  
✅ **灵活过滤** - 按属性、层级、内容、自定义条件过滤  
✅ **Lambda 支持** - 使用 lambda 函数进行复杂条件判断  
✅ **排序功能** - 按字段或自定义 lambda 函数排序  
✅ **多种视图** - 树形、表格和列表输出格式  
✅ **字段提取** - 提取特定字段为字典  

## 安装

```bash
# 克隆仓库
git clone <repo-url>
cd wesleysbmd

# 运行测试
python tests/test_query_builder.py
```

## 快速开始

```python
from query import query

# 加载并查询 BlockMD 文件
results = (query()
    .from_path('docs/spec.bmd')
    .where(level=lambda l: l >= 2)
    .contains('TODO')
    .order_by('title')
    .view('table')
)

print(results)
```

## API 参考

### 加载数据

#### `from_path(path: str | Path) -> QueryBuilder`
从文件或目录加载区块。

```python
# 加载单个文件
query().from_path('document.bmd')

# 加载目录中所有 .bmd 文件
query().from_path('docs/')
```

#### `from_blocks(blocks: List[Block]) -> QueryBuilder`
从现有 Block 对象初始化。

```python
from parser import parse_blockmd

blocks = parse_blockmd(text)
query().from_blocks(blocks)
```

### 过滤

#### `where(**conditions) -> QueryBuilder`
按属性条件过滤区块。

```python
# 精确匹配
query().where(status='done', level=2)

# Lambda 函数
query().where(level=lambda l: l >= 2)

# 多个值
query().where(status=['todo', 'doing'])

# 特殊字段: title, level
query().where(title='Introduction')
```

#### `has_property(*keys: str) -> QueryBuilder`
过滤具有指定属性的区块。

```python
# 单个属性
query().has_property('status')

# 多个属性 (AND)
query().has_property('status', 'priority')
```

#### `contains(keyword: str, case_sensitive: bool = False) -> QueryBuilder`
过滤内容包含关键词的区块。

```python
# 不区分大小写 (默认)
query().contains('TODO')

# 区分大小写
query().contains('FIXME', case_sensitive=True)
```

#### `filter(predicate: Callable[[Block], bool]) -> QueryBuilder`
使用自定义条件函数过滤。

```python
# 有子节点的区块
query().filter(lambda b: len(b.children) > 0)

# 叶子节点
query().filter(lambda b: b.is_leaf())

# 长内容
query().filter(lambda b: len(b.get_content_text()) > 100)
```

### 排序

#### `order_by(key: str | Callable, reverse: bool = False) -> QueryBuilder`
按字段或函数对区块排序。

```python
# 按字段名排序
query().order_by('title')
query().order_by('level', reverse=True)

# 按属性排序
query().order_by('priority')

# 按 lambda 排序
query().order_by(lambda b: len(b.title))
query().order_by(lambda b: b.get_depth())
```

可用字段名：
- `title` - 区块标题
- `level` - 标题层级 (1-6)
- `depth` - 树深度
- 任何属性键

### 限制

#### `limit(count: int) -> QueryBuilder`
限制结果数量。

```python
# 获取前 10 个结果
query().limit(10)

# 按优先级排序取前 5 个
query().order_by('priority').limit(5)
```

### 输出

#### `view(format: 'tree' | 'table' | 'list', fields: List[str] = None) -> str`
格式化结果以供显示。

```python
# 树形视图 - 层级结构
print(query().view('tree'))
# └── Root [prop=value]
#     ├── Child A
#     └── Child B

# 表格视图 - 结构化列
print(query().view('table', fields=['title', 'level', 'status']))
# title | level | status
# ------+-------+--------
# Block | 2     | done

# 列表视图 - 简单编号列表
print(query().view('list', fields=['title', 'status']))
# 1. Block | status=done
# 2. Other | status=todo
```

#### `select(*fields: str) -> List[dict]`
提取特定字段为字典。

```python
results = query().select('title', 'level', 'content')
# [
#   {'title': 'Intro', 'level': 1, 'content': '...'},
#   {'title': 'Overview', 'level': 2, 'content': '...'}
# ]
```

可用字段名：
- `title`, `level`, `content`, `path`, `depth`
- `id` (来自属性)
- 任何属性键

#### `execute() -> List[Block]`
获取匹配的 Block 对象列表。

```python
blocks = query().where(status='done').execute()
for block in blocks:
    print(block.title, block.level)
```

#### `count() -> int`
统计匹配的区块数量。

```python
total = query().contains('TODO').count()
print(f"Found {total} TODOs")
```

## 示例

### 基础过滤

```python
# 所有二级标题
query().from_path('doc.bmd').where(level=2)

# 已完成的任务
query().from_path('tasks.bmd').where(status='complete')

# 高优先级项目
query().from_path('tasks.bmd').where(priority='high')
```

### Lambda 条件

```python
# 二级或更高层级
query().where(level=lambda l: l >= 2)

# 多个状态值
query().where(status=['todo', 'doing'])

# 复杂条件
query().filter(lambda b: 
    b.level <= 3 and 
    len(b.children) > 0 and
    'TODO' in b.get_content_text()
)
```

### 内容搜索

```python
# 查找所有 TODOs
query().contains('TODO')

# 查找 FIXMEs (区分大小写)
query().contains('FIXME', case_sensitive=True)

# 提及 "database" 的区块
query().contains('database').where(level=2)
```

### 排序

```python
# 按标题字母顺序
query().order_by('title')

# 按深度 (最深优先)
query().order_by('depth', reverse=True)

# 按内容长度
query().order_by(lambda b: len(b.get_content_text()))

# 按子节点数量
query().order_by(lambda b: len(b.children), reverse=True)
```

### 复杂链式调用

```python
# 查找 2 级以上未完成的高优先级任务
results = (query()
    .from_path('project/')
    .where(
        status='todo',
        priority='high',
        level=lambda l: l >= 2
    )
    .order_by('priority')
    .limit(10)
    .view('table', fields=['title', 'priority', 'status'])
)

# 搜索 TODOs，按层级过滤，按深度排序
results = (query()
    .from_path('docs/')
    .contains('TODO')
    .where(level=lambda l: 2 <= l <= 3)
    .order_by('depth')
    .view('tree')
)

# 获取有子节点的模块，按名称排序
modules = (query()
    .from_path('specs/')
    .where(type='module')
    .filter(lambda b: len(b.children) > 0)
    .order_by('title')
    .select('title', 'children_count')
)
```

### 视图格式

```python
# 树形视图 - 查看层级结构
print(query()
    .from_path('project.bmd')
    .where(level=lambda l: l <= 2)
    .view('tree')
)

# 表格视图 - 结构化数据
print(query()
    .from_path('tasks.bmd')
    .where(status='todo')
    .view('table', fields=['title', 'priority', 'status'])
)

# 列表视图 - 简单输出
print(query()
    .from_path('docs.bmd')
    .has_property('section')
    .view('list', fields=['title', 'section'])
)
```

### 统计

```python
# 按状态统计
q = query().from_path('tasks.bmd')
print(f"总数: {q.count()}")
print(f"已完成: {q.where(status='done').count()}")
print(f"待办: {q.where(status='todo').count()}")

# 按层级统计区块
for level in range(1, 4):
    count = q.where(level=level).count()
    print(f"层级 {level}: {count} 个区块")
```

## 高级用法

### 从多个源加载

```python
# 加载多个文件
q = query()
q.from_path('specs/api.bmd')
q.from_path('specs/database.bmd')
results = q.where(type='function').execute()

# 加载整个目录
q = query().from_path('docs/')  # 递归 *.bmd
```

### 自定义过滤器

```python
# 标题较长的区块
query().filter(lambda b: len(b.title) > 50)

# 特定深度的区块
query().filter(lambda b: b.get_depth() == 2)

# 有特定祖先的区块
def has_ancestor(block, title):
    current = block.parent
    while current:
        if current.title == title:
            return True
        current = current.parent
    return False

query().filter(lambda b: has_ancestor(b, 'API'))
```

### 属性继承

BlockMD 支持使用 `key*` 语法的属性继承：

```markdown
# Parent
`status`: active

## Child
`status*`: working

结果: Child 的 status="active/working"
```

查询继承的属性：
```python
# 查找具有继承状态的区块
query().filter(lambda b: '/' in b.get_property('status', ''))
```

## 运行测试

```bash
# 测试 QueryBuilder API
python tests/test_query_builder.py

# 运行所有解析器测试
python tests/test_parser.py
```

## 示例

查看 `examples/query_demo.py` 获取所有特性的完整示例：

```bash
python examples/query_demo.py
```

## API 使用

```python
# 链式、流畅的接口
query()
    .from_path('docs/')
    .where(level=lambda l: l >= 2)
    .contains('TODO')
    .order_by('title')
    .view('table')
```

## BlockMD 格式说明

### 什么是 BlockMD？

BlockMD 是一种与 Markdown 完全兼容的块状标记语言，为标准 Markdown 文件增加了结构化和元数据功能。

### 基本语法

```markdown
# 区块标题
`property`: value
`status`: active

区块内容可以包含任何标准 Markdown 格式。

## 子区块
`status*`: working

子区块继承父区块的属性。
```

### 核心概念

- **区块（Block）**：由 Markdown 标题（`#`、`##` 等）定义
- **层级（Level）**：标题的 `#` 数量（1-6）决定嵌套层级
- **属性（Properties）**：`` `key`: value `` 格式的键值对元数据
- **继承（Inheritance）**：`` `key*`: value `` 语法从父区块继承属性
- **内容（Content）**：标准 Markdown 文本

## 许可证

MIT

## 贡献

欢迎贡献！提交 PR 前请确保测试通过。