首先准备数据。我们将使用一个包含《瑞克和莫蒂》中大量台词的CSV文件
!wget http://vectordb-recipes.s3.us-west-2.amazonaws.com/rick_and_morty_quotes.csv
!head rick_and_morty_quotes.csv
--2024-12-17 15:58:31-- http://vectordb-recipes.s3.us-west-2.amazonaws.com/rick_and_morty_quotes.csv Resolving vectordb-recipes.s3.us-west-2.amazonaws.com (vectordb-recipes.s3.us-west-2.amazonaws.com)... 3.5.84.162, 3.5.76.76, 52.92.228.138, ... Connecting to vectordb-recipes.s3.us-west-2.amazonaws.com (vectordb-recipes.s3.us-west-2.amazonaws.com)|3.5.84.162|:80... connected. HTTP request sent, awaiting response... 200 OK Length: 8236 (8.0K) [text/csv] Saving to: ‘rick_and_morty_quotes.csv.3’ rick_and_morty_quot 100%[===================>] 8.04K --.-KB/s in 0s 2024-12-17 15:58:31 (160 MB/s) - ‘rick_and_morty_quotes.csv.3’ saved [8236/8236] id,author,quote 1,Rick," Morty, you got to come on. You got to come with me." 2,Morty," Rick, what’s going on?" 3,Rick," I got a surprise for you, Morty." 4,Morty," It’s the middle of the night. What are you talking about?" 5,Rick," I got a surprise for you." 6,Morty," Ow! Ow! You’re tugging me too hard." 7,Rick," I got a surprise for you, Morty." 8,Rick," What do you think of this flying vehicle, Morty? I built it out of stuff I found in the garage." 9,Morty," Yeah, Rick, it’s great. Is this the surprise?"
让我们将这些数据加载到pandas数据框中。
它包含3列:引文ID、引文字符串和引文作者的名字:
import pandas as pd
df = pd.read_csv("rick_and_morty_quotes.csv")
df.head()
| id | 作者 | 引用 | |
|---|---|---|---|
| 0 | 1 | 瑞克 | 莫蒂,你必须来。你得跟我一起... |
| 1 | 2 | 莫蒂 | 瑞克,发生什么事了? |
| 2 | 3 | Rick | 我给你准备了个惊喜,Morty。 |
| 3 | 4 | 莫蒂 | 现在是半夜。你在干什么... |
| 4 | 5 | Rick | 我给你准备了个惊喜。 |
从pandas数据框创建LanceDB表非常简单,只需使用create_table
我们将从本地LanceDB连接开始
!pip install lancedb -q
import lancedb
async_db = await lancedb.connect_async("~/.lancedb")
await async_db.drop_table("rick_and_morty")
async_table = await async_db.create_table("rick_and_morty", df, mode="overwrite")
await async_table.to_pandas()
[2024-12-17T23:58:46Z WARN lance::dataset::write::insert] No existing dataset at ~/.lancedb/rick_and_morty.lance, it will be created
| id | 作者 | 引用 | |
|---|---|---|---|
| 0 | 1 | 瑞克 | 莫蒂,你必须来。你得跟我一起... |
| 1 | 2 | 莫蒂 | 瑞克,发生什么事了? |
| 2 | 3 | Rick | 我给你准备了个惊喜,Morty。 |
| 3 | 4 | Morty | 现在是半夜。你在干什么... |
| 4 | 5 | Rick | 我给你准备了个惊喜。 |
| 5 | 6 | 莫蒂 | 哎哟!哎哟!你拽得太用力了。 |
| 6 | 7 | Rick | 我给你准备了个惊喜,Morty。 |
| 7 | 8 | Rick | 你觉得这辆飞行器怎么样,Mor... |
| 8 | 9 | 莫蒂 | 是啊,瑞克,太棒了。这就是你说的惊喜吗? |
| 9 | 10 | Rick | Morty,我必须我必须我必须我必须做... |
更新¶
既然瑞克是多元宇宙中最聪明的人,他的名言理应署上全名:理查德·丹尼尔·桑切斯。
这可以通过LanceTable.update实现。它需要两个参数:
- 一个
where字符串过滤器(SQL语法),用于确定要更新的行 - 一个包含
updates的字典,其中键是要更新的列名,值是新值
await async_table.update(where="author='Morty'", updates={"author": "Richard Daniel Sanchez"})
await async_table.to_pandas()
| id | 作者 | 引用 | |
|---|---|---|---|
| 0 | 1 | 瑞克 | 莫蒂,你必须来。你得跟我一起... |
| 1 | 3 | Rick | 我给你准备了个惊喜,Morty。 |
| 2 | 5 | Rick | 我给你准备了个惊喜。 |
| 3 | 7 | Rick | 我给你准备了个惊喜,Morty。 |
| 4 | 8 | Rick | 你觉得这辆飞行器怎么样,Mor... |
| 5 | 10 | 瑞克 | 莫蒂,我必须我必须我必须我必须... |
| 6 | 12 | Rick | 我们准备把它放下去,只要拿到一个谁... |
| 7 | 14 | Rick | 来吧,Morty。放轻松点,Morty。这... |
| 8 | 16 | Rick | 当我投下炸弹时,你知道的,我要你... |
| 9 | 18 | Rick | 而Jessica将扮演Eve,... |
模式演进¶
让我们向表格添加一个new_id列,其中每个值是原始id加1。
await async_table.add_columns({"new_id": "id + 1"})
await async_table.to_pandas()
| id | 作者 | 引用 | 新id | |
|---|---|---|---|---|
| 0 | 1 | 瑞克 | 莫蒂,你必须来。你得跟我一起... | 2 |
| 1 | 3 | 瑞克 | 我给你准备了个惊喜,莫蒂。 | 4 |
| 2 | 5 | Rick | 我给你准备了个惊喜。 | 6 |
| 3 | 7 | 瑞克 | 我给你准备了个惊喜,莫蒂。 | 8 |
| 4 | 8 | Rick | 你觉得这辆飞行汽车怎么样,Mor... | 9 |
| 5 | 10 | 瑞克 | 莫蒂,我必须我必须我必须我必须做... | 11 |
| 6 | 12 | Rick | 我们准备把它放下去,只要得到一个... | 13 |
| 7 | 14 | Rick | 来吧,Morty。放轻松点,Morty。这... | 15 |
| 8 | 16 | Rick | 当我投下炸弹时,你知道的,我要你... | 17 |
| 9 | 18 | Rick | 而Jessica将扮演Eve,... | 19 |
如果我们查看模式(schema),会发现新增了一个int64类型的列
await async_table.schema()
id: int64 author: string quote: string new_id: int64
回滚¶
假设我们使用了该表并发现新列应为不同的值。如何在保留变更历史的同时使用另一个新列?
首先,LanceDB中的主要操作会自动进行版本控制。 版本1是表的创建,包含数据的初始插入。 版本2和3代表更新(删除+追加) 版本4是添加新列。
await async_table.checkout_latest()
await async_table.list_versions()
[{'version': 1,
'timestamp': datetime.datetime(2024, 12, 17, 15, 58, 46, 983259),
'metadata': {}},
{'version': 2,
'timestamp': datetime.datetime(2024, 12, 17, 15, 59, 0, 291948),
'metadata': {}},
{'version': 3,
'timestamp': datetime.datetime(2024, 12, 17, 15, 59, 8, 381165),
'metadata': {}}]
我们可以恢复版本3,即在我们添加new_id向量列之前的状态
await async_table.checkout(2)
await async_table.restore()
await async_table.to_pandas()
| id | 作者 | 引用 | |
|---|---|---|---|
| 0 | 1 | 瑞克 | 莫蒂,你必须来。你得跟我一起... |
| 1 | 3 | Rick | 我给你准备了个惊喜,Morty。 |
| 2 | 5 | Rick | 我给你准备了个惊喜。 |
| 3 | 7 | Rick | 我给你准备了个惊喜,Morty。 |
| 4 | 8 | Rick | 你觉得这辆飞行汽车怎么样,Mor... |
| 5 | 10 | 瑞克 | 莫蒂,我必须我必须我必须我必须... |
| 6 | 12 | Rick | 我们准备把它放下去,只要拿到一个谁... |
| 7 | 14 | Rick | 来吧,Morty。放轻松点,Morty。这... |
| 8 | 16 | Rick | 当我投下炸弹时,你知道的,我要你... |
| 9 | 18 | 瑞克 | 杰西卡将扮演夏娃,... |
请注意,我们现在不是减少而是多了一个版本。当我们恢复旧版本时,并不会删除版本历史记录,而是创建一个新版本,其模式和数据与恢复的旧版本相同。通过这种方式,我们可以跟踪所有变更,并随时回滚到之前的状态。
await async_table.list_versions()
[{'version': 1,
'timestamp': datetime.datetime(2024, 12, 17, 15, 58, 46, 983259),
'metadata': {}},
{'version': 2,
'timestamp': datetime.datetime(2024, 12, 17, 15, 59, 0, 291948),
'metadata': {}},
{'version': 3,
'timestamp': datetime.datetime(2024, 12, 17, 15, 59, 8, 381165),
'metadata': {}},
{'version': 4,
'timestamp': datetime.datetime(2024, 12, 17, 15, 59, 22, 800694),
'metadata': {}}]
添加另一个新列¶
现在我们将更改new_id列的值,并将其再次添加到恢复的数据集中
await async_table.add_columns({"new_id": "id + 10"})
await async_table.schema()
id: int64 author: string quote: string new_id: int64
删除¶
如果整个节目都只是Rick式的台词会怎样? 让我们删除任何不是Rick说的台词
await async_table.delete("author != 'Richard Daniel Sanchez'")
我们可以看到行数已减少到30
await async_table.count_rows()
34
好的,我们玩够了,现在回到完整的报价数据集吧
await async_table.checkout(5)
await async_table.restore()
await async_table.count_rows()
99
历史¶
当前数据中共有9个版本。我们可以在下方查看每个版本对应的操作记录:
await async_table.version()
6
版本:
- 1 - 创建
- 2 - 更新
- 3 - 添加新列
- 4 - 恢复 (2)
- 5 - 添加新列
- 6 - 删除
- 7 - 恢复
概述¶
我们无需显式管理版本控制,也无需创建昂贵且缓慢的快照。LanceDB会自动追踪我创建的所有操作历史,并支持快速回滚。在生产环境中,这对于调试问题至关重要,并能通过秒级回滚到之前成功状态来最小化停机时间。