|
44 | 44 | "cell_type": "code",
|
45 | 45 | "execution_count": null,
|
46 | 46 | "metadata": {},
|
47 |
| - "outputs": [ |
48 |
| - { |
49 |
| - "data": { |
50 |
| - "text/plain": [ |
51 |
| - "'postgres://tsdbadmin:[email protected]:36462/tsdb?sslmode=require'" |
52 |
| - ] |
53 |
| - }, |
54 |
| - "execution_count": null, |
55 |
| - "metadata": {}, |
56 |
| - "output_type": "execute_result" |
57 |
| - } |
58 |
| - ], |
| 47 | + "outputs": [], |
59 | 48 | "source": [
|
60 | 49 | "_ = load_dotenv(find_dotenv())\n",
|
61 | 50 | "service_url = os.environ['TIMESCALE_SERVICE_URL']"
|
|
79 | 68 | "import asyncpg\n",
|
80 | 69 | "import uuid\n",
|
81 | 70 | "from pgvector.asyncpg import register_vector\n",
|
82 |
| - "from typing import (List, Optional, Union, Dict, Tuple, Any)\n", |
| 71 | + "from typing import (List, Optional, Union, Dict, Tuple, Any, Iterable)\n", |
83 | 72 | "import json\n",
|
84 | 73 | "import numpy as np\n",
|
85 | 74 | "import math\n",
|
|
266 | 255 | " if id_type.lower() != 'uuid' and id_type.lower() != 'text':\n",
|
267 | 256 | " raise ValueError(f\"unrecognized id_type {id_type}\")\n",
|
268 | 257 | "\n",
|
| 258 | + " if time_partition_interval is not None and id_type.lower() != 'uuid':\n", |
| 259 | + " raise ValueError(f\"time partitioning is only supported for uuid id_type\")\n", |
| 260 | + "\n", |
269 | 261 | " self.id_type = id_type.lower()\n",
|
270 | 262 | " self.time_partition_interval = time_partition_interval\n",
|
271 | 263 | "\n",
|
|
521 | 513 | "text/markdown": [
|
522 | 514 | "---\n",
|
523 | 515 | "\n",
|
524 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L222){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 516 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L225){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
525 | 517 | "\n",
|
526 | 518 | "### QueryBuilder.get_create_query\n",
|
527 | 519 | "\n",
|
|
532 | 524 | "text/plain": [
|
533 | 525 | "---\n",
|
534 | 526 | "\n",
|
535 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L222){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 527 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L225){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
536 | 528 | "\n",
|
537 | 529 | "### QueryBuilder.get_create_query\n",
|
538 | 530 | "\n",
|
|
597 | 589 | " self.service_url = service_url\n",
|
598 | 590 | " self.pool = None\n",
|
599 | 591 | " self.max_db_connections = max_db_connections\n",
|
| 592 | + " self.time_partition_interval = time_partition_interval\n", |
600 | 593 | "\n",
|
601 | 594 | " async def _default_max_db_connections(self) -> int:\n",
|
602 | 595 | " \"\"\"\n",
|
|
653 | 646 | " rec = await pool.fetchrow(query)\n",
|
654 | 647 | " return rec == None\n",
|
655 | 648 | "\n",
|
| 649 | + " def munge_record(self, records) -> Iterable[Tuple[uuid.UUID, str, str, List[float]]]:\n", |
| 650 | + " if self.time_partition_interval is not None:\n", |
| 651 | + " for record in records:\n", |
| 652 | + " id = record[0]\n", |
| 653 | + " if id.variant != uuid.RFC_4122 or id.version != 1:\n", |
| 654 | + " raise ValueError(\"When using time partitioning, id must be a v1 uuid\")\n", |
| 655 | + "\n", |
| 656 | + " metadata_is_dict = isinstance(records[0][1], dict)\n", |
| 657 | + " if metadata_is_dict:\n", |
| 658 | + " records = map(lambda item: Async._convert_record_meta_to_json(item), records)\n", |
| 659 | + "\n", |
| 660 | + " return records \n", |
| 661 | + "\n", |
656 | 662 | " def _convert_record_meta_to_json(item):\n",
|
657 | 663 | " if not isinstance(item[1], dict):\n",
|
658 | 664 | " raise ValueError(\n",
|
|
672 | 678 | " -------\n",
|
673 | 679 | " None\n",
|
674 | 680 | " \"\"\"\n",
|
675 |
| - " if isinstance(records[0][1], dict):\n", |
676 |
| - " records = map(lambda item: Async._convert_record_meta_to_json(item), records)\n", |
| 681 | + " records = self.munge_record(records)\n", |
677 | 682 | " query = self.builder.get_upsert_query()\n",
|
678 | 683 | " async with await self.connect() as pool:\n",
|
679 | 684 | " await pool.executemany(query, records)\n",
|
|
817 | 822 | "text/markdown": [
|
818 | 823 | "---\n",
|
819 | 824 | "\n",
|
820 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L458){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 825 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L533){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
821 | 826 | "\n",
|
822 | 827 | "### Async.create_tables\n",
|
823 | 828 | "\n",
|
|
828 | 833 | "text/plain": [
|
829 | 834 | "---\n",
|
830 | 835 | "\n",
|
831 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L458){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 836 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L533){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
832 | 837 | "\n",
|
833 | 838 | "### Async.create_tables\n",
|
834 | 839 | "\n",
|
|
856 | 861 | "text/markdown": [
|
857 | 862 | "---\n",
|
858 | 863 | "\n",
|
859 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L458){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 864 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L533){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
860 | 865 | "\n",
|
861 | 866 | "### Async.create_tables\n",
|
862 | 867 | "\n",
|
|
867 | 872 | "text/plain": [
|
868 | 873 | "---\n",
|
869 | 874 | "\n",
|
870 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L458){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 875 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L533){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
871 | 876 | "\n",
|
872 | 877 | "### Async.create_tables\n",
|
873 | 878 | "\n",
|
|
907 | 912 | "text/markdown": [
|
908 | 913 | "---\n",
|
909 | 914 | "\n",
|
910 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L556){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 915 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L633){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
911 | 916 | "\n",
|
912 | 917 | "### Async.search\n",
|
913 | 918 | "\n",
|
|
928 | 933 | "text/plain": [
|
929 | 934 | "---\n",
|
930 | 935 | "\n",
|
931 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L556){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 936 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L633){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
932 | 937 | "\n",
|
933 | 938 | "### Async.search\n",
|
934 | 939 | "\n",
|
|
1072 | 1077 | "rec = await vec.search(limit=4, predicates=~Predicates((\"key\", \"val2\"), (\"key_10\", \"<\", 100)))\n",
|
1073 | 1078 | "assert len(rec) == 4\n",
|
1074 | 1079 | "\n",
|
1075 |
| - "\n", |
| 1080 | + "raised = False\n", |
1076 | 1081 | "try:\n",
|
1077 | 1082 | " # can't upsert using both keys and dictionaries\n",
|
1078 | 1083 | " await vec.upsert([\n",
|
1079 | 1084 | " (uuid.uuid4(), {\"key\": \"val\"}, \"the brown fox\", [1.0, 1.2]),\n",
|
1080 | 1085 | " (uuid.uuid4(), '''{\"key2\":\"val\"}''', \"the brown fox\", [1.0, 1.2])\n",
|
1081 | 1086 | " ])\n",
|
1082 |
| - " assert False\n", |
1083 | 1087 | "except ValueError as e:\n",
|
1084 |
| - " pass\n", |
| 1088 | + " raised = True\n", |
| 1089 | + "assert raised\n", |
1085 | 1090 | "\n",
|
| 1091 | + "raised = False\n", |
1086 | 1092 | "try:\n",
|
1087 | 1093 | " # can't upsert using both keys and dictionaries opposite order\n",
|
1088 | 1094 | " await vec.upsert([\n",
|
1089 | 1095 | " (uuid.uuid4(), '''{\"key2\":\"val\"}''', \"the brown fox\", [1.0, 1.2]),\n",
|
1090 | 1096 | " (uuid.uuid4(), {\"key\": \"val\"}, \"the brown fox\", [1.0, 1.2])\n",
|
1091 | 1097 | " ])\n",
|
1092 |
| - " assert False\n", |
1093 | 1098 | "except BaseException as e:\n",
|
1094 |
| - " pass\n", |
| 1099 | + " raised = True\n", |
| 1100 | + "assert raised\n", |
1095 | 1101 | "\n",
|
1096 | 1102 | "rec = await vec.search([1.0, 2.0], limit=4, filter=[{\"key_1\": \"val_1\"}, {\"key2\": \"val2\"}])\n",
|
1097 | 1103 | "assert len(rec) == 2\n",
|
|
1125 | 1131 | "empty = await vec.table_is_empty()\n",
|
1126 | 1132 | "assert empty\n",
|
1127 | 1133 | "await vec.drop_table()\n",
|
| 1134 | + "await vec.close()\n", |
| 1135 | + "\n", |
| 1136 | + "vec = Async(service_url, \"data_table\", 2, time_partition_interval=timedelta(seconds=60))\n", |
| 1137 | + "await vec.create_tables()\n", |
| 1138 | + "empty = await vec.table_is_empty()\n", |
| 1139 | + "assert empty\n", |
| 1140 | + "id = uuid.uuid1()\n", |
| 1141 | + "await vec.upsert([(id, {\"key\": \"val\"}, \"the brown fox\", [1.0, 1.2])])\n", |
| 1142 | + "empty = await vec.table_is_empty()\n", |
| 1143 | + "assert not empty\n", |
| 1144 | + "await vec.delete_by_ids([id])\n", |
| 1145 | + "empty = await vec.table_is_empty()\n", |
| 1146 | + "assert empty\n", |
| 1147 | + "\n", |
| 1148 | + "raised = False\n", |
| 1149 | + "try:\n", |
| 1150 | + " # can't upsert with uuid type 4 in time partitioned table\n", |
| 1151 | + " await vec.upsert([\n", |
| 1152 | + " (uuid.uuid4(), {\"key\": \"val\"}, \"the brown fox\", [1.0, 1.2])\n", |
| 1153 | + " ])\n", |
| 1154 | + "except BaseException as e:\n", |
| 1155 | + " raised = True\n", |
| 1156 | + "assert raised\n", |
| 1157 | + "\n", |
| 1158 | + "await vec.drop_table()\n", |
1128 | 1159 | "await vec.close()"
|
1129 | 1160 | ]
|
1130 | 1161 | },
|
|
1192 | 1223 | " self.service_url = service_url\n",
|
1193 | 1224 | " self.pool = None\n",
|
1194 | 1225 | " self.max_db_connections = max_db_connections\n",
|
| 1226 | + " self.time_partition_interval = time_partition_interval\n", |
1195 | 1227 | " psycopg2.extras.register_uuid()\n",
|
1196 | 1228 | "\n",
|
1197 | 1229 | " def default_max_db_connections(self):\n",
|
|
1285 | 1317 | " cur.execute(query)\n",
|
1286 | 1318 | " rec = cur.fetchone()\n",
|
1287 | 1319 | " return rec == None\n",
|
| 1320 | + " \n", |
| 1321 | + " def munge_record(self, records) -> Iterable[Tuple[uuid.UUID, str, str, List[float]]]:\n", |
| 1322 | + " if self.time_partition_interval is not None:\n", |
| 1323 | + " for record in records:\n", |
| 1324 | + " id = record[0]\n", |
| 1325 | + " if id.variant != uuid.RFC_4122 or id.version != 1:\n", |
| 1326 | + " raise ValueError(\"When using time partitioning, id must be a v1 uuid\")\n", |
| 1327 | + "\n", |
| 1328 | + " metadata_is_dict = isinstance(records[0][1], dict)\n", |
| 1329 | + " if metadata_is_dict:\n", |
| 1330 | + " records = map(lambda item: Sync._convert_record_meta_to_json(item), records)\n", |
| 1331 | + "\n", |
| 1332 | + " return records\n", |
| 1333 | + "\n", |
1288 | 1334 | "\n",
|
1289 | 1335 | " def _convert_record_meta_to_json(item):\n",
|
1290 | 1336 | " if not isinstance(item[1], dict):\n",
|
|
1305 | 1351 | " -------\n",
|
1306 | 1352 | " None\n",
|
1307 | 1353 | " \"\"\"\n",
|
1308 |
| - " if isinstance(records[0][1], dict):\n", |
1309 |
| - " records = list(\n", |
1310 |
| - " map(lambda item: Async._convert_record_meta_to_json(item), records))\n", |
1311 |
| - "\n", |
| 1354 | + " records = self.munge_record(records)\n", |
1312 | 1355 | " query = self.builder.get_upsert_query()\n",
|
1313 | 1356 | " query, _ = self._translate_to_pyformat(query, None)\n",
|
1314 | 1357 | " with self.connect() as conn:\n",
|
|
1479 | 1522 | "text/markdown": [
|
1480 | 1523 | "---\n",
|
1481 | 1524 | "\n",
|
1482 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L727){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 1525 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L827){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
1483 | 1526 | "\n",
|
1484 | 1527 | "### Sync.create_tables\n",
|
1485 | 1528 | "\n",
|
|
1490 | 1533 | "text/plain": [
|
1491 | 1534 | "---\n",
|
1492 | 1535 | "\n",
|
1493 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L727){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 1536 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L827){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
1494 | 1537 | "\n",
|
1495 | 1538 | "### Sync.create_tables\n",
|
1496 | 1539 | "\n",
|
|
1518 | 1561 | "text/markdown": [
|
1519 | 1562 | "---\n",
|
1520 | 1563 | "\n",
|
1521 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L704){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 1564 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L804){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
1522 | 1565 | "\n",
|
1523 | 1566 | "### Sync.upsert\n",
|
1524 | 1567 | "\n",
|
|
1534 | 1577 | "text/plain": [
|
1535 | 1578 | "---\n",
|
1536 | 1579 | "\n",
|
1537 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L704){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 1580 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L804){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
1538 | 1581 | "\n",
|
1539 | 1582 | "### Sync.upsert\n",
|
1540 | 1583 | "\n",
|
|
1567 | 1610 | "text/markdown": [
|
1568 | 1611 | "---\n",
|
1569 | 1612 | "\n",
|
1570 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L841){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 1613 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L944){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
1571 | 1614 | "\n",
|
1572 | 1615 | "### Sync.search\n",
|
1573 | 1616 | "\n",
|
|
1588 | 1631 | "text/plain": [
|
1589 | 1632 | "---\n",
|
1590 | 1633 | "\n",
|
1591 |
| - "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L841){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
| 1634 | + "[source](https://github.com/timescale/python-vector/blob/main/timescale_vector/client.py#L944){target=\"_blank\" style=\"float:right; font-size:smaller\"}\n", |
1592 | 1635 | "\n",
|
1593 | 1636 | "### Sync.search\n",
|
1594 | 1637 | "\n",
|
|
1703 | 1746 | " \"key2\": \"val2\"}, {\"no such key\": \"no such val\"}])\n",
|
1704 | 1747 | "assert len(rec) == 2\n",
|
1705 | 1748 | "\n",
|
| 1749 | + "raised = False\n", |
1706 | 1750 | "try:\n",
|
1707 | 1751 | " # can't upsert using both keys and dictionaries\n",
|
1708 | 1752 | " await vec.upsert([\n",
|
1709 | 1753 | " (uuid.uuid4(), {\"key\": \"val\"}, \"the brown fox\", [1.0, 1.2]),\n",
|
1710 | 1754 | " (uuid.uuid4(), '''{\"key2\":\"val\"}''', \"the brown fox\", [1.0, 1.2])\n",
|
1711 | 1755 | " ])\n",
|
1712 |
| - " assert False\n", |
1713 | 1756 | "except ValueError as e:\n",
|
1714 |
| - " pass\n", |
| 1757 | + " raised = True\n", |
| 1758 | + "assert raised\n", |
1715 | 1759 | "\n",
|
| 1760 | + "raised = False\n", |
1716 | 1761 | "try:\n",
|
1717 | 1762 | " # can't upsert using both keys and dictionaries opposite order\n",
|
1718 | 1763 | " await vec.upsert([\n",
|
1719 | 1764 | " (uuid.uuid4(), '''{\"key2\":\"val\"}''', \"the brown fox\", [1.0, 1.2]),\n",
|
1720 | 1765 | " (uuid.uuid4(), {\"key\": \"val\"}, \"the brown fox\", [1.0, 1.2])\n",
|
1721 | 1766 | " ])\n",
|
1722 |
| - " assert False\n", |
1723 | 1767 | "except BaseException as e:\n",
|
1724 |
| - " pass\n", |
| 1768 | + " raised = True\n", |
| 1769 | + "assert raised\n", |
1725 | 1770 | "\n",
|
1726 | 1771 | "rec = vec.search([1.0, 2.0], filter={\"key_1\": \"val_1\", \"key_2\": \"val_2\"})\n",
|
1727 | 1772 | "assert rec[0][SEARCH_RESULT_CONTENTS_IDX] == 'the brown fox'\n",
|
|
1765 | 1810 | "vec.delete_by_ids([\"Not a valid UUID\"])\n",
|
1766 | 1811 | "assert vec.table_is_empty()\n",
|
1767 | 1812 | "vec.drop_table()\n",
|
| 1813 | + "vec.close()\n", |
| 1814 | + "\n", |
| 1815 | + "vec = Sync(service_url, \"data_table\", 2, time_partition_interval=timedelta(seconds=60))\n", |
| 1816 | + "vec.create_tables()\n", |
| 1817 | + "assert vec.table_is_empty()\n", |
| 1818 | + "id = uuid.uuid1()\n", |
| 1819 | + "vec.upsert([(id, {\"key\": \"val\"}, \"the brown fox\", [1.0, 1.2])])\n", |
| 1820 | + "assert not vec.table_is_empty()\n", |
| 1821 | + "vec.delete_by_ids([id])\n", |
| 1822 | + "assert vec.table_is_empty()\n", |
| 1823 | + "raised = False\n", |
| 1824 | + "try:\n", |
| 1825 | + " # can't upsert with uuid type 4 in time partitioned table\n", |
| 1826 | + " vec.upsert([\n", |
| 1827 | + " (uuid.uuid4(), {\"key\": \"val\"}, \"the brown fox\", [1.0, 1.2])\n", |
| 1828 | + " ])\n", |
| 1829 | + " #pass\n", |
| 1830 | + "except BaseException as e:\n", |
| 1831 | + " raised = True\n", |
| 1832 | + "assert raised\n", |
| 1833 | + "vec.drop_table()\n", |
1768 | 1834 | "vec.close()"
|
1769 | 1835 | ]
|
1770 | 1836 | },
|
|
0 commit comments