跳转到主要内容

Souper - 基于 ZODB 的通用索引存储

项目描述

https://travis-ci.org/bluedynamics/souper.svg?branch=master

ZODB 存储大量(轻量级)数据。

利用

https://raw.githubusercontent.com/bluedynamics/souper/master/docs/Souper-64.png

Souper 是一款面向程序员的工具。它提供了一个与目录中的索引集成的一体化存储。存储中的记录是通用的。如果数据在 ZODB 中是持久的可选择的,则可以在记录中存储任何数据。

Souper 可以用于任何 Python 应用程序,无论是独立使用纯 ZODB,还是与 PyramidZopePlone 结合使用。

使用 Souper

提供定位器

通过将 souper.interfaces.IStorageLocator 适配到某个上下文来查找 Soup。Souper 不提供任何默认定位器。因此,首先需要提供一个。假设上下文是某个持久的类似于字典的实例

>>> from zope.interface import implementer
>>> from zope.interface import Interface
>>> from zope.component import provideAdapter
>>> from souper.interfaces import IStorageLocator
>>> from souper.soup import SoupData
>>> @implementer(IStorageLocator)
... class StorageLocator(object):
...
...     def __init__(self, context):
...        self.context = context
...
...     def storage(self, soup_name):
...        if soup_name not in self.context:
...            self.context[soup_name] = SoupData()
...        return self.context[soup_name]

>>> provideAdapter(StorageLocator, adapts=[Interface])

因此,我们现在可以通过名称动态创建 Soup。现在通过名称获取 Soup 更加容易

>>> from souper.soup import get_soup
>>> soup = get_soup('mysoup', context)
>>> soup
<souper.soup.Soup object at 0x...>

提供目录工厂

根据您的需求,目录及其索引可能因用例而异。目录工厂负责为 Soup 创建目录。工厂是一个名为实用工具,实现 souper.interfaces.ICatalogFactory 的实用工具。实用工具的名称必须与 Soup 相同。

在这里使用 repoze.catalog,并通过使用 NodeAttributeIndexer 允许索引通过键访问记录上的数据。对于特殊情况,可以编写自定义索引器,但默认索引器在大多数情况下就足够了

>>> from souper.interfaces import ICatalogFactory
>>> from souper.soup import NodeAttributeIndexer
>>> from souper.soup import NodeTextIndexer
>>> from zope.component import provideUtility
>>> from repoze.catalog.catalog import Catalog
>>> from repoze.catalog.indexes.field import CatalogFieldIndex
>>> from repoze.catalog.indexes.text import CatalogTextIndex
>>> from repoze.catalog.indexes.keyword import CatalogKeywordIndex

>>> @implementer(ICatalogFactory)
... class MySoupCatalogFactory(object):
...
...     def __call__(self, context=None):
...         catalog = Catalog()
...         userindexer = NodeAttributeIndexer('user')
...         catalog[u'user'] = CatalogFieldIndex(userindexer)
...         textindexer = NodeTextIndexer(['text', 'user')
...         catalog[u'text'] = CatalogTextIndex(textindexer)
...         keywordindexer = NodeAttributeIndexer('keywords')
...         catalog[u'keywords'] = CatalogKeywordIndex(keywordindexer)
...         return catalog

>>> provideUtility(MySoupCatalogFactory(), name="mysoup")

目录工厂仅用于 Soup 内部,但您可能希望检查它是否正常工作

>>> catalogfactory = getUtility(ICatalogFactory, name='mysoup')
>>> catalogfactory
<MySoupCatalogFactory object at 0x...>

>>> catalog = catalogfactory()
>>> sorted(catalog.items())
[(u'keywords', <repoze.catalog.indexes.keyword.CatalogKeywordIndex object at 0x...>),
(u'text', <repoze.catalog.indexes.text.CatalogTextIndex object at 0x...>),
(u'user', <repoze.catalog.indexes.field.CatalogFieldIndex object at 0x...>)]

添加记录

如上所述,souper.soup.Record 是添加到 Soup 的唯一数据类型。记录具有包含数据的属性

>>> from souper.soup import get_soup
>>> from souper.soup import Record
>>> soup = get_soup('mysoup', context)
>>> record = Record()
>>> record.attrs['user'] = 'user1'
>>> record.attrs['text'] = u'foo bar baz'
>>> record.attrs['keywords'] = [u'1', u'2', u'ü']
>>> record_id = soup.add(record)

记录可以包含其他记录。但是,为了对它们进行索引,需要自定义索引器。因此,通常包含的记录对后续显示很有价值,但不是用于搜索

>>> record['subrecord'] = Record()
>>> record['homeaddress'].attrs['zip'] = '6020'
>>> record['homeaddress'].attrs['town'] = 'Innsbruck'
>>> record['homeaddress'].attrs['country'] = 'Austria'

访问数据

即使没有查询,也可以通过 id 获取记录

>>> from souper.soup import get_soup
>>> soup = get_soup('mysoup', context)
>>> record = soup.get(record_id)

可以使用容器 BTree 利用所有记录

>>> soup.data.keys()[0] == record_id
True

查询数据

如何查询 repoze 目录有很好的文档。排序也相同。查询通过 Soup 的 query 方法(使用 repoze 目录)传递。它返回一个生成器

>>> from repoze.catalog.query import Eq
>>> [r for r in soup.query(Eq('user', 'user1'))]
[<Record object 'None' at ...>]

>>> [r for r in soup.query(Eq('user', 'nonexists'))]
[]

要获取结果集的大小,将 with_size=True 传递给查询。生成器返回的第一个项目是大小

>>> [r for r in soup.query(Eq('user', 'user1'), with_size-True)]
[1, <Record object 'None' at ...>]

为了优化大型结果集的处理,可以不获取记录,而是获取返回轻量级对象的生成器。记录在调用时获取

>>> lazy = [l for l in soup.lazy(Eq('name', 'name'))]
>>> lazy
[<souper.soup.LazyRecord object at ...>,

>>> lazy[0]()
<Record object 'None' at ...>

如果传递 with_size=True,则将大小作为生成器的第一个值传递

删除记录

要从 Soup 中删除记录,使用 Python 的 del,就像在任何字典上做的那样

>>> del soup[record]

重新索引

记录的数据更改后,需要重新索引

>>> record.attrs['user'] = 'user1'
>>> soup.reindex(records=[record])

有时可能想要重新索引所有数据。然后必须不带参数调用 reindex。这可能需要一些时间

>>> soup.reindex()

重建目录

通常在更改目录工厂后(即添加了某些索引)需要重建目录。它用目录工厂创建的新目录替换当前目录,并重新索引所有数据。这可能需要一些时间

>>> soup.rebuild()

重置(或清除)Soup

要从 Soup 中删除所有数据并清空并重建目录,请调用 clear

注意所有数据都会丢失!

>>> soup.clear()

源代码

源代码存储在 GIT 分布式版本控制系统(DVCS)中,其主要分支位于 github

我们很高兴看到许多分叉和拉取请求,以使 Souper 更加完善。

贡献者

  • Robert Niederreiter <rnix [at] squarewave [dot] at>

  • Jens W. Klein <jk [at] kleinundpartner [dot] at>

变更日志

1.1.2 (2022-12-05)

  • 发布轮。 [rnix]

1.1.1 (2019-09-16)

  • 清理 NodeTextIndexer(一个循环就足够了)。 [jensens]

1.1.0 (2019-03-08)

  • 代码风格(black,isort,utf8headers)。 [jensens]

  • 已切换到tox进行测试,builodut已不再使用。[jensens]

  • Python 2/3 兼容性 [agitator]

1.0.2 (2015-02-25)

  • 修复:文本索引器中的特殊字符Unicode失败。[jensens, 2014-02-25]

1.0.1

  • PEP-8。[rnix, 2012-10-16]

  • Python 2.7 支持。[rnix, 2012-10-16]

  • 修复文档。

1.0

  • 使其工作 [rnix, jensens, et al]

项目详情


下载文件

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

源分布

souper-1.1.2.tar.gz (25.6 kB 查看哈希值)

上传时间

构建分布

souper-1.1.2-py3-none-any.whl (11.5 kB 查看哈希值)

上传时间 Python 3

支持