{"id":24973313,"url":"https://github.com/bluedynamics/cornerstone.soup","last_synced_at":"2025-03-29T06:13:00.365Z","repository":{"id":5015903,"uuid":"6174484","full_name":"bluedynamics/cornerstone.soup","owner":"bluedynamics","description":"Maintainance of cornerstone.soup","archived":false,"fork":false,"pushed_at":"2012-10-13T13:41:52.000Z","size":223,"stargazers_count":0,"open_issues_count":0,"forks_count":1,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-02-03T18:38:38.613Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"fontello/brandico.font","license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bluedynamics.png","metadata":{"files":{"readme":"README.txt","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2012-10-11T13:45:14.000Z","updated_at":"2014-04-22T18:16:56.000Z","dependencies_parsed_at":"2022-08-29T02:11:02.204Z","dependency_job_id":null,"html_url":"https://github.com/bluedynamics/cornerstone.soup","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluedynamics%2Fcornerstone.soup","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluedynamics%2Fcornerstone.soup/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluedynamics%2Fcornerstone.soup/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bluedynamics%2Fcornerstone.soup/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bluedynamics","download_url":"https://codeload.github.com/bluedynamics/cornerstone.soup/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":246145089,"owners_count":20730495,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-02-03T18:27:10.433Z","updated_at":"2025-03-29T06:13:00.344Z","avatar_url":"https://github.com/bluedynamics.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"Overview\n========\n\n``cornerstone.soup`` provides a container for persistent records which are\nqueryable. It is a genric storage for mass-data in an isolated container. \nLight-weight records are stored in an ``IOBTree``. A Zope-Tool-Kit catalog in \nused to index values of interest. cornerstone.soup  is no out-of-the-box \npackage. Its addressed to developers needing to solve the problem of storing \ntiny entities of mass-data, where heavy weight archetypes or dexterity are too \nmuch effort and are to slow. I.e if you need a container for non-CMSish content, \nlike votes, data from a poll, orders in a webshop, measuring data, or alike.\n\n\nUpdating\n========\n\nIn earlier days of this package we thought it's a good idea to persist the\nsoup data in persistent local components. That was quite a mistake, at least\nin Plone context, because GenericSetup purges local components when applying\nbase profiles - what you're normally not doing, but experience shows that shit\nhappens ;). So we changed the storage location to annotations on an acquireable,\n``ISoupAnnotatable`` providing context.\n\nFurther the soup API was designed as utility, which was basically a good idea,\nbut caused toubles when looking up ``SoupData`` after the storage change.\nWe used ``getSiteManager`` to access the Acquisition context, and encountered\ninconsistencies for accessing the Acquisition context from different site\nmanagers in Plone.\n\nThe second problem forced us more or less to abandon the utility pattern, the\nsoup object itself now acts as adapter for context and is looked up via\n``getSoup`` instead of a utility lookup. After updating, you'll get\n``NoLongerSupported`` errors when trying to access a soup which is provided and\nlooked up as utility. You'll have to change your code to use ``getSoup``,\nand remove the soup local component registration from your GS Profile(s).\n::\n\n    \u003e\u003e\u003e from cornerstone.soup import getSoup\n    \u003e\u003e\u003e soup = getSoup(context, 'mysoup')\n\nThe new package ships with data migration. After updating call\n``soup-controlpanel`` (in Plone) and run storage migration and remove persistent\nlocal component for each soup. Prior to running the storage migration the \nexisting soup data is inaccessible.\n\nDue to the fact that the soup was originally persisted to the ZOBD, it still\ninherits from SimpleItem. This will be changed with the release 3.0.\nKeep in mind that 3.0 will break installations with non-cleaned-up\ndatabases.\n\n\nUsage\n=====\n\n``SoupData`` objects are stored as annotation to an object providing the\n``ISoupAnnotatable`` interface.\n\nFor use inside Plone, provide ``ISoupAnnotatable`` via ``five.implements`` on\nthe plone site object usind ZCML.\n::\n\n    \u003cfive:implements\n        class=\"Products.CMFPlone.Portal.PloneSite\"\n        interface=\"cornerstone.soup.interfaces.ISoupAnnotatable\" /\u003e\n\n``SoupData`` is looked up by ``id`` for a given context. This context acquires\nit's parent until ``ISoupAnnotatable`` is found, on which the ``SoupData`` is\nannotated by ``id``. Use ``getSoup`` function for this.\n::\n\n    \u003e\u003e\u003e from cornerstone.soup import getSoup\n    \u003e\u003e\u003e soup = getSoup(context, 'my_soup_id')\n    \u003e\u003e\u003e soup\n    \u003cSoup at my_soup_id\u003e\n\nIf no ``SoupData`` is found for given id, a new one is created and annotated\nto ``ISoupAnnotatable``.\n\nWe must provide an ``ICatalogFactory`` implementation for each soup, registered\nas utility under the same ``id`` as ``SoupData`` is annotated.\n\nMake sure that Catalog is re-created each time catalog factory gets called. this\nis needed for correct record reindexing.\n::\n\n    \u003e\u003e\u003e from zope.interface import implements\n    \u003e\u003e\u003e from zope.catalog.catalog import Catalog\n    \u003e\u003e\u003e from zope.catalog.field import FieldIndex\n    \u003e\u003e\u003e from cornerstone.soup.interfaces import ICatalogFactory\n    \u003e\u003e\u003e class MyCatalogFactory(object):\n    ...     implements(ICatalogFactory)\n    ...     \n    ...     def __call__(self):\n    ...         catalog = Catalog()\n    ...         catalog[u'name'] = FieldIndex(field_name='name',\n    ...                                       field_callable=False)\n    ...         return catalog\n\nZCML.\n::\n\n    \u003cutility\n        name=\"my_soup_id\"\n        factory=\".mymodule.MyCatalogFactory\"\n        provides=\"cornerstone.soup.interfaces.ICatalogFactory\" /\u003e\n\nA Soup can only contain ``Records``. A Record is a simple persistent object\nwhich accepts any keyword arguments on ``__init__`` time. This arguments are \nused as Record properties.\n\nCreate a Record and add it to soup.\n::\n\n    \u003e\u003e\u003e from cornerstone.soup import Record\n    \u003e\u003e\u003e record = Record(user='user1')\n    \u003e\u003e\u003e id = soup.add(record)\n\nCheck querying.\n::\n\n    \u003e\u003e\u003e [r for r in soup.query(user='user1')]\n    [\u003cRecord at ...\u003e]\n    \n    \u003e\u003e\u003e [r for r in soup.query(user='nonexist')]\n    []\n    \nAdd some more Records.\n::\n\n    \u003e\u003e\u003e id = soup.add(Record(user='user1'))\n    \u003e\u003e\u003e id = soup.add(Record(user='user2'))\n    \u003e\u003e\u003e u1records = [r for r in soup.query(user='user1')]\n    \u003e\u003e\u003e u1records\n    [\u003cRecord at ...\u003e, \n    \u003cRecord at ...\u003e]\n\nChange user attribute of one record.\n::\n\n    \u003e\u003e\u003e u1records[0].data['user'] = 'user2'\n\nThe query still returns the old result. The Record must be reindexed.\n::\n\n    \u003e\u003e\u003e [r for r in soup.query(user='user1')]\n    [\u003cRecord at ...\u003e, \n    \u003cRecord at ...\u003e]\n    \n    \u003e\u003e\u003e soup.reindex([u1records[0]])\n    \n    \u003e\u003e\u003e u1 = [r for r in soup.query(user='user1')]\n    \u003e\u003e\u003e u1\n    [\u003cRecord at ...\u003e]\n    \n    \u003e\u003e\u003e u2 = [r for r in soup.query(user='user2')]\n    \u003e\u003e\u003e u2\n    [\u003cRecord at ...\u003e, \n    \u003cRecord at ...\u003e]\n\nYou can reindex all records in soup at once.\n::\n\n    \u003e\u003e\u003e all = [r for r in soup.data.values()]\n    \u003e\u003e\u003e all = sorted(all, key=lambda x: x.user)\n    \u003e\u003e\u003e all\n    [\u003cRecord at ...\u003e, \n    \u003cRecord at ...\u003e, \n    \u003cRecord at ...\u003e]\n    \n    \u003e\u003e\u003e all[-1].data['user'] = 'user3'\n    \u003e\u003e\u003e soup.reindex()\n    \u003e\u003e\u003e [r for r in soup.query(user='user3')]\n    [\u003cRecord at ...\u003e]\n\nYou can also rebuild the catalog. In this case the catalog factory is called\nagain and the new catalog is used.\n\nAdd index with key name in catalog factory source.\n::\n\n    \u003e\u003e\u003e from zope.catalog.field import FieldIndex\n    \n    \u003e\u003e\u003e catalog[u'name'] = FieldIndex(field_name='name',\n    ...                               field_callable=False)\n\nSet name attribute on some record data, rebuild soup and check results.\n::\n\n    \u003e\u003e\u003e all[0].data['name'] = 'name'\n    \u003e\u003e\u003e all[1].data['name'] = 'name'\n    \u003e\u003e\u003e all[2].data['name'] = 'name'\n    \u003e\u003e\u003e soup.rebuild()\n    \u003e\u003e\u003e [r for r in soup.query(name='name')]\n    [\u003cRecord at ...\u003e, \n    \u003cRecord at ...\u003e, \n    \u003cRecord at ...\u003e]\n    \n\nDelete records.\n::\n\n    \u003e\u003e\u003e del soup[all[0]]\n    \u003e\u003e\u003e [r for r in soup.query(name='name')]\n    [\u003cRecord at ...\u003e, \n    \u003cRecord at ...\u003e]\n\nFor huge expected results we can query LazyRecords. They return the real record\non call.\n::\n\n    \u003e\u003e\u003e lazy = [l for l in soup.lazy(name='name')]\n    \u003e\u003e\u003e lazy\n    [\u003ccornerstone.soup.soup.LazyRecord object at ...\u003e, \n    \u003ccornerstone.soup.soup.LazyRecord object at ...\u003e]\n    \n    \u003e\u003e\u003e lazy[0]()\n    \u003cRecord at ...\u003e\n\n\nText Index NG 3 support\n=======================\n\nThis package provides a zope3 index wrapper for textindexng3. It is located at\n``cornerstone.soup.ting.TingIndex``.\n\nYou can use textindexng3 to index multiple fields of record at once, and make\ncomplex queries to this index. See \n`Products.TextIndexNG3 \u003chttp://pypi.python.org/pypi/Products.TextIndexNG3\u003e`_\nfor more information.\n\nI you want to use textindexng3 with ``cornerstone.soup``, make sure package\n``zopyx.txng3.core`` is installed and it's ZCML is loaded. ``zopyx.txng3.core``\nis NO hard dependency of ``cornerstone.soup``.\n\nA ``TingIndex`` just expects field names as space separated string, or as\niterable. A catalog factory using ``TingIndex`` looks like this.\n::\n\n    \u003e\u003e\u003e class TingCatalogFactory(object):\n    ...     implements(ICatalogFactory)\n    ...     \n    ...     def __call__(self):\n    ...         catalog = Catalog()\n    ...         catalog[u'ting'] = TingIndex(field_name=('foo', 'bar', 'baz'),\n    ...                                      field_callable=False)\n    ...         return catalog\n\nRegister this catalog factory as utility, we use ``tingsoup`` in this\nexample.\n\nQuery textindexng3 using soup.\n::\n\n    \u003e\u003e\u003e soup = getSoup(site, 'tingsoup')\n    \u003e\u003e\u003e soup\n    \u003cSoup at tingsoup\u003e\n\nIndex some records.\n::\n    \n    \u003e\u003e\u003e id = soup.add(Record(foo='foo', bar='bar', baz='baz'))\n    \u003e\u003e\u003e id = soup.add(Record(foo='foobar', bar='barbaz', baz='bazfoo'))\n    \u003e\u003e\u003e id = soup.add(Record(foo='aaa', bar='barrrr', baz='ccc'))\n\nand query them.\n::\n\n    \u003e\u003e\u003e query = {\n    ...     'query': u'bar::and(bar*)',\n    ...     'search_all_fields': True,\n    ... }\n    \u003e\u003e\u003e [r.bar for r in soup.query(ting=query)]\n    ['bar', 'barbaz', 'barrrr']\n\n\nContributors\n============\n\n  * Robert Niederreiter \u003crnix@squarewave.at\u003e\n  * Jens Klein \u003cjens@bluedynamics.com\u003e\n  * Sven Plage\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbluedynamics%2Fcornerstone.soup","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbluedynamics%2Fcornerstone.soup","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbluedynamics%2Fcornerstone.soup/lists"}