跳转到主要内容

用于建模层次化数据的Harvest Stack应用程序

项目描述

# Harvest-Vocab

Harvest-Vocab提供了一组抽象的Django模型,以便轻松查询层次化数据。大多数数据库系统都没有支持跨多行[逻辑合取](http://en.wikipedia.org/wiki/Logical_conjunction)的运算符。`IN`运算符是[析取的](http://en.wikipedia.org/wiki/Logical_disjunction),因为只需有一个操作数必须为真即可满足真值。Harvest-Vocab提供了构建合取查询(以及其否定)以及排他性合取查询的支持。查询生成通过Django管理器方法公开。

此外,钩子用于与[Harvest](http://harvest.research.chop.edu)应用程序集成,包括一个支持将Avocado运算符映射到构建此类查询的管理器方法的Avocado翻译器。这可以与[Harvest-Vocab客户端](https://github.com/cbmi/harvest-vocab-client)一起使用,该客户端与[Harvest的官方客户端Cilantro](http://cilantro.harvest.io)集成。

## 安装

```bash
pip install harvest-vocab
```

## 问题 & 示例

在医疗保健领域用于计费目的的常见词汇是[ICD9代码](http://en.wikipedia.org/wiki/List_of_ICD-9_codes)。这些代码是分层的,每个级别都比上一个级别更具体。在关系型数据库中存储层次化数据的标准方式是具有与父项(`Patient`模型也在此定义)的_自_关系。

```python
class Diagnosis(models.Model)
code = models.CharField(max_length=10)
description = models.CharField(max_length=50)
parent = models.ForeignKey('self', related_name='children', null=True)


class Patient(models.Model)
diagnoses = models.ManyToManyField(Diagnosis)
# 其他字段...
```

这对于存储数据来说非常合适,但在进行查询时会出现问题。一个示例查询可以是“找到所有患有ICD9 367(屈光不正和调节障碍)的诊断的患者”。执行此查询很简单。

```python
Patient.objects.filter(diagnoses__code='367')
```

然而,这将仅找到具有这种_精确_诊断的患者。问题是“屈光不正和调节障碍”是一个非常一般的_诊断_(它更像是一个类别),并且它有两个级别的代码。任何具有367下的更具体诊断的患者(例如,代码367.1为近视,即近视)都将被排除在结果之外。这种_原始_行为通常不希望,用户期望查询代码的后代。当层次结构的深度未知或任意时,此类查询的编写变得困难(使用Django ORM或原始SQL)。

## 解决方案 & 设置

harvest-vocab用于启用查询任意深度层次结构的方法是利用一个_扁平索引_。也就是说,一个项目将关联到每个祖先直到根。首先定义项目的模型,自我关系的通过模型和索引。

```python
from vocab.models import AbstractItem, AbstractItemIndex

# 继承抽象项目模型
class Diagnosis(AbstractItem)
code = models.CharField(max_length=10)
description = models.CharField(max_length=50)
parent = models.ForeignKey('self', null=True)

# 定义项目/父字段索引
class DiagnosisIndex(AbstractItemIndex)
item = models.ForeignKey(Diagnosis, related_name='item_indexes')
parent = models.ForeignKey(Diagnosis, related_name='parent_indexes')

# 添加关联模型的许多到多字段
class Patient(models.Model)
diagnoses = models.ManyToManyField(Diagnosis, through='PatientDiagnosis')
# 其他字段...

# 患者和诊断之间的通过表,自定义管理器提供
# 自定义运算符的方法
class PatientDiagnosis(models.Model)
diagnosis = models.ForeignKey(Diagnosis, null=True)
patient = models.ForeignKey(Patient, null=True)

objects = ItemThroughManager('diagnosis', 'patient')
```

数据库中创建表后,我们可以通过调用

```python
DiagnosisIndex.objects.index()
```

现在,可以使用上述相同的方式回答上述问题(使用上述相同条件)

```python
diagnosis = Diagnosis.objects.filter(code='367')
# 移除患者ID以用作子查询
subquery = PatientDiagnosis.objects.requires_any(diagnosis)
diagnoses = Diagnosis.objects.filter(id__in=subquery)
```

_以上有些冗长,计划将抽象变得更加透明_。

这利用了索引并返回所有与条件明确匹配或与感兴趣的诊断的后代匹配的诊断。

## 管理器方法

harvest-vocab定义了五个方法以支持基于层次结构的查询,并通过`ItemThroughManager`类公开。

- `requires_any(values)` - 对应于`IN`子句(为完整性定义)
- `excludes_any(values)` - 对应于`NOT IN`子句
- `requires_all(values)` - 要求所有值匹配
- `excludes_all(values)` - 要求所有值_不_匹配
- `only(values)` - 如果对象_仅_包含指定的值则匹配

## Harvest集成

Harvest-Vocab附带一个自定义的Avocado翻译器,它公开与上述管理器方法对应的自定义运算符。必须子类化翻译器并设置`through_model`类属性

```python
from avocado.query.translators import registry
from vocab.translators import VocabularyTranslator
from myapp.models import PatientDiagnosis

class DiagnosisTranslator(VocabularyTranslator)
through_model = PatientDiagnosis

registry.register(DiagnosisTranslator)
```

为了支持[harvest-vocab-client](https://github.com/cbmi/harvest-vocab-client/),必须定义端点以便客户端组件进行查询。将`vocab.urls`包含在`ROOT_URLCONF`模式中。

```python
from django.conf.urls import url, reverse, patterns

urlpatterns = patterns('',
# 其他url模式...

url(r'^vocab/', include('vocab.urls')),
)
```

此外,还需要定义`VOCAB_FIELDS`设置,它是一个包含支持Avocado字段ID的列表/元组。

## 实现方法

自定义操作符使用SQL `CASE`语句实现。一个用于_all_查询的示例输出可能如下所示。虽然这个查询看起来可能很令人畏惧,但重要的部分只是与那些表达式相结合的求和`CASE`语句和`WHERE`条件,例如`sc1`和`sc5`。为了处理数据的层次结构(在本例中是[ICD9代码](http://en.wikipedia.org/wiki/List_of_ICD-9_codes),使用索引表,它允许通过“item_id”= 1与项目本身进行匹配或通过“parent_id”= 1与项目的子项进行匹配。

```sql
SELECT DISTINCT "core_person"."id",
FROM "core_person",
"core_subject"
WHERE "core_person"."id" = "core_subject"."person_id"
AND "core_subject"."id" IN (
SELECT "patient_id"
FROM (
SELECT "patient_id",
SUM(
CASE
WHEN "core_diagnosisindex"."item_id" = 1 THEN 1
WHEN "core_diagnosisindex"."parent_id" = 1 THEN 1
ELSE 0
END
) AS "sc1",
SUM(
CASE
WHEN "core_diagnosisindex"."item_id" = 5 THEN 1
WHEN "core_diagnosisindex"."parent_id" = 5 THEN 1
ELSE 0
END
) AS "sc5",
FROM "core_patientdiagnosis"
INNER JOIN "core_diagnosisindex" ON ("core_patientdiagnosis"."diagnosis_id" = "core_diagnosisindex"."item_id" )
GROUP BY "patient_id"
) AS T
WHERE "sc1" > 0
AND "sc5" > 0
)
```

操作符之间的区别在于是否否定条件以及需要匹配的条件数量。

- **requires all** - 项必须至少匹配所有项一次
- **requires any** - 项必须至少匹配任何项一次(相当于`IN`子句)
- **excludes all** - 项必须不匹配所有项
- **excludes any** - 项必须不匹配任何项(相当于`NOT IN`子句)
- **only** - 项必须只匹配所有项一次,且不能匹配其他任何项


## 资源

Harvest-vocab包含两个自定义资源类,旨在覆盖默认的Serrano值资源。主要资源公开了Serrano的`FieldValuesResource`的超集以确保兼容性。超集公开了`_links`和`id`属性。`_links`对象允许它被爬取并用于层次结构的下级,如果存在`children`条目(它不在下面)。下面是一个示例表示

```javascript
{
"_links": {
"parent": {
"href": "http://localhost:8000/api/fields/2209/values/"
},
"self": {
"href": "http://localhost:8000/api/fields/2209/values/190/"
}
},
"id": 190,
"label": "L-LOOP CORRECTED TRANSPOSITION {SL?}",
"value": 190
}
```

项目详情


下载文件

下载适合您平台的文件。如果您不确定该选择哪一个,请了解更多关于 安装包 的信息。

源代码分发

harvest-vocab-2.0.4.tar.gz (11.9 kB 查看哈希值)

上传时间 源代码

由以下支持