{"id":27933441,"url":"https://github.com/timescale/python-vector","last_synced_at":"2025-05-07T04:58:42.951Z","repository":{"id":194692825,"uuid":"678507514","full_name":"timescale/python-vector","owner":"timescale","description":null,"archived":false,"fork":false,"pushed_at":"2024-10-28T23:48:58.000Z","size":1889,"stargazers_count":26,"open_issues_count":9,"forks_count":1,"subscribers_count":10,"default_branch":"main","last_synced_at":"2025-05-07T04:58:37.949Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://timescale.github.io/python-vector/","language":"Jupyter Notebook","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/timescale.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-08-14T17:59:38.000Z","updated_at":"2025-04-22T19:12:39.000Z","dependencies_parsed_at":"2023-09-28T04:08:08.061Z","dependency_job_id":"5f1640bd-e4a0-4122-920a-c440dd31504d","html_url":"https://github.com/timescale/python-vector","commit_stats":null,"previous_names":["timescale/python-vector"],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timescale%2Fpython-vector","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timescale%2Fpython-vector/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timescale%2Fpython-vector/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/timescale%2Fpython-vector/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/timescale","download_url":"https://codeload.github.com/timescale/python-vector/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":252816948,"owners_count":21808704,"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-05-07T04:58:42.494Z","updated_at":"2025-05-07T04:58:42.932Z","avatar_url":"https://github.com/timescale.png","language":"Jupyter Notebook","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Timescale Vector\n\n\u003c!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! --\u003e\n\nPostgreSQL++ for AI Applications.\n\n- [Signup for Timescale\n  Vector](https://console.cloud.timescale.com/signup?utm_campaign=vectorlaunch\u0026utm_source=github\u0026utm_medium=direct):\n  Get 90 days free to try Timescale Vector on the Timescale cloud data\n  platform. There is no self-managed version at this time.\n- [Documentation](https://timescale.github.io/python-vector/): Learn the\n  key features of Timescale Vector and how to use them.\n- [Getting Started\n  Tutorial](https://timescale.github.io/python-vector/tsv_python_getting_started_tutorial.html):\n  Learn how to use Timescale Vector for semantic search on a real-world\n  dataset.\n- [Learn\n  more](https://www.timescale.com/blog/how-we-made-postgresql-the-best-vector-database/?utm_campaign=vectorlaunch\u0026utm_source=github\u0026utm_medium=direct):\n  Learn more about Timescale Vector, how it works and why we built it.\n\nIf you prefer to use an LLM development or data framework, see Timescale\nVector’s integrations with\n[LangChain](https://python.langchain.com/docs/integrations/vectorstores/timescalevector)\nand\n[LlamaIndex](https://gpt-index.readthedocs.io/en/stable/examples/vector_stores/Timescalevector.html)\n\n## Install\n\nTo install the main library use:\n\n``` sh\npip install timescale_vector\n```\n\nWe also use `dotenv` in our examples for passing around secrets and\nkeys. You can install that with:\n\n``` sh\npip install python-dotenv\n```\n\nIf you run into installation errors related to the psycopg2 package, you\nwill need to install some prerequisites. The timescale-vector package\nexplicitly depends on psycopg2 (the non-binary version). This adheres to\n[the advice provided by\npsycopg2](https://www.psycopg.org/docs/install.html#psycopg-vs-psycopg-binary).\nBuilding psycopg from source [requires a few prerequisites to be\ninstalled](https://www.psycopg.org/docs/install.html#build-prerequisites).\nMake sure these are installed before trying to\n`pip install timescale_vector`.\n\n## Basic usage\n\nFirst, import all the necessary libraries:\n\n``` python\nfrom dotenv import load_dotenv, find_dotenv\nimport os\nfrom timescale_vector import client\nimport uuid\nfrom datetime import datetime, timedelta\n```\n\nLoad up your PostgreSQL credentials. Safest way is with a .env file:\n\n``` python\n_ = load_dotenv(find_dotenv(), override=True) \nservice_url  = os.environ['TIMESCALE_SERVICE_URL']\n```\n\nNext, create the client. In this tutorial, we will use the sync client.\nBut we have an async client as well (with an identical interface that\nuses async functions).\n\nThe client constructor takes three required arguments:\n\n| name           | description                                                                               |\n|----------------|-------------------------------------------------------------------------------------------|\n| service_url    | Timescale service URL / connection string                                                 |\n| table_name     | Name of the table to use for storing the embeddings. Think of this as the collection name |\n| num_dimensions | Number of dimensions in the vector                                                        |\n\nYou can also specify the schema name, distance type, primary key type,\netc. as optional parameters. Please see the documentation for details.\n\n``` python\nvec  = client.Sync(service_url, \"my_data\", 2)\n```\n\nNext, create the tables for the collection:\n\n``` python\nvec.create_tables()\n```\n\nNext, insert some data. The data record contains:\n\n- A UUID to uniquely identify the embedding\n- A JSON blob of metadata about the embedding\n- The text the embedding represents\n- The embedding itself\n\nBecause this data includes UUIDs which become primary keys, we ingest\nwith upserts.\n\n``` python\nvec.upsert([\\\n    (uuid.uuid1(), {\"animal\": \"fox\"}, \"the brown fox\", [1.0,1.3]),\\\n    (uuid.uuid1(), {\"animal\": \"fox\", \"action\":\"jump\"}, \"jumped over the\", [1.0,10.8]),\\\n])\n```\n\nYou can now create a vector index to speed up similarity search:\n\n``` python\nvec.create_embedding_index(client.DiskAnnIndex())\n```\n\nNow, you can query for similar items:\n\n``` python\nvec.search([1.0, 9.0])\n```\n\n    [[UUID('4494c186-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456],\n     [UUID('4494c12c-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'animal': 'fox'},\n      'the brown fox',\n      array([1. , 1.3], dtype=float32),\n      0.14489260377438218]]\n\nThere are many search options which we will cover below in the\n`Advanced search` section.\n\nAs one example, we will return one item using a similarity search\nconstrained by a metadata filter.\n\n``` python\nvec.search([1.0, 9.0], limit=1, filter={\"action\": \"jump\"})\n```\n\n    [[UUID('4494c186-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456]]\n\nThe returned records contain 5 fields:\n\n| name      | description                                             |\n|-----------|---------------------------------------------------------|\n| id        | The UUID of the record                                  |\n| metadata  | The JSON metadata associated with the record            |\n| contents  | the text content that was embedded                      |\n| embedding | The vector embedding                                    |\n| distance  | The distance between the query embedding and the vector |\n\nYou can access the fields by simply using the record as a dictionary\nkeyed on the field name:\n\n``` python\nrecords = vec.search([1.0, 9.0], limit=1, filter={\"action\": \"jump\"})\n(records[0][\"id\"],records[0][\"metadata\"], records[0][\"contents\"], records[0][\"embedding\"], records[0][\"distance\"])\n```\n\n    (UUID('4494c186-4a0d-11ef-94a3-6ee10b77fd09'),\n     {'action': 'jump', 'animal': 'fox'},\n     'jumped over the',\n     array([ 1. , 10.8], dtype=float32),\n     0.00016793422934946456)\n\nYou can delete by ID:\n\n``` python\nvec.delete_by_ids([records[0][\"id\"]])\n```\n\nOr you can delete by metadata filters:\n\n``` python\nvec.delete_by_metadata({\"action\": \"jump\"})\n```\n\nTo delete all records use:\n\n``` python\nvec.delete_all()\n```\n\n## Advanced usage\n\nIn this section, we will go into more detail about our feature. We will\ncover:\n\n1.  Search filter options - how to narrow your search by additional\n    constraints\n2.  Indexing - how to speed up your similarity queries\n3.  Time-based partitioning - how to optimize similarity queries that\n    filter on time\n4.  Setting different distance types to use in distance calculations\n\n### Search options\n\nThe `search` function is very versatile and allows you to search for the\nright vector in a wide variety of ways. We’ll describe the search option\nin 3 parts:\n\n1.  We’ll cover basic similarity search.\n2.  Then, we’ll describe how to filter your search based on the\n    associated metadata.\n3.  Finally, we’ll talk about filtering on time when time-partitioning\n    is enabled.\n\nLet’s use the following data for our example:\n\n``` python\nvec.upsert([\\\n    (uuid.uuid1(), {\"animal\":\"fox\", \"action\": \"sit\", \"times\":1}, \"the brown fox\", [1.0,1.3]),\\\n    (uuid.uuid1(),  {\"animal\":\"fox\", \"action\": \"jump\", \"times\":100}, \"jumped over the\", [1.0,10.8]),\\\n])\n```\n\nThe basic query looks like:\n\n``` python\nvec.search([1.0, 9.0])\n```\n\n    [[UUID('456dbbbc-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456],\n     [UUID('456dbb6c-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 1, 'action': 'sit', 'animal': 'fox'},\n      'the brown fox',\n      array([1. , 1.3], dtype=float32),\n      0.14489260377438218]]\n\nYou could provide a limit for the number of items returned:\n\n``` python\nvec.search([1.0, 9.0], limit=1)\n```\n\n    [[UUID('456dbbbc-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456]]\n\n#### Narrowing your search by metadata\n\nWe have two main ways to filter results by metadata: - `filters` for\nequality matches on metadata. - `predicates` for complex conditions on\nmetadata.\n\nFilters are more likely to be performant but are more limited in what\nthey can express, so we suggest using those if your use case allows it.\n\n##### Filters\n\nYou could specify a match on the metadata as a dictionary where all keys\nhave to match the provided values (keys not in the filter are\nunconstrained):\n\n``` python\nvec.search([1.0, 9.0], limit=1, filter={\"action\": \"sit\"})\n```\n\n    [[UUID('456dbb6c-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 1, 'action': 'sit', 'animal': 'fox'},\n      'the brown fox',\n      array([1. , 1.3], dtype=float32),\n      0.14489260377438218]]\n\nYou can also specify a list of filter dictionaries, where an item is\nreturned if it matches any dict:\n\n``` python\nvec.search([1.0, 9.0], limit=2, filter=[{\"action\": \"jump\"}, {\"animal\": \"fox\"}])\n```\n\n    [[UUID('456dbbbc-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456],\n     [UUID('456dbb6c-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 1, 'action': 'sit', 'animal': 'fox'},\n      'the brown fox',\n      array([1. , 1.3], dtype=float32),\n      0.14489260377438218]]\n\n##### Predicates\n\nPredicates allow for more complex search conditions. For example, you\ncould use greater than and less than conditions on numeric values.\n\n``` python\nvec.search([1.0, 9.0], limit=2, predicates=client.Predicates(\"times\", \"\u003e\", 1))\n```\n\n    [[UUID('456dbbbc-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456]]\n\n[`Predicates`](https://timescale.github.io/python-vector/vector.html#predicates)\nobjects are defined by the name of the metadata key, an operator, and a\nvalue.\n\nThe supported operators are: `==`, `!=`, `\u003c`, `\u003c=`, `\u003e`, `\u003e=`\n\nThe type of the values determines the type of comparison to perform. For\nexample, passing in `\"Sam\"` (a string) will do a string comparison while\na `10` (an int) will perform an integer comparison while a `10.0`\n(float) will do a float comparison. It is important to note that using a\nvalue of `\"10\"` will do a string comparison as well so it’s important to\nuse the right type. Supported Python types are: `str`, `int`, and\n`float`. One more example with a string comparison:\n\n``` python\nvec.search([1.0, 9.0], limit=2, predicates=client.Predicates(\"action\", \"==\", \"jump\"))\n```\n\n    [[UUID('456dbbbc-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456]]\n\nThe real power of predicates is that they can also be combined using the\n`\u0026` operator (for combining predicates with AND semantics) and `|`(for\ncombining using OR semantic). So you can do:\n\n``` python\nvec.search([1.0, 9.0], limit=2, predicates=client.Predicates(\"action\", \"==\", \"jump\") \u0026 client.Predicates(\"times\", \"\u003e\", 1))\n```\n\n    [[UUID('456dbbbc-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456]]\n\nJust for sanity, let’s show a case where no results are returned because\nor predicates:\n\n``` python\nvec.search([1.0, 9.0], limit=2, predicates=client.Predicates(\"action\", \"==\", \"jump\") \u0026 client.Predicates(\"times\", \"==\", 1))\n```\n\n    []\n\nAnd one more example where we define the predicates as a variable and\nuse grouping with parenthesis:\n\n``` python\nmy_predicates = client.Predicates(\"action\", \"==\", \"jump\") \u0026 (client.Predicates(\"times\", \"==\", 1) | client.Predicates(\"times\", \"\u003e\", 1))\nvec.search([1.0, 9.0], limit=2, predicates=my_predicates)\n```\n\n    [[UUID('456dbbbc-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456]]\n\nWe also have some semantic sugar for combining many predicates with AND\nsemantics. You can pass in multiple 3-tuples to\n[`Predicates`](https://timescale.github.io/python-vector/vector.html#predicates):\n\n``` python\nvec.search([1.0, 9.0], limit=2, predicates=client.Predicates((\"action\", \"==\", \"jump\"), (\"times\", \"\u003e\", 10)))\n```\n\n    [[UUID('456dbbbc-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456]]\n\n#### Filter your search by time\n\nWhen using `time-partitioning`(see below). You can very efficiently\nfilter your search by time. Time-partitioning makes a timestamp embedded\nas part of the UUID-based ID associated with an embedding. Let us first\ncreate a collection with time partitioning and insert some data (one\nitem from January 2018 and another in January 2019):\n\n``` python\ntpvec = client.Sync(service_url, \"time_partitioned_table\", 2, time_partition_interval=timedelta(hours=6))\ntpvec.create_tables()\n\nspecific_datetime = datetime(2018, 1, 1, 12, 0, 0)\ntpvec.upsert([\\\n    (client.uuid_from_time(specific_datetime), {\"animal\":\"fox\", \"action\": \"sit\", \"times\":1}, \"the brown fox\", [1.0,1.3]),\\\n    (client.uuid_from_time(specific_datetime+timedelta(days=365)),  {\"animal\":\"fox\", \"action\": \"jump\", \"times\":100}, \"jumped over the\", [1.0,10.8]),\\\n])\n```\n\nThen, you can filter using the timestamps by specifing a\n`uuid_time_filter`:\n\n``` python\ntpvec.search([1.0, 9.0], limit=4, uuid_time_filter=client.UUIDTimeRange(specific_datetime, specific_datetime+timedelta(days=1)))\n```\n\n    [[UUID('33c52800-ef15-11e7-8a12-ea51d07b6447'),\n      {'times': 1, 'action': 'sit', 'animal': 'fox'},\n      'the brown fox',\n      array([1. , 1.3], dtype=float32),\n      0.14489260377438218]]\n\nA\n[`UUIDTimeRange`](https://timescale.github.io/python-vector/vector.html#uuidtimerange)\ncan specify a start_date or end_date or both(as in the example above).\nSpecifying only the start_date or end_date leaves the other end\nunconstrained.\n\n``` python\ntpvec.search([1.0, 9.0], limit=4, uuid_time_filter=client.UUIDTimeRange(start_date=specific_datetime))\n```\n\n    [[UUID('ac8be800-0de6-11e9-a5fd-5a100e653c25'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456],\n     [UUID('33c52800-ef15-11e7-8a12-ea51d07b6447'),\n      {'times': 1, 'action': 'sit', 'animal': 'fox'},\n      'the brown fox',\n      array([1. , 1.3], dtype=float32),\n      0.14489260377438218]]\n\nYou have the option to define the inclusivity of the start and end dates\nwith the `start_inclusive` and `end_inclusive` parameters. Setting\n`start_inclusive` to true results in comparisons using the `\u003e=`\noperator, whereas setting it to false applies the `\u003e` operator. By\ndefault, the start date is inclusive, while the end date is exclusive.\nOne example:\n\n``` python\ntpvec.search([1.0, 9.0], limit=4, uuid_time_filter=client.UUIDTimeRange(start_date=specific_datetime, start_inclusive=False))\n```\n\n    [[UUID('ac8be800-0de6-11e9-a5fd-5a100e653c25'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456]]\n\nNotice how the results are different when we use the\n`start_inclusive=False` option because the first row has the exact\ntimestamp specified by `start_date`.\n\nWe’ve also made it easy to integrate time filters using the `filter` and\n`predicates` parameters described above using special reserved key names\nto make it appear that the timestamps are part of your metadata. We\nfound this useful when integrating with other systems that just want to\nspecify a set of filters (often these are “auto retriever” type\nsystems). The reserved key names are `__start_date` and `__end_date` for\nfilters and `__uuid_timestamp` for predicates. Some examples below:\n\n``` python\ntpvec.search([1.0, 9.0], limit=4, filter={ \"__start_date\": specific_datetime, \"__end_date\": specific_datetime+timedelta(days=1)})\n```\n\n    [[UUID('33c52800-ef15-11e7-8a12-ea51d07b6447'),\n      {'times': 1, 'action': 'sit', 'animal': 'fox'},\n      'the brown fox',\n      array([1. , 1.3], dtype=float32),\n      0.14489260377438218]]\n\n``` python\ntpvec.search([1.0, 9.0], limit=4, \n             predicates=client.Predicates(\"__uuid_timestamp\", \"\u003e=\", specific_datetime) \u0026 client.Predicates(\"__uuid_timestamp\", \"\u003c\", specific_datetime+timedelta(days=1)))\n```\n\n    [[UUID('33c52800-ef15-11e7-8a12-ea51d07b6447'),\n      {'times': 1, 'action': 'sit', 'animal': 'fox'},\n      'the brown fox',\n      array([1. , 1.3], dtype=float32),\n      0.14489260377438218]]\n\n### Indexing\n\nIndexing speeds up queries over your data. By default, we set up indexes\nto query your data by the UUID and the metadata.\n\nBut to speed up similarity search based on the embeddings, you have to\ncreate additional indexes.\n\nNote that if performing a query without an index, you will always get an\nexact result, but the query will be slow (it has to read all of the data\nyou store for every query). With an index, your queries will be\norder-of-magnitude faster, but the results are approximate (because\nthere are no known indexing techniques that are exact).\n\nNevertheless, there are excellent approximate algorithms. There are 3\ndifferent indexing algorithms available on the Timescale platform:\nTimescale Vector index, pgvector HNSW, and pgvector ivfflat. Below are\nthe trade-offs between these algorithms:\n\n| Algorithm        | Build speed | Query speed | Need to rebuild after updates |\n|------------------|-------------|-------------|-------------------------------|\n| StreamingDiskANN | Fast        | Fastest     | No                            |\n| pgvector hnsw    | Slowest     | Faster      | No                            |\n| pgvector ivfflat | Fastest     | Slowest     | Yes                           |\n\nYou can see\n[benchmarks](https://www.timescale.com/blog/how-we-made-postgresql-the-best-vector-database/)\non our blog.\n\nWe recommend using the Timescale Vector index for most use cases. This\ncan be created with:\n\n``` python\nvec.create_embedding_index(client.DiskAnnIndex())\n```\n\nIndexes are created for a particular distance metric type. So it is\nimportant that the same distance metric is set on the client during\nindex creation as it is during queries. See the `distance type` section\nbelow.\n\nEach of these indexes has a set of build-time options for controlling\nthe speed/accuracy trade-off when creating the index and an additional\nquery-time option for controlling accuracy during a particular query. We\nhave smart defaults for all of these options but will also describe the\ndetails below so that you can adjust these options manually.\n\n#### StreamingDiskANN index\n\nThe StreamingDiskANN index from pgvectorscale is a graph-based algorithm\nthat uses the [DiskANN](https://github.com/microsoft/DiskANN) algorithm.\nYou can read more about it on our\n[blog](https://www.timescale.com/blog/how-we-made-postgresql-as-fast-as-pinecone-for-vector-data/)\nannouncing its release.\n\nTo create this index, run:\n\n``` python\nvec.create_embedding_index(client.DiskAnnIndex())\n```\n\nThe above command will create the index using smart defaults. There are\na number of parameters you could tune to adjust the accuracy/speed\ntrade-off.\n\nThe parameters you can set at index build time are:\n\n| Parameter name           | Description                                                                                                                                                                                      | Default value                               |\n|--------------------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------------------------------------|\n| `storage_layout`         | `memory_optimized` which uses SBQ to compress vector data or `plain` which stores data uncompressed                                                                                              | memory_optimized                            |\n| `num_neighbors`          | Sets the maximum number of neighbors per node. Higher values increase accuracy but make the graph traversal slower.                                                                              | 50                                          |\n| `search_list_size`       | This is the S parameter used in the greedy search algorithm used during construction. Higher values improve graph quality at the cost of slower index builds.                                    | 100                                         |\n| `max_alpha`              | Is the alpha parameter in the algorithm. Higher values improve graph quality at the cost of slower index builds.                                                                                 | 1.2                                         |\n| `num_dimensions`         | The number of dimensions to index. By default, all dimensions are indexed. But you can also index less dimensions to make use of [Matryoshka embeddings](https://huggingface.co/blog/matryoshka) | 0 (all dimensions)                          |\n| `num_bits_per_dimension` | Number of bits used to encode each dimension when using SBQ                                                                                                                                      | 2 for less than 900 dimensions, 1 otherwise |\n\nTo set these parameters, you could run:\n\n``` python\nvec.create_embedding_index(client.DiskAnnIndex(num_neighbors=50, search_list_size=100, max_alpha=1.0, storage_layout=\"memory_optimized\", num_dimensions=0, num_bits_per_dimension=1))\n```\n\nYou can also set a parameter to control the accuracy vs. query speed\ntrade-off at query time. The parameter is set in the `search()` function\nusing the `query_params` argment.\n\n| Parameter name     | Description                                                             | Default value |\n|--------------------|-------------------------------------------------------------------------|---------------|\n| `search_list_size` | The number of additional candidates considered during the graph search. | 100           |\n| `rescore`          | The number of elements rescored (0 to disable rescoring)                | 50            |\n\nWe suggest using the `rescore` parameter to fine-tune accuracy.\n\n``` python\nvec.search([1.0, 9.0], limit=4, query_params=client.DiskAnnIndexParams(rescore=400, search_list_size=10))\n```\n\n    [[UUID('456dbbbc-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456],\n     [UUID('456dbb6c-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 1, 'action': 'sit', 'animal': 'fox'},\n      'the brown fox',\n      array([1. , 1.3], dtype=float32),\n      0.14489260377438218]]\n\nTo drop the index, run:\n\n``` python\nvec.drop_embedding_index()\n```\n\n#### pgvector HNSW index\n\nPgvector provides a graph-based indexing algorithm based on the popular\n[HNSW algorithm](https://arxiv.org/abs/1603.09320).\n\nTo create this index, run:\n\n``` python\nvec.create_embedding_index(client.HNSWIndex())\n```\n\nThe above command will create the index using smart defaults. There are\na number of parameters you could tune to adjust the accuracy/speed\ntrade-off.\n\nThe parameters you can set at index build time are:\n\n| Parameter name  | Description                                                                                                                                                                                                                                                            | Default value |\n|-----------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|---------------|\n| m               | Represents the maximum number of connections per layer. Think of these connections as edges created for each node during graph construction. Increasing m increases accuracy but also increases index build time and size.                                             | 16            |\n| ef_construction | Represents the size of the dynamic candidate list for constructing the graph. It influences the trade-off between index quality and construction speed. Increasing ef_construction enables more accurate search results at the expense of lengthier index build times. | 64            |\n\nTo set these parameters, you could run:\n\n``` python\nvec.create_embedding_index(client.HNSWIndex(m=16, ef_construction=64))\n```\n\nYou can also set a parameter to control the accuracy vs. query speed\ntrade-off at query time. The parameter is set in the `search()` function\nusing the `query_params` argument. You can set the `ef_search`(default:\n40). This parameter specifies the size of the dynamic candidate list\nused during search. Higher values improve query accuracy while making\nthe query slower.\n\nYou can specify this value during search as follows:\n\n``` python\nvec.search([1.0, 9.0], limit=4, query_params=client.HNSWIndexParams(ef_search=10))\n```\n\n    [[UUID('456dbbbc-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456],\n     [UUID('456dbb6c-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 1, 'action': 'sit', 'animal': 'fox'},\n      'the brown fox',\n      array([1. , 1.3], dtype=float32),\n      0.14489260377438218]]\n\nTo drop the index run:\n\n``` python\nvec.drop_embedding_index()\n```\n\n#### pgvector ivfflat index\n\nPgvector provides a clustering-based indexing algorithm. Our [blog\npost](https://www.timescale.com/blog/nearest-neighbor-indexes-what-are-ivfflat-indexes-in-pgvector-and-how-do-they-work/)\ndescribes how it works in detail. It provides the fastest index-build\nspeed but the slowest query speeds of any indexing algorithm.\n\nTo create this index, run:\n\n``` python\nvec.create_embedding_index(client.IvfflatIndex())\n```\n\nNote: *ivfflat should never be created on empty tables* because it needs\nto cluster data, and that only happens when an index is first created,\nnot when new rows are inserted or modified. Also, if your table\nundergoes a lot of modifications, you will need to rebuild this index\noccasionally to maintain good accuracy. See our [blog\npost](https://www.timescale.com/blog/nearest-neighbor-indexes-what-are-ivfflat-indexes-in-pgvector-and-how-do-they-work/)\nfor details.\n\nPgvector ivfflat has a `lists` index parameter that is automatically set\nwith a smart default based on the number of rows in your table. If you\nknow that you’ll have a different table size, you can specify the number\nof records to use for calculating the `lists` parameter as follows:\n\n``` python\nvec.create_embedding_index(client.IvfflatIndex(num_records=1000000))\n```\n\nYou can also set the `lists` parameter directly:\n\n``` python\nvec.create_embedding_index(client.IvfflatIndex(num_lists=100))\n```\n\nYou can also set a parameter to control the accuracy vs. query speed\ntrade-off at query time. The parameter is set in the `search()` function\nusing the `query_params` argument. You can set the `probes`. This\nparameter specifies the number of clusters searched during a query. It\nis recommended to set this parameter to `sqrt(lists)` where lists is the\n`num_list` parameter used above during index creation. Higher values\nimprove query accuracy while making the query slower.\n\nYou can specify this value during search as follows:\n\n``` python\nvec.search([1.0, 9.0], limit=4, query_params=client.IvfflatIndexParams(probes=10))\n```\n\n    [[UUID('456dbbbc-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 100, 'action': 'jump', 'animal': 'fox'},\n      'jumped over the',\n      array([ 1. , 10.8], dtype=float32),\n      0.00016793422934946456],\n     [UUID('456dbb6c-4a0d-11ef-94a3-6ee10b77fd09'),\n      {'times': 1, 'action': 'sit', 'animal': 'fox'},\n      'the brown fox',\n      array([1. , 1.3], dtype=float32),\n      0.14489260377438218]]\n\nTo drop the index, run:\n\n``` python\nvec.drop_embedding_index()\n```\n\n### Time partitioning\n\nIn many use cases where you have many embeddings, time is an important\ncomponent associated with the embeddings. For example, when embedding\nnews stories, you often search by time as well as similarity (e.g.,\nstories related to Bitcoin in the past week or stories about Clinton in\nNovember 2016).\n\nYet, traditionally, searching by two components “similarity” and “time”\nis challenging for Approximate Nearest Neighbor (ANN) indexes and makes\nthe similarity-search index less effective.\n\nOne approach to solving this is partitioning the data by time and\ncreating ANN indexes on each partition individually. Then, during\nsearch, you can:\n\n- Step 1: filter our partitions that don’t match the time predicate.\n- Step 2: perform the similarity search on all matching partitions.\n- Step 3: combine all the results from each partition in step 2, rerank,\n  and filter out results by time.\n\nStep 1 makes the search a lot more efficient by filtering out whole\nswaths of data in one go.\n\nTimescale-vector supports time partitioning using TimescaleDB’s\nhypertables. To use this feature, simply indicate the length of time for\neach partition when creating the client:\n\n``` python\nfrom datetime import timedelta\nfrom datetime import datetime\n```\n\n``` python\nvec = client.Async(service_url, \"my_data_with_time_partition\", 2, time_partition_interval=timedelta(hours=6))\nawait vec.create_tables()\n```\n\nThen, insert data where the IDs use UUIDs v1 and the time component of\nthe UUID specifies the time of the embedding. For example, to create an\nembedding for the current time, simply do:\n\n``` python\nid = uuid.uuid1()\nawait vec.upsert([(id, {\"key\": \"val\"}, \"the brown fox\", [1.0, 1.2])])\n```\n\nTo insert data for a specific time in the past, create the UUID using\nour\n[`uuid_from_time`](https://timescale.github.io/python-vector/vector.html#uuid_from_time)\nfunction\n\n``` python\nspecific_datetime = datetime(2018, 8, 10, 15, 30, 0)\nawait vec.upsert([(client.uuid_from_time(specific_datetime), {\"key\": \"val\"}, \"the brown fox\", [1.0, 1.2])])\n```\n\nYou can then query the data by specifying a `uuid_time_filter` in the\nsearch call:\n\n``` python\nrec = await vec.search([1.0, 2.0], limit=4, uuid_time_filter=client.UUIDTimeRange(specific_datetime-timedelta(days=7), specific_datetime+timedelta(days=7)))\n```\n\n### Distance metrics\n\nBy default, we use cosine distance to measure how similarly an embedding\nis to a given query. In addition to cosine distance, we also support\nEuclidean/L2 distance. The distance type is set when creating the client\nusing the `distance_type` parameter. For example, to use the Euclidean\ndistance metric, you can create the client with:\n\n``` python\nvec  = client.Sync(service_url, \"my_data\", 2, distance_type=\"euclidean\")\n```\n\nValid values for `distance_type` are `cosine` and `euclidean`.\n\nIt is important to note that you should use consistent distance types on\nclients that create indexes and perform queries. That is because an\nindex is only valid for one particular type of distance measure.\n\nPlease note the Timescale Vector index only supports cosine distance at\nthis time.\n\n# LangChain integration\n\n[LangChain](https://www.langchain.com/) is a popular framework for\ndevelopment applications powered by LLMs. Timescale Vector has a native\nLangChain integration, enabling you to use Timescale Vector as a\nvectorstore and leverage all its capabilities in your applications built\nwith LangChain.\n\nHere are resources about using Timescale Vector with LangChain:\n\n- [Getting started with LangChain and Timescale\n  Vector](https://python.langchain.com/docs/integrations/vectorstores/timescalevector):\n  You’ll learn how to use Timescale Vector for (1) semantic search, (2)\n  time-based vector search, (3) self-querying, and (4) how to create\n  indexes to speed up queries.\n- [PostgreSQL Self\n  Querying](https://python.langchain.com/docs/integrations/retrievers/self_query/timescalevector_self_query):\n  Learn how to use Timescale Vector with self-querying in LangChain.\n- [LangChain template: RAG with conversational\n  retrieval](https://github.com/langchain-ai/langchain/tree/master/templates/rag-timescale-conversation):\n  This template is used for conversational retrieval, which is one of\n  the most popular LLM use-cases. It passes both a conversation history\n  and retrieved documents into an LLM for synthesis.\n- [LangChain template: RAG with time-based search and self-query\n  retrieval](https://github.com/langchain-ai/langchain/tree/master/templates/rag-timescale-hybrid-search-time):This\n  template shows how to use timescale-vector with the self-query\n  retriver to perform hybrid search on similarity and time. This is\n  useful any time your data has a strong time-based component.\n- [Learn more about Timescale Vector and\n  LangChain](https://blog.langchain.dev/timescale-vector-x-langchain-making-postgresql-a-better-vector-database-for-ai-applications/)\n\n# LlamaIndex integration\n\n\\[LlamaIndex\\] is a popular data framework for connecting custom data\nsources to large language models (LLMs). Timescale Vector has a native\nLlamaIndex integration, enabling you to use Timescale Vector as a\nvectorstore and leverage all its capabilities in your applications built\nwith LlamaIndex.\n\nHere are resources about using Timescale Vector with LlamaIndex:\n\n- [Getting started with LlamaIndex and Timescale\n  Vector](https://docs.llamaindex.ai/en/stable/examples/vector_stores/Timescalevector.html):\n  You’ll learn how to use Timescale Vector for (1) similarity\n  search, (2) time-based vector search, (3) faster search with indexes,\n  and (4) retrieval and query engine.\n- [Time-based\n  retrieval](https://youtu.be/EYMZVfKcRzM?si=I0H3uUPgzKbQw__W): Learn\n  how to power RAG applications with time-based retrieval.\n- [Llama Pack: Auto Retrieval with time-based\n  search](https://github.com/run-llama/llama-hub/tree/main/llama_hub/llama_packs/timescale_vector_autoretrieval):\n  This pack demonstrates performing auto-retrieval for hybrid search\n  based on both similarity and time, using the timescale-vector\n  (PostgreSQL) vectorstore.  \n- [Learn more about Timescale Vector and\n  LlamaIndex](https://www.timescale.com/blog/timescale-vector-x-llamaindex-making-postgresql-a-better-vector-database-for-ai-applications/)\n\n# PgVectorize\n\nPgVectorize enables you to create vector embeddings from any data that\nyou already have stored in PostgreSQL. You can get more background\ninformation in our [blog\npost](https://www.timescale.com/blog/a-complete-guide-to-creating-and-storing-embeddings-for-postgresql-data/)\nannouncing this feature, as well as a [“how we built\nin”](https://www.timescale.com/blog/how-we-designed-a-resilient-vector-embedding-creation-system-for-postgresql-data/)\npost going into the details of the design.\n\nTo create vector embeddings, simply attach PgVectorize to any PostgreSQL\ntable, and it will automatically sync that table’s data with a set of\nembeddings stored in Timescale Vector. For example, let’s say you have a\nblog table defined in the following way:\n\n``` python\nimport psycopg2\nfrom langchain.docstore.document import Document\nfrom langchain.text_splitter import CharacterTextSplitter\nfrom timescale_vector import client, pgvectorizer\nfrom langchain_openai import OpenAIEmbeddings\nfrom langchain_community.vectorstores.timescalevector import TimescaleVector\nfrom datetime import timedelta\n```\n\n``` python\nwith psycopg2.connect(service_url) as conn:\n    with conn.cursor() as cursor:\n        cursor.execute('''\n        CREATE TABLE IF NOT EXISTS blog (\n            id              SERIAL PRIMARY KEY NOT NULL,\n            title           TEXT NOT NULL,\n            author          TEXT NOT NULL,\n            contents        TEXT NOT NULL,\n            category        TEXT NOT NULL,\n            published_time  TIMESTAMPTZ NULL --NULL if not yet published\n        );\n        ''')\n```\n\nYou can insert some data as follows:\n\n``` python\nwith psycopg2.connect(service_url) as conn:\n    with conn.cursor() as cursor:\n        cursor.execute('''\n            INSERT INTO blog (title, author, contents, category, published_time) VALUES ('First Post', 'Matvey Arye', 'some super interesting content about cats.', 'AI', '2021-01-01');\n        ''')\n```\n\nNow, say you want to embed these blogs in Timescale Vector. First, you\nneed to define an `embed_and_write` function that takes a set of blog\nposts, creates the embeddings, and writes them into TimescaleVector. For\nexample, if using LangChain, it could look something like the following.\n\n``` python\ndef get_document(blog):\n    text_splitter = CharacterTextSplitter(\n        chunk_size=1000,\n        chunk_overlap=200,\n    )\n    docs = []\n    for chunk in text_splitter.split_text(blog['contents']):\n        content = f\"Author {blog['author']}, title: {blog['title']}, contents:{chunk}\"\n        metadata = {\n            \"id\": str(client.uuid_from_time(blog['published_time'])),\n            \"blog_id\": blog['id'], \n            \"author\": blog['author'], \n            \"category\": blog['category'],\n            \"published_time\": blog['published_time'].isoformat(),\n        }\n        docs.append(Document(page_content=content, metadata=metadata))\n    return docs\n\ndef embed_and_write(blog_instances, vectorizer):\n    embedding = OpenAIEmbeddings()\n    vector_store = TimescaleVector(\n        collection_name=\"blog_embedding\",\n        service_url=service_url,\n        embedding=embedding,\n        time_partition_interval=timedelta(days=30),\n    )\n\n    # delete old embeddings for all ids in the work queue. locked_id is a special column that is set to the primary key of the table being\n    # embedded. For items that are deleted, it is the only key that is set.\n    metadata_for_delete = [{\"blog_id\": blog['locked_id']} for blog in blog_instances]\n    vector_store.delete_by_metadata(metadata_for_delete)\n\n    documents = []\n    for blog in blog_instances:\n        # skip blogs that are not published yet, or are deleted (in which case it will be NULL)\n        if blog['published_time'] != None:\n            documents.extend(get_document(blog))\n\n    if len(documents) == 0:\n        return\n    \n    texts = [d.page_content for d in documents]\n    metadatas = [d.metadata for d in documents]\n    ids = [d.metadata[\"id\"] for d in documents]\n    vector_store.add_texts(texts, metadatas, ids)\n```\n\nThen, all you have to do is run the following code in a scheduled job\n(cron job, Lambda job, etc):\n\n``` python\n# this job should be run on a schedule\nvectorizer = pgvectorizer.Vectorize(service_url, 'blog')\nwhile vectorizer.process(embed_and_write) \u003e 0:\n    pass\n```\n\nEvery time that job runs, it will sync the table with your embeddings.\nIt will sync all inserts, updates, and deletes to an embeddings table\ncalled `blog_embedding`.\n\nNow, you can simply search the embeddings as follows (again, using\nLangChain in the example):\n\n``` python\nembedding = OpenAIEmbeddings()\nvector_store = TimescaleVector(\n    collection_name=\"blog_embedding\",\n    service_url=service_url,\n    embedding=embedding,\n    time_partition_interval=timedelta(days=30),\n)\n\nres = vector_store.similarity_search_with_score(\"Blogs about cats\")\nres\n```\n\n    [(Document(metadata={'id': '334e4800-4bee-11eb-a52a-57b3c4a96ccb', 'author': 'Matvey Arye', 'blog_id': 1, 'category': 'AI', 'published_time': '2021-01-01T00:00:00-05:00'}, page_content='Author Matvey Arye, title: First Post, contents:some super interesting content about cats.'),\n      0.12680577303752072)]\n\n## Development\n\nThis project is developed with [nbdev](https://nbdev.fast.ai/). Please\nsee that website for the development process.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimescale%2Fpython-vector","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftimescale%2Fpython-vector","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftimescale%2Fpython-vector/lists"}