平面文件 NoSQL 解决方案

Bas*_*asj 10 python sql database sqlite nosql

对于小型项目,SQLite(或类似的)中是否有内置的方法来保持 SQL/NoSQL 两全其美,即:

  • 存储在(平)文件一样的SQLite(无客户端/服务器方案,没有服务器安装;更确切地说:闲来无事安装除pip install <package>
  • 可以将行存储为dict每行没有通用结构,如 NoSQL 数据库
  • 支持简单查询

例子:

db = NoSQLite('test.db')
db.addrow({'name': 'john doe', 'balance': 1000, 'data': [1, 73.23, 18]})
db.addrow({'name': 'alice', 'balance': 2000, 'email': 'a@b.com'})
for row in db.find('balance > 1500'):
    print(row)

# {'id': 'f565a9fd3a', 'name': 'alice', 'balance': 2000, 'email': 'a@b.com'}   # id was auto-generated
Run Code Online (Sandbox Code Playgroud)

注意:多年来,我一直惊讶于 SQLite 实际上可以在几行代码中实现多少有趣的功能,这就是为什么我问我这里描述的内容是否可以通过 SQLite 仅使用一些 SQLite 核心功能。

PS:可能看起来像一个解决方案,但实际上它只是一个持久的键/值存储,并且它不具有查询/功能; 此外(BerkeleyDB for Python)看起来已被弃用,并且没有类似 API 的查询功能。shelve findbsddb

Sha*_*awn 6

可以通过使用JSON1扩展来查询存储在列中的 JSON 数据,是的:

sqlite> CREATE TABLE test(data TEXT);
sqlite> INSERT INTO test VALUES ('{"name":"john doe","balance":1000,"data":[1,73.23,18]}');
sqlite> INSERT INTO test VALUES ('{"name":"alice","balance":2000,"email":"a@b.com"}');
sqlite> SELECT * FROM test WHERE json_extract(data, '$.balance') > 1500;
data
--------------------------------------------------
{"name":"alice","balance":2000,"email":"a@b.com"}
Run Code Online (Sandbox Code Playgroud)

如果您要多次查询同一个字段,可以通过在表达式上添加索引来提高效率:

CREATE INDEX test_idx_balance ON test(json_extract(data, '$.balance'));
Run Code Online (Sandbox Code Playgroud)

将在上述查询中使用该索引,而不是扫描每一行。


Bas*_*asj 6

SQLite

  • JSON1扩展名和json_extract(见接受的答案)。例子:

    import sqlite3, json  # tested with precompiled Windows binaries from https://www.sqlite.org/download.html (sqlite3.dll copied in C:\Python37\DLLs)
    
    class sqlitenosql:
        def __init__(self, f):
            self.db = sqlite3.connect(f)
            self.db.execute('CREATE TABLE test(data TEXT);')
    
        def close(self):
            self.db.commit()
            self.db.close()
    
        def addrow(self, d):
            self.db.execute("INSERT INTO test VALUES (?);", (json.dumps(d),))
    
        def find(self, query):
            for k, v in query.items():
                if isinstance(v, str):
                    query[k] = f"'{v}'"
            q = ' AND '.join(f" json_extract(data, '$.{k}') = {v}" for k, v in query.items())
            for r in self.db.execute(f"SELECT * FROM test WHERE {q}"):
                yield r[0]
    
    db = sqlitenosql(':memory:')
    db.addrow({'name': 'john', 'balance': 1000, 'data': [1, 73.23, 18], 'abc': 'hello'})
    db.addrow({'name': 'alice', 'balance': 2000, 'email': 'a@b.com'})
    db.addrow({'name': 'bob', 'balance': 1000})
    db.addrow({'name': 'richard', 'balance': 1000, 'abc': 'hello'})
    for r in db.find({'balance': 1000, 'abc': 'hello'}):
        print(r)
    # {"name": "john", "balance": 1000, "data": [1, 73.23, 18], "abc": "hello"}
    # {"name": "richard", "balance": 1000, "abc": "hello"}    
    db.close()
    
    Run Code Online (Sandbox Code Playgroud)
  • Key: value store in Python 中提到的sqlitedict可能有 100 GB 的数据,没有客户端/服务器使用 SQLite 作为键:值存储

    键 = 一个 ID

    value = 我们要存储的字典,例如 {'name': 'alice', 'balance': 2000, 'email': 'a@b.com'}

  • 进一步阅读 SQLite 与 JSON 的使用:https : //community.esri.com/groups/appstudio/blog/2018/08/21/working-with-json-in-sqlite-databases

小数据库

TinyDB看起来是一个不错的解决方案:

>>> from tinydb import TinyDB, Query
>>> db = TinyDB('path/to/db.json')
>>> User = Query()
>>> db.insert({'name': 'John', 'age': 22})
>>> db.search(User.name == 'John')
[{'name': 'John', 'age': 22}]
Run Code Online (Sandbox Code Playgroud)

但是,文档提到如果我们需要,它不是正确的工具:

  • 从多个进程或线程访问,
  • 为表创建索引,
  • 一个 HTTP 服务器,
  • 管理表或类似表之间的关系,
  • 酸保证

所以这是一个半解决方案:)

其他解决方案

看起来也很有趣:WhiteDB