Getting Started with Apache Spark¶
The Lance Spark connector lets Apache Spark read and write Lance tables, so you can work with your AI data using Spark SQL and DataFrames. In this guide, you'll run Spark on your machine, create a product catalog, query it, search it by vector similarity and by keyword, and modify it. Finally, you'll open the same table from LanceDB.
Each step shows both Spark SQL and PySpark. Pick whichever you prefer.
Install Spark¶
The connector needs Java 17 or later. Install PySpark, which includes the spark-sql shell, along with LanceDB and pandas for the last step:
Start Spark¶
Start Spark with the Lance connector and a Lance catalog named lance. The dir catalog keeps tables in a local directory. For production, the connector also supports catalogs such as Hive, AWS Glue, and REST catalogs; see Configuration.
spark-sql \
--packages org.lance:lance-spark-bundle-4.2_2.13:0.8.0 \
--conf spark.sql.extensions=org.lance.spark.extensions.LanceSparkSessionExtensions \
--conf spark.sql.catalog.lance=org.lance.spark.LanceNamespaceSparkCatalog \
--conf spark.sql.catalog.lance.impl=dir \
--conf spark.sql.catalog.lance.root=/tmp/lance-spark-quickstart \
--conf spark.sql.defaultCatalog=lance \
--conf spark.hadoop.hive.cli.print.header=true
from pyspark.sql import SparkSession
spark = (
SparkSession.builder.appName("lance-quickstart")
.config("spark.jars.packages", "org.lance:lance-spark-bundle-4.2_2.13:0.8.0")
.config(
"spark.sql.extensions", "org.lance.spark.extensions.LanceSparkSessionExtensions"
)
.config("spark.sql.catalog.lance", "org.lance.spark.LanceNamespaceSparkCatalog")
.config("spark.sql.catalog.lance.impl", "dir")
.config("spark.sql.catalog.lance.root", "/tmp/lance-spark-quickstart")
.config("spark.sql.defaultCatalog", "lance")
.getOrCreate()
)
The --packages option downloads the connector from Maven Central the first time you run it. In spark-sql, the last option prints column headers with query results. Use the bundle that matches your Spark version: lance-spark-bundle-4.2_2.13 is for Spark 4.2 with Scala 2.13.
Create a Table¶
Lance stores vectors as fixed-size lists. Spark has no fixed-size array type, so declare the vector column as ARRAY<FLOAT> and set its dimension with a table property:
CREATE TABLE products (
id BIGINT NOT NULL,
name STRING,
category STRING,
price DOUBLE,
vector ARRAY<FLOAT> NOT NULL
) TBLPROPERTIES ('vector.arrow.fixed-size-list.size' = '4');
INSERT INTO products VALUES
(1, 'red running shoes', 'shoes', 89.0, array(1.0, 0.0, 0.0, 0.25)),
(2, 'blue trail running shoes', 'shoes', 120.0, array(0.875, 0.125, 0.0, 0.375)),
(3, 'leather hiking boots', 'shoes', 150.0, array(0.625, 0.0, 0.5, 0.25)),
(4, 'waterproof rain jacket', 'outerwear', 99.0, array(0.0, 1.0, 0.25, 0.0)),
(5, 'wool winter coat', 'outerwear', 210.0, array(0.25, 0.75, 0.625, 0.0));
products = spark.createDataFrame(
[
(1, "red running shoes", "shoes", 89.0, [1.0, 0.0, 0.0, 0.25]),
(2, "blue trail running shoes", "shoes", 120.0, [0.875, 0.125, 0.0, 0.375]),
(3, "leather hiking boots", "shoes", 150.0, [0.625, 0.0, 0.5, 0.25]),
(4, "waterproof rain jacket", "outerwear", 99.0, [0.0, 1.0, 0.25, 0.0]),
(5, "wool winter coat", "outerwear", 210.0, [0.25, 0.75, 0.625, 0.0]),
],
"id BIGINT, name STRING, category STRING, price DOUBLE, vector ARRAY<FLOAT>",
)
# Lance stores vectors as fixed-size lists, so tell it the dimension.
(
products.writeTo("products")
.tableProperty("vector.arrow.fixed-size-list.size", "4")
.create()
)
Query the Table¶
Lance tables work with standard Spark SQL. The connector pushes filters and column selections down to Lance, so Spark reads only the data it needs:
from pyspark.sql import functions as F
table = spark.table("products")
(
table.filter("price < 100")
.select("id", "name", "price")
.orderBy("id")
.show(truncate=False)
)
(
table.groupBy("category")
.agg(F.count("*").alias("num_products"), F.avg("price").alias("avg_price"))
.orderBy("category")
.show(truncate=False)
)
+---+----------------------+-----+
|id |name |price|
+---+----------------------+-----+
|1 |red running shoes |89.0 |
|4 |waterproof rain jacket|99.0 |
+---+----------------------+-----+
+---------+------------+------------------+
|category |num_products|avg_price |
+---------+------------+------------------+
|outerwear|2 |154.5 |
|shoes |3 |119.66666666666667|
+---------+------------+------------------+
Search by Vector Similarity¶
The VECTOR_SEARCH table function returns the rows nearest to a query vector, along with their _distance:
VECTOR_SEARCH also takes filters and index tuning options. See Vector Search.
Search by Keyword¶
Create a full-text search index on a text column, then search it with lance_match:
Add, Update, and Delete Rows¶
socks = spark.createDataFrame(
[(6, "trail socks", "accessories", 15.0, [0.75, 0.25, 0.25, 0.25])],
"id BIGINT, name STRING, category STRING, price DOUBLE, vector ARRAY<FLOAT>",
)
socks.writeTo("products").append()
spark.sql("UPDATE products SET price = 79.0 WHERE id = 1")
spark.sql("DELETE FROM products WHERE category = 'outerwear'")
spark.table("products").select("id", "name", "price").orderBy("id").show(truncate=False)
Open the Table from LanceDB¶
Spark wrote an ordinary Lance table, so other engines can use it directly. For example, you can connect LanceDB to the same directory and search the table from Python:
import lancedb
db = lancedb.connect("/tmp/lance-spark-quickstart")
(
db.open_table("products")
.search([0.875, 0.125, 0.0, 0.25])
.limit(3)
.select(["id", "name", "_distance"])
.to_pandas()
)
id name _distance
0 2 blue trail running shoes 0.0156
1 1 red running shoes 0.0312
2 6 trail socks 0.0938
Next Steps¶
- Read the Spark connector documentation for the full set of supported DDL, DML, and query operations.
- See the LanceDB quickstart to build search into an application.
- See the PyLance quickstart to work directly with the Lance format.