ComponentsVector storesTimescale Vector (Postgres)On this pageTimescale Vector (Postgres)This notebook shows how to use the Postgres vector database Timescale Vector. You'll learn how to use TimescaleVector for (1) semantic search, (2) time-based vector search, (3) self-querying, and (4) how to create indexes to speed up queries.What is Timescale Vector?​Timescale Vector is PostgreSQL++ for AI applications.Timescale Vector enables you to efficiently store and query millions of vector embeddings in PostgreSQL.Enhances pgvector with faster and more accurate similarity search on 100M+ vectors via DiskANN inspired indexing algorithm.Enables fast time-based vector search via automatic time-based partitioning and indexing.Provides a familiar SQL interface for querying vector embeddings and relational data.Timescale Vector is cloud PostgreSQL for AI that scales with you from POC to production:Simplifies operations by enabling you to store relational metadata, vector embeddings, and time-series data in a single database.Benefits from rock-solid PostgreSQL foundation with enterprise-grade feature liked streaming backups and replication, high-availability and row-level security.Enables a worry-free experience with enterprise-grade security and compliance.How to access Timescale Vector​Timescale Vector is available on Timescale, the cloud PostgreSQL platform. (There is no self-hosted version at this time.)LangChain users get a 90-day free trial for Timescale Vector.To get started, signup to Timescale, create a new database and follow this notebook!See the Timescale Vector explainer blog for more details and performance benchmarks.See the installation instructions for more details on using Timescale Vector in python.Setup​Follow these steps to get ready to follow this tutorial.# Pip install necessary packagespip install timescale-vectorpip install openaipip install tiktokenIn this example, we'll use OpenAIEmbeddings, so let's load your OpenAI API key.import os# Run export OPENAI_API_KEY=sk-YOUR_OPENAI_API_KEY...# Get openAI api key by reading local .env filefrom dotenv import load_dotenv, find_dotenv_ = load_dotenv(find_dotenv())OPENAI_API_KEY  = os.environ['OPENAI_API_KEY']# Get the API key and save it as an environment variable#import os#import getpass#os.environ["OPENAI_API_KEY"] = getpass.getpass("OpenAI API Key:")from typing import List, TupleNext we'll import the needed Python libraries and libraries from LangChain. Note that we import the timescale-vector library as well as the TimescaleVector LangChain vectorstore.import timescale_vectorfrom datetime import datetime, timedeltafrom langchain.embeddings.openai import OpenAIEmbeddingsfrom langchain.text_splitter import CharacterTextSplitterfrom langchain.document_loaders import TextLoaderfrom langchain.document_loaders.json_loader import JSONLoaderfrom langchain.docstore.document import Documentfrom langchain.vectorstores.timescalevector import TimescaleVector1. Similarity Search with Euclidean Distance (Default)​First, we'll look at an example of doing a similarity search query on the State of the Union speech to find the most similar sentences to a given query sentence. We'll use the Euclidean distance as our similarity metric.# Load the text and split it into chunksloader = TextLoader("../../../extras/modules/state_of_the_union.txt")documents = loader.load()text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0)docs = text_splitter.split_documents(documents)embeddings = OpenAIEmbeddings()Next, we'll load the service URL for our Timescale database. If you haven't already, signup for Timescale, and create a new database.Then, to connect to your PostgreSQL database, you'll need your service URI, which can be found in the cheatsheet or .env file you downloaded after creating a new database. The URI will look something like this: postgres://tsdbadmin:<password>@<id>.tsdb.cloud.timescale.com:<port>/tsdb?sslmode=require. # Timescale Vector needs the service url to your cloud database. You can see this as soon as you create the # service in the cloud UI or in your credentials.sql fileSERVICE_URL = os.environ['TIMESCALE_SERVICE_URL']# Specify directly if testing#SERVICE_URL = "postgres://tsdbadmin:<password>@<id>.tsdb.cloud.timescale.com:<port>/tsdb?sslmode=require"# # You can get also it from an enviornment variables. We suggest using a .env file.# import os# SERVICE_URL = os.environ.get("TIMESCALE_SERVICE_URL", "")Next we create a TimescaleVector vectorstore. We specify a collection name, which will be the name of the table our data is stored in. Note: When creating a new instance of TimescaleVector, the TimescaleVector Module will try to create a table with the name of the collection. So, make sure that the collection name is unique (i.e it doesn't already exist).# The TimescaleVector Module will create a table with the name of the collection.COLLECTION_NAME = "state_of_the_union_test"# Create a Timescale Vector instance from the collection of documentsdb = TimescaleVector.from_documents(    embedding=embeddings,    documents=docs,    collection_name=COLLECTION_NAME,    service_url=SERVICE_URL,)Now that we've loaded our data, we can perform a similarity search.query = "What did the president say about Ketanji Brown Jackson"docs_with_score = db.similarity_search_with_score(query)for doc, score in docs_with_score:    print("-" * 80)    print("Score: ", score)    print(doc.page_content)    print("-" * 80)    --------------------------------------------------------------------------------    Score:  0.18443380687035138    Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections.         Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.         One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.         And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.    --------------------------------------------------------------------------------    --------------------------------------------------------------------------------    Score:  0.18452197313308139    Tonight. I call on the Senate to: Pass the Freedom to Vote Act. Pass the John Lewis Voting Rights Act. And while you’re at it, pass the Disclose Act so Americans can know who is funding our elections.         Tonight, I’d like to honor someone who has dedicated his life to serve this country: Justice Stephen Breyer—an Army veteran, Constitutional scholar, and retiring Justice of the United States Supreme Court. Justice Breyer, thank you for your service.         One of the most serious constitutional responsibilities a President has is nominating someone to serve on the United States Supreme Court.         And I did that 4 days ago, when I nominated Circuit Court of Appeals Judge Ketanji Brown Jackson. One of our nation’s top legal minds, who will continue Justice Breyer’s legacy of excellence.    --------------------------------------------------------------------------------    --------------------------------------------------------------------------------    Score:  0.21720781018594182    A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.         And if we are to advance liberty and justice, we need to secure the Border and fix the immigration system.         We can do both. At our border, we’ve installed new technology like cutting-edge scanners to better detect drug smuggling.          We’ve set up joint patrols with Mexico and Guatemala to catch more human traffickers.          We’re putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster.         We’re securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders.    --------------------------------------------------------------------------------    --------------------------------------------------------------------------------    Score:  0.21724902288621384    A former top litigator in private practice. A former federal public defender. And from a family of public school educators and police officers. A consensus builder. Since she’s been nominated, she’s received a broad range of support—from the Fraternal Order of Police to former judges appointed by Democrats and Republicans.         And if we are to advance liberty and justice, we need to secure the Border and fix the immigration system.         We can do both. At our border, we’ve installed new technology like cutting-edge scanners to better detect drug smuggling.          We’ve set up joint patrols with Mexico and Guatemala to catch more human traffickers.          We’re putting in place dedicated immigration judges so families fleeing persecution and violence can have their cases heard faster.         We’re securing commitments and supporting partners in South and Central America to host more refugees and secure their own borders.    --------------------------------------------------------------------------------Using a Timescale Vector as a Retriever​After initializing a TimescaleVector store, you can use it as a retriever.# Use TimescaleVector as a retrieverretriever = db.as_retriever()print(retriever)    tags=['TimescaleVector', 'OpenAIEmbeddings'] metadata=None vectorstore=<langchain.vectorstores.timescalevector.TimescaleVector object at 0x10fc8d070> search_type='similarity' search_kwargs={}Let's look at an example of using Timescale Vector as a retriever with the RetrievalQA chain and the stuff chain.In this example, we'll ask the same query as above, but this time we'll pass the relevant documents returned from Timescale Vector to an LLM to use as context to answer our question.First we'll create our stuff chain:# Initialize GPT3.5 modelfrom langchain.chat_models import ChatOpenAIllm = ChatOpenAI(temperature = 0.1, model = 'gpt-3.5-turbo-16k')# Initialize a RetrievalQA class from a stuff chainfrom langchain.chains import RetrievalQAqa_stuff = RetrievalQA.from_chain_type(    llm=llm,     chain_type="stuff",     retriever=retriever,    verbose=True,)query = "What did the president say about Ketanji Brown Jackson?"response = qa_stuff.run(query)            > Entering new RetrievalQA chain...        > Finished chain.print(response)    The President said that he nominated Circuit Court of Appeals Judge Ketanji Brown Jackson, who is one of our nation's top legal minds and will continue Justice Breyer's legacy of excellence. He also mentioned that since her nomination, she has received a broad range of support from various groups, including the Fraternal Order of Police and former judges appointed by Democrats and Republicans.2. Similarity Search with time-based filtering​A key use case for Timescale Vector is efficient time-based vector search. Timescale Vector enables this by automatically partitioning vectors (and associated metadata) by time. This allows you to efficiently query vectors by both similarity to a query vector and time.Time-based vector search functionality is helpful for applications like:Storing and retrieving LLM response history (e.g. chatbots)Finding the most recent embeddings that are similar to a query vector (e.g recent news).Constraining similarity search to a relevant time range (e.g asking time-based questions about a knowledge base)To illustrate how to use TimescaleVector's time-based vector search functionality, we'll ask questions about the git log history for TimescaleDB . We'll illustrate how to add documents with a time-based uuid and how run similarity searches with time range filters.Extract content and metadata from git log JSON​First lets load in the git log data into a new collection in our PostgreSQL database named timescale_commits.import jsonWe'll define a helper funciton to create a uuid for a document and associated vector embedding based on its timestamp. We'll use this function to create a uuid for each git log entry.Important note: If you are working with documents and want the current date and time associated with vector for time-based search, you can skip this step. A uuid will be automatically generated when the documents are ingested by default.from timescale_vector import client# Function to take in a date string in the past and return a uuid v1def create_uuid(date_string: str):    if date_string is None:        return None    time_format = '%a %b %d %H:%M:%S %Y %z'    datetime_obj = datetime.strptime(date_string, time_format)    uuid = client.uuid_from_time(datetime_obj)    return str(uuid)Next, we'll define a metadata function to extract the relevant metadata from the JSON record. We'll pass this function to the JSONLoader. See the JSON document loader docs for more details.# Helper function to split name and email given an author string consisting of Name Lastname <email>def split_name(input_string: str) -> Tuple[str, str]:    if input_string is None:        return None, None    start = input_string.find("<")    end = input_string.find(">")    name = input_string[:start].strip()    email = input_string[start+1:end].strip()    return name, email# Helper function to transform a date string into a timestamp_tz stringdef create_date(input_string: str) -> datetime:    if input_string is None:        return None    # Define a dictionary to map month abbreviations to their numerical equivalents    month_dict = {        "Jan": "01",        "Feb": "02",        "Mar": "03",        "Apr": "04",        "May": "05",        "Jun": "06",        "Jul": "07",        "Aug": "08",        "Sep": "09",        "Oct": "10",        "Nov": "11",        "Dec": "12",    }    # Split the input string into its components    components = input_string.split()    # Extract relevant information    day = components[2]    month = month_dict[components[1]]    year = components[4]    time = components[3]    timezone_offset_minutes = int(components[5])  # Convert the offset to minutes    timezone_hours = timezone_offset_minutes // 60  # Calculate the hours    timezone_minutes = timezone_offset_minutes % 60  # Calculate the remaining minutes    # Create a formatted string for the timestamptz in PostgreSQL format    timestamp_tz_str = f"{year}-{month}-{day} {time}+{timezone_hours:02}{timezone_minutes:02}"    return timestamp_tz_str# Metadata extraction function to extract metadata from a JSON recorddef extract_metadata(record: dict, metadata: dict) -> dict:    record_name, record_email = split_name(record["author"])    metadata["id"] = create_uuid(record["date"])    metadata["date"] = create_date(record["date"])    metadata["author_name"] = record_name    metadata["author_email"] = record_email    metadata["commit_hash"] = record["commit"]    return metadataNext, you'll need to download the sample dataset and place it in the same directory as this notebook.You can use following command:# Download the file using curl and save it as commit_history.csv# Note: Execute this command in your terminal, in the same directory as the notebookcurl -O https://s3.amazonaws.com/assets.timescale.com/ai/ts_git_log.jsonFinally we can initialize the JSON loader to parse the JSON records. We also remove empty records for simplicity.# Define path to the JSON file relative to this notebook# Change this to the path to your JSON fileFILE_PATH = "../../../../../ts_git_log.json"# Load data from JSON file and extract metadataloader = JSONLoader(    file_path=FILE_PATH,    jq_schema='.commit_history[]',    text_content=False,    metadata_func=extract_metadata)documents = loader.load()# Remove documents with None datesdocuments = [doc for doc in documents if doc.metadata["date"] is not None]print(documents[0])    page_content='{"commit": "44e41c12ab25e36c202f58e068ced262eadc8d16", "author": "Lakshmi Narayanan Sreethar<lakshmi@timescale.com>", "date": "Tue Sep 5 21:03:21 2023 +0530", "change summary": "Fix segfault in set_integer_now_func", "change details": "When an invalid function oid is passed to set_integer_now_func, it finds out that the function oid is invalid but before throwing the error, it calls ReleaseSysCache on an invalid tuple causing a segfault. Fixed that by removing the invalid call to ReleaseSysCache.  Fixes #6037 "}' metadata={'source': '/Users/avtharsewrathan/sideprojects2023/timescaleai/tsv-langchain/ts_git_log.json', 'seq_num': 1, 'id': '8b407680-4c01-11ee-96a6-b82284ddccc6', 'date': '2023-09-5 21:03:21+0850', 'author_name': 'Lakshmi Narayanan Sreethar', 'author_email': 'lakshmi@timescale.com', 'commit_hash': '44e41c12ab25e36c202f58e068ced262eadc8d16'}Load documents and metadata into TimescaleVector vectorstore​Now that we have prepared our documents, let's process them and load them, along with their vector embedding representations into our TimescaleVector vectorstore.Since this is a demo, we will only load the first 500 records. In practice, you can load as many records as you want.NUM_RECORDS = 500documents = documents[:NUM_RECORDS]Then we use the CharacterTextSplitter to split the documents into smaller chunks if needed for easier embedding. Note that this splitting process retains the metadata for each document.# Split the documents into chunks for embeddingtext_splitter = CharacterTextSplitter(    chunk_size=1000,    chunk_overlap=200,)docs = text_splitter.split_documents(documents)Next we'll create a Timescale Vector instance from the collection of documents that we finished pre-processsing.First, we'll define a collection name, which will be the name of our table in the PostgreSQL database. We'll also define a time delta, which we pass to the time_partition_interval argument, which will be used to as the interval for partitioning the data by time. Each partition will consist of data for the specified length of time. We'll use 7 days for simplicity, but you can pick whatever value make sense for your use case -- for example if you query recent vectors frequently you might want to use a smaller time delta like 1 day, or if you query vectors over a decade long time period then you might want to use a larger time delta like 6 months or 1 year.Finally, we'll create the TimescaleVector instance. We specify the ids argument to be the uuid field in our metadata that we created in the pre-processing step above. We do this because we want the time part of our uuids to reflect dates in the past (i.e when the commit was made). However, if we wanted the current date and time to be associated with our document, we can remove the id argument and uuid's will be automatically created with the current date and time.# Define collection nameCOLLECTION_NAME = "timescale_commits"embeddings = OpenAIEmbeddings()# Create a Timescale Vector instance from the collection of documentsdb = TimescaleVector.from_documents(      embedding=embeddings,      ids = [doc.metadata["id"] for doc in docs],      documents=docs,      collection_name=COLLECTION_NAME,      service_url=SERVICE_URL,      time_partition_interval=timedelta(days = 7),)Querying vectors by time and similarity​Now that we have loaded our documents into TimescaleVector, we can query them by time and similarity.TimescaleVector provides multiple methods for querying vectors by doing similarity search with time-based filtering.Let's take a look at each method below:# Time filter variablesstart_dt = datetime(2023, 8, 1, 22, 10, 35) # Start date = 1 August 2023, 22:10:35end_dt = datetime(2023, 8, 30, 22, 10, 35) # End date = 30 August 2023, 22:10:35td = timedelta(days=7) # Time delta = 7 daysquery = "What's new with TimescaleDB functions?"Method 1: Filter within a provided start date and end date.# Method 1: Query for vectors between start_date and end_datedocs_with_score = db.similarity_search_with_score(query, start_date=start_dt, end_date=end_dt)for doc, score in docs_with_score:    print("-" * 80)    print("Score: ", score)    print("Date: ", doc.metadata["date"])    print(doc.page_content)    print("-" * 80)    --------------------------------------------------------------------------------    Score:  0.17488396167755127    Date:  2023-08-29 18:13:24+0320    {"commit": " e4facda540286b0affba47ccc63959fefe2a7b26", "author": "Sven Klemm<sven@timescale.com>", "date": "Tue Aug 29 18:13:24 2023 +0200", "change summary": "Add compatibility layer for _timescaledb_internal functions", "change details": "With timescaledb 2.12 all the functions present in _timescaledb_internal were moved into the _timescaledb_functions schema to improve schema security. This patch adds a compatibility layer so external callers of these internal functions will not break and allow for more flexibility when migrating. "}    --------------------------------------------------------------------------------    --------------------------------------------------------------------------------    Score:  0.18102192878723145    Date:  2023-08-20 22:47:10+0320    {"commit": " 0a66bdb8d36a1879246bd652e4c28500c4b951ab", "author": "Sven Klemm<sven@timescale.com>", "date": "Sun Aug 20 22:47:10 2023 +0200", "change summary": "Move functions to _timescaledb_functions schema", "change details": "To increase schema security we do not want to mix our own internal objects with user objects. Since chunks are created in the _timescaledb_internal schema our internal functions should live in a different dedicated schema. This patch make the necessary adjustments for the following functions:  - to_unix_microseconds(timestamptz) - to_timestamp(bigint) - to_timestamp_without_timezone(bigint) - to_date(bigint) - to_interval(bigint) - interval_to_usec(interval) - time_to_internal(anyelement) - subtract_integer_from_now(regclass, bigint) "}    --------------------------------------------------------------------------------    --------------------------------------------------------------------------------    Score:  0.18150119891755445    Date:  2023-08-22 12:01:19+0320    {"commit": " cf04496e4b4237440274eb25e4e02472fc4e06fc", "author": "Sven Klemm<sven@timescale.com>", "date": "Tue Aug 22 12:01:19 2023 +0200", "change summary": "Move utility functions to _timescaledb_functions schema", "change details": "To increase schema security we do not want to mix our own internal objects with user objects. Since chunks are created in the _timescaledb_internal schema our internal functions should live in a different dedicated schema. This patch make the necessary adjustments for the following functions:  - generate_uuid() - get_git_commit() - get_os_info() - tsl_loaded() "}    --------------------------------------------------------------------------------    --------------------------------------------------------------------------------    Score:  0.18422493887617963    Date:  2023-08-9 15:26:03+0500    {"commit": " 44eab9cf9bef34274c88efd37a750eaa74cd8044", "author": "Konstantina Skovola<konstantina@timescale.com>", "date": "Wed Aug 9 15:26:03 2023 +0300", "change summary": "Release 2.11.2", "change details": "This release contains bug fixes since the 2.11.1 release. We recommend that you upgrade at the next available opportunity.  **Features** * #5923 Feature flags for TimescaleDB features  **Bugfixes** * #5680 Fix DISTINCT query with JOIN on multiple segmentby columns * #5774 Fixed two bugs in decompression sorted merge code * #5786 Ensure pg_config --cppflags are passed * #5906 Fix quoting owners in sql scripts. * #5912 Fix crash in 1-step integer policy creation  **Thanks** * @mrksngl for submitting a PR to fix extension upgrade scripts * @ericdevries for reporting an issue with DISTINCT queries using segmentby columns of compressed hypertable "}    --------------------------------------------------------------------------------Note how the query only returns results within the specified date range.Method 2: Filter within a provided start date, and a time delta later.# Method 2: Query for vectors between start_dt and a time delta td later# Most relevant vectors between 1 August and 7 days laterdocs_with_score = db.similarity_search_with_score(query, start_date=start_dt, time_delta=td)for doc, score in docs_with_score:    print("-" * 80)    print("Score: ", score)    print("Date: ", doc.metadata["date"])    print(doc.page_content)    print("-" * 80)    --------------------------------------------------------------------------------    Score:  0.18458807468414307    Date:  2023-08-3 14:30:23+0500    {"commit": " 7aeed663b9c0f337b530fd6cad47704a51a9b2ec", "author": "Dmitry Simonenko<dmitry@timescale.com>", "date": "Thu Aug 3 14:30:23 2023 +0300", "change summary": "Feature flags for TimescaleDB features", "change details": "This PR adds several GUCs which allow to enable/disable major timescaledb features:  - enable_hypertable_create - enable_hypertable_compression - enable_cagg_create - enable_policy_create "}    --------------------------------------------------------------------------------    --------------------------------------------------------------------------------    Score:  0.20492422580718994    Date:  2023-08-7 18:31:40+0320    {"commit": " 07762ea4cedefc88497f0d1f8712d1515cdc5b6e", "author": "Sven Klemm<sven@timescale.com>", "date": "Mon Aug 7 18:31:40 2023 +0200", "change summary": "Test timescaledb debian 12 packages in CI", "change details": ""}    --------------------------------------------------------------------------------    --------------------------------------------------------------------------------    Score:  0.21106326580047607    Date:  2023-08-3 14:36:39+0500    {"commit": " 2863daf3df83c63ee36c0cf7b66c522da5b4e127", "author": "Dmitry Simonenko<dmitry@timescale.com>", "date": "Thu Aug 3 14:36:39 2023 +0300", "change summary": "Support CREATE INDEX ONLY ON main table", "change details": "This PR adds support for CREATE INDEX ONLY ON clause which allows to create index only on the main table excluding chunks.  Fix #5908 "}    --------------------------------------------------------------------------------    --------------------------------------------------------------------------------    Score:  0.21698051691055298    Date:  2023-08-2 20:24:14+0140    {"commit": " 3af0d282ea71d9a8f27159a6171e9516e62ec9cb", "author": "Lakshmi Narayanan Sreethar<lakshmi@timescale.com>", "date": "Wed Aug 2 20:24:14 2023 +0100", "change summary": "PG16: ExecInsertIndexTuples requires additional parameter", "change details": "PG16 adds a new boolean parameter to the ExecInsertIndexTuples function to denote if the index is a BRIN index, which is then used to determine if the index update can be skipped. The fix also removes the INDEX_ATTR_BITMAP_ALL enum value.  Adapt these changes by updating the compat function to accomodate the new parameter added to the ExecInsertIndexTuples function and using an alternative for the removed INDEX_ATTR_BITMAP_ALL enum value.  postgres/postgres@19d8e23 "}    --------------------------------------------------------------------------------Once again, notice how we get results within the specified time filter, different from the previous query.Method 3: Filter within a provided end date and a time delta earlier.# Method 3: Query for vectors between end_dt and a time delta td earlier# Most relevant vectors between 30 August and 7 days earlierdocs_with_score = db.similarity_search_with_score(query, end_date=end_dt, time_delta=td)for doc, score in docs_with_score:    print("-" * 80)    print("Score: ", score)    print("Date: ", doc.metadata["date"])    print(doc.page_content)    print("-" * 80)    --------------------------------------------------------------------------------    Score:  0.17488396167755127    Date:  2023-08-29 18:13:24+0320    {"commit": " e4facda540286b0affba47ccc63959fefe2a7b26", "author": "Sven Klemm<sven@timescale.com>", "date": "Tue Aug 29 18:13:24 2023 +0200", "change summary": "Add compatibility layer for _timescaledb_internal functions", "change details": "With timescaledb 2.12 all the functions present in _timescaledb_internal were moved into the _timescaledb_functions schema to improve schema security. This patch adds a compatibility layer so external callers of these internal functions will not break and allow for more flexibility when migrating. "}    --------------------------------------------------------------------------------    --------------------------------------------------------------------------------    Score:  0.18496227264404297    Date:  2023-08-29 10:49:47+0320    {"commit": " a9751ccd5eb030026d7b975d22753f5964972389", "author": "Sven Klemm<sven@timescale.com>", "date": "Tue Aug 29 10:49:47 2023 +0200", "change summary": "Move partitioning functions to _timescaledb_functions schema", "change details": "To increase schema security we do not want to mix our own internal objects with user objects. Since chunks are created in the _timescaledb_internal schema our internal functions should live in a different dedicated schema. This patch make the necessary adjustments for the following functions:  - get_partition_for_key(val anyelement) - get_partition_hash(val anyelement) "}    --------------------------------------------------------------------------------    --------------------------------------------------------------------------------    Score:  0.1871250867843628    Date:  2023-08-28 23:26:23+0320    {"commit": " b2a91494a11d8b82849b6f11f9ea6dc26ef8a8cb", "author": "Sven Klemm<sven@timescale.com>", "date": "Mon Aug 28 23:26:23 2023 +0200", "change summary": "Move ddl_internal functions to _timescaledb_functions schema", "change details": "To increase schema security we do not want to mix our own internal objects with user objects. Since chunks are created in the _timescaledb_internal schema our internal functions should live in a different dedicated schema. This patch make the necessary adjustments for the following functions:  - chunk_constraint_add_table_constraint(_timescaledb_catalog.chunk_constraint) - chunk_drop_replica(regclass,name) - chunk_index_clone(oid) - chunk_index_replace(oid,oid) - create_chunk_replica_table(regclass,name) - drop_stale_chunks(name,integer[]) - health() - hypertable_constraint_add_table_fk_constraint(name,name,name,integer) - process_ddl_event() - wait_subscription_sync(name,name,integer,numeric) "}    --------------------------------------------------------------------------------    --------------------------------------------------------------------------------    Score:  0.18867712088363497    Date:  2023-08-27 13:20:04+0320    {"commit": " e02b1f348eb4c48def00b7d5227238b4d9d41a4a", "author": "Sven Klemm<sven@timescale.com>", "date": "Sun Aug 27 13:20:04 2023 +0200", "change summary": "Simplify schema move update script", "change details": "Use dynamic sql to create the ALTER FUNCTION statements for those functions that may not exist in previous versions. "}    --------------------------------------------------------------------------------Method 4: We can also filter for all vectors after a given date by only specifying a start date in our query.Method 5: Similarly, we can filter for or all vectors before a given date by only specify an end date in our query.# Method 4: Query all vectors after start_datedocs_with_score = db.similarity_search_with_score(query,start_date=start_dt)for doc, score in docs_with_score:    print("-" * 80)    print("Score: ", score)    print("Date: ", doc.metadata["date"])    print(doc.page_content)    print("-" * 80)    --------------------------------------------------------------------------------    Score:  0.17488396167755127    Date:  2023-08-29 18:13:24+0320    {"commit": " e4facda540286b0affba47ccc63959fefe2a7b26", "author": "Sven Klemm<sven@timescale.com>", "date": "Tue Aug 29 18:13:24 2023 +0200", "change summary": "Add compatibility layer for _timescaledb_internal functions", "change details": "With timescaledb 2.12 all the functions present in _timescaledb_internal were moved into the _timescaledb_functions schema to improve schema security. This patch adds a compatibility layer so external callers of these internal functions will not break a