mirror of
https://github.com/lancedb/lancedb.git
synced 2026-09-11 15:52:17 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2faa2fc686 | ||
|
|
77f0027d35 | ||
|
|
bb6d145b14 | ||
|
|
b5bc4025c8 | ||
|
|
582a260c2d | ||
|
|
cad46fc683 | ||
|
|
a3c6926943 | ||
|
|
47c10304b3 | ||
|
|
5f80dcb335 | ||
|
|
0ce322876f |
+1
-1
@@ -1,5 +1,5 @@
|
||||
[tool.bumpversion]
|
||||
current_version = "0.38.0-beta.13"
|
||||
current_version = "0.38.0-beta.14"
|
||||
parse = """(?x)
|
||||
(?P<major>0|[1-9]\\d*)\\.
|
||||
(?P<minor>0|[1-9]\\d*)\\.
|
||||
|
||||
Generated
+3
-3
@@ -5402,7 +5402,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lancedb"
|
||||
version = "0.38.0-beta.13"
|
||||
version = "0.38.0-beta.14"
|
||||
dependencies = [
|
||||
"ahash",
|
||||
"anyhow",
|
||||
@@ -5490,7 +5490,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lancedb-nodejs"
|
||||
version = "0.38.0-beta.13"
|
||||
version = "0.38.0-beta.14"
|
||||
dependencies = [
|
||||
"arrow-array",
|
||||
"arrow-buffer",
|
||||
@@ -5515,7 +5515,7 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lancedb-python"
|
||||
version = "0.38.0-beta.13"
|
||||
version = "0.38.0-beta.14"
|
||||
dependencies = [
|
||||
"arrow",
|
||||
"async-trait",
|
||||
|
||||
@@ -14,7 +14,7 @@ Add the following dependency to your `pom.xml`:
|
||||
<dependency>
|
||||
<groupId>com.lancedb</groupId>
|
||||
<artifactId>lancedb-core</artifactId>
|
||||
<version>0.38.0-beta.13</version>
|
||||
<version>0.38.0-beta.14</version>
|
||||
</dependency>
|
||||
```
|
||||
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<parent>
|
||||
<groupId>com.lancedb</groupId>
|
||||
<artifactId>lancedb-parent</artifactId>
|
||||
<version>0.38.0-beta.13</version>
|
||||
<version>0.38.0-beta.14</version>
|
||||
<relativePath>../pom.xml</relativePath>
|
||||
</parent>
|
||||
|
||||
|
||||
+1
-1
@@ -6,7 +6,7 @@
|
||||
|
||||
<groupId>com.lancedb</groupId>
|
||||
<artifactId>lancedb-parent</artifactId>
|
||||
<version>0.38.0-beta.13</version>
|
||||
<version>0.38.0-beta.14</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>${project.artifactId}</name>
|
||||
<description>LanceDB Java SDK Parent POM</description>
|
||||
|
||||
+1
-1
@@ -1,7 +1,7 @@
|
||||
[package]
|
||||
name = "lancedb-nodejs"
|
||||
edition.workspace = true
|
||||
version = "0.38.0-beta.13"
|
||||
version = "0.38.0-beta.14"
|
||||
publish = false
|
||||
license.workspace = true
|
||||
description.workspace = true
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-darwin-arm64",
|
||||
"version": "0.38.0-beta.13",
|
||||
"version": "0.38.0-beta.14",
|
||||
"os": ["darwin"],
|
||||
"cpu": ["arm64"],
|
||||
"main": "lancedb.darwin-arm64.node",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-linux-arm64-gnu",
|
||||
"version": "0.38.0-beta.13",
|
||||
"version": "0.38.0-beta.14",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"],
|
||||
"main": "lancedb.linux-arm64-gnu.node",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-linux-arm64-musl",
|
||||
"version": "0.38.0-beta.13",
|
||||
"version": "0.38.0-beta.14",
|
||||
"os": ["linux"],
|
||||
"cpu": ["arm64"],
|
||||
"main": "lancedb.linux-arm64-musl.node",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-linux-x64-gnu",
|
||||
"version": "0.38.0-beta.13",
|
||||
"version": "0.38.0-beta.14",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"],
|
||||
"main": "lancedb.linux-x64-gnu.node",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-linux-x64-musl",
|
||||
"version": "0.38.0-beta.13",
|
||||
"version": "0.38.0-beta.14",
|
||||
"os": ["linux"],
|
||||
"cpu": ["x64"],
|
||||
"main": "lancedb.linux-x64-musl.node",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-win32-arm64-msvc",
|
||||
"version": "0.38.0-beta.13",
|
||||
"version": "0.38.0-beta.14",
|
||||
"os": [
|
||||
"win32"
|
||||
],
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@lancedb/lancedb-win32-x64-msvc",
|
||||
"version": "0.38.0-beta.13",
|
||||
"version": "0.38.0-beta.14",
|
||||
"os": ["win32"],
|
||||
"cpu": ["x64"],
|
||||
"main": "lancedb.win32-x64-msvc.node",
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@
|
||||
"ann"
|
||||
],
|
||||
"private": false,
|
||||
"version": "0.38.0-beta.13",
|
||||
"version": "0.38.0-beta.14",
|
||||
"main": "dist/index.js",
|
||||
"exports": {
|
||||
".": "./dist/index.js",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lancedb-python"
|
||||
version = "0.38.0-beta.13"
|
||||
version = "0.38.0-beta.14"
|
||||
publish = false
|
||||
edition.workspace = true
|
||||
description = "Python bindings for LanceDB"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "lancedb"
|
||||
version = "0.38.0-beta.13"
|
||||
version = "0.38.0-beta.14"
|
||||
edition.workspace = true
|
||||
description = "LanceDB: A serverless, low-latency vector database for AI applications"
|
||||
license.workspace = true
|
||||
|
||||
@@ -580,7 +580,14 @@ fn resolve_field_path<'a>(schema: &'a ArrowSchema, path: &str) -> Result<Resolve
|
||||
Ok(ResolvedFieldPath { root, leaf })
|
||||
}
|
||||
|
||||
fn canonical_input_arrow_type(field: &JsonArrowField) -> Result<String> {
|
||||
fn canonical_input_arrow_type(field: &ArrowField) -> Result<String> {
|
||||
if field.is_blob_v2() {
|
||||
return Ok("blob_v2".to_string());
|
||||
}
|
||||
let schema =
|
||||
lance_namespace::schema::arrow_schema_to_json(&ArrowSchema::new(vec![field.clone()]))
|
||||
.map_err(|e| invalid_function(format!("invalid Function input schema: {e}")))?;
|
||||
let field = schema.fields.into_iter().next().unwrap();
|
||||
if field.r#type.fields.is_none() && field.r#type.length.is_none() {
|
||||
Ok(field.r#type.r#type.clone())
|
||||
} else {
|
||||
@@ -669,6 +676,90 @@ fn parse_output_arrow_type(raw: &str) -> Result<JsonArrowDataType> {
|
||||
Ok(data_type)
|
||||
}
|
||||
|
||||
fn parse_output_arrow_field(name: &str, nullable: bool, raw: &str) -> Result<JsonArrowField> {
|
||||
if raw.trim() == "blob_v2" {
|
||||
let schema =
|
||||
lance_namespace::schema::arrow_schema_to_json(&ArrowSchema::new(vec![crate::blob(
|
||||
name, nullable,
|
||||
)]))
|
||||
.map_err(|e| invalid_function(format!("invalid Function Blob type: {e}")))?;
|
||||
return Ok(schema.fields.into_iter().next().unwrap());
|
||||
}
|
||||
|
||||
fn parse_nested(name: &str, nullable: bool, raw: &str) -> Result<JsonArrowField> {
|
||||
let raw = raw.trim();
|
||||
if raw == "blob_v2" {
|
||||
return parse_output_arrow_field(name, nullable, raw);
|
||||
}
|
||||
if let Some(inner) = raw
|
||||
.strip_prefix("list<")
|
||||
.and_then(|value| value.strip_suffix('>'))
|
||||
{
|
||||
let mut data_type = JsonArrowDataType::new("list".to_string());
|
||||
data_type.fields = Some(vec![parse_nested("item", false, inner)?]);
|
||||
return Ok(JsonArrowField::new(name.to_string(), nullable, data_type));
|
||||
}
|
||||
if let Some(inner) = raw
|
||||
.strip_prefix("large_list<")
|
||||
.and_then(|value| value.strip_suffix('>'))
|
||||
{
|
||||
let mut data_type = JsonArrowDataType::new("large_list".to_string());
|
||||
data_type.fields = Some(vec![parse_nested("item", false, inner)?]);
|
||||
return Ok(JsonArrowField::new(name.to_string(), nullable, data_type));
|
||||
}
|
||||
if let Some((inner, size)) = split_fixed_size_list(raw) {
|
||||
let mut data_type = JsonArrowDataType::new("fixed_size_list".to_string());
|
||||
data_type.fields = Some(vec![parse_nested("item", false, inner)?]);
|
||||
data_type.length = Some(i64::from(size));
|
||||
return Ok(JsonArrowField::new(name.to_string(), nullable, data_type));
|
||||
}
|
||||
Ok(JsonArrowField::new(
|
||||
name.to_string(),
|
||||
nullable,
|
||||
parse_output_arrow_type(raw)?,
|
||||
))
|
||||
}
|
||||
|
||||
let field = parse_nested(name, nullable, raw)?;
|
||||
lance_namespace::schema::convert_json_arrow_field(&field)
|
||||
.map_err(|e| invalid_function(format!("unsupported Function Arrow type '{raw}': {e}")))?;
|
||||
Ok(field)
|
||||
}
|
||||
|
||||
fn function_fields_equivalent(actual: &ArrowField, expected: &ArrowField) -> bool {
|
||||
if actual.is_blob_v2() || expected.is_blob_v2() {
|
||||
return actual.is_blob_v2()
|
||||
&& expected.is_blob_v2()
|
||||
&& actual.data_type() == expected.data_type();
|
||||
}
|
||||
match (actual.data_type(), expected.data_type()) {
|
||||
(DataType::Struct(actual), DataType::Struct(expected)) => {
|
||||
actual.len() == expected.len()
|
||||
&& actual.iter().zip(expected).all(|(actual, expected)| {
|
||||
actual.name() == expected.name()
|
||||
&& actual.is_nullable() == expected.is_nullable()
|
||||
&& function_fields_equivalent(actual, expected)
|
||||
})
|
||||
}
|
||||
(DataType::List(actual), DataType::List(expected))
|
||||
| (DataType::LargeList(actual), DataType::LargeList(expected)) => {
|
||||
actual.name() == expected.name()
|
||||
&& actual.is_nullable() == expected.is_nullable()
|
||||
&& function_fields_equivalent(actual, expected)
|
||||
}
|
||||
(
|
||||
DataType::FixedSizeList(actual, actual_size),
|
||||
DataType::FixedSizeList(expected, expected_size),
|
||||
) => {
|
||||
actual_size == expected_size
|
||||
&& actual.name() == expected.name()
|
||||
&& actual.is_nullable() == expected.is_nullable()
|
||||
&& function_fields_equivalent(actual, expected)
|
||||
}
|
||||
_ => actual.data_type() == expected.data_type(),
|
||||
}
|
||||
}
|
||||
|
||||
fn ensure_binding_matches_schema(schema: &ArrowSchema, binding: &FunctionBinding) -> Result<()> {
|
||||
let mut input_fields = Vec::with_capacity(binding.inputs().len());
|
||||
for input in binding.inputs() {
|
||||
@@ -701,12 +792,7 @@ fn ensure_binding_matches_schema(schema: &ArrowSchema, binding: &FunctionBinding
|
||||
input.nullable,
|
||||
)
|
||||
.with_metadata(field.metadata().clone());
|
||||
let json = lance_namespace::schema::arrow_schema_to_json(&ArrowSchema::new(vec![
|
||||
parameter_field.clone(),
|
||||
]))
|
||||
.map_err(|e| invalid_function(format!("invalid Function input schema: {e}")))?;
|
||||
let json_field = json.fields.into_iter().next().unwrap();
|
||||
if canonical_input_arrow_type(&json_field)? != input.arrow_type {
|
||||
if canonical_input_arrow_type(¶meter_field)? != input.arrow_type {
|
||||
return Err(invalid_function(format!(
|
||||
"Function input '{}' type no longer matches binding '{}'",
|
||||
input.field_path,
|
||||
@@ -749,10 +835,10 @@ fn ensure_binding_matches_schema(schema: &ArrowSchema, binding: &FunctionBinding
|
||||
binding.binding_id()
|
||||
)));
|
||||
}
|
||||
let expected_type = parse_output_arrow_type(&output.arrow_type)?;
|
||||
let expected_type = lance_namespace::schema::convert_json_arrow_type(&expected_type)
|
||||
let expected_field = parse_output_arrow_field(field.name(), true, &output.arrow_type)?;
|
||||
let expected_field = lance_namespace::schema::convert_json_arrow_field(&expected_field)
|
||||
.map_err(|e| invalid_function(format!("invalid Function output type: {e}")))?;
|
||||
if field.data_type() != &expected_type {
|
||||
if !function_fields_equivalent(field, &expected_field) {
|
||||
return Err(invalid_function(format!(
|
||||
"Function output '{}' type no longer matches binding '{}'",
|
||||
output.output_name,
|
||||
@@ -781,11 +867,7 @@ fn ensure_binding_matches_schema(schema: &ArrowSchema, binding: &FunctionBinding
|
||||
binding.binding_id()
|
||||
)));
|
||||
}
|
||||
output_fields.push(ArrowField::new(
|
||||
field.name().clone(),
|
||||
field.data_type().clone(),
|
||||
true,
|
||||
));
|
||||
output_fields.push(expected_field);
|
||||
}
|
||||
let output_schema =
|
||||
lance_namespace::schema::arrow_schema_to_json(&ArrowSchema::new(output_fields))
|
||||
@@ -881,15 +963,10 @@ pub(crate) fn plan_function_application(
|
||||
field.is_nullable(),
|
||||
)
|
||||
.with_metadata(field.metadata().clone());
|
||||
let input_schema = lance_namespace::schema::arrow_schema_to_json(&ArrowSchema::new(vec![
|
||||
parameter_field.clone(),
|
||||
]))
|
||||
.map_err(|e| invalid_function(format!("invalid Function input schema: {e}")))?;
|
||||
let json_field = input_schema.fields.into_iter().next().unwrap();
|
||||
input_bindings.push(FunctionInputTarget {
|
||||
parameter: input.parameter.clone(),
|
||||
field_path: path.to_string(),
|
||||
arrow_type: canonical_input_arrow_type(&json_field)?,
|
||||
arrow_type: canonical_input_arrow_type(¶meter_field)?,
|
||||
nullable: field.is_nullable(),
|
||||
});
|
||||
input_fields.push(parameter_field);
|
||||
@@ -918,16 +995,19 @@ pub(crate) fn plan_function_application(
|
||||
"Function logical outputs must be non-nullable during NULL assignment",
|
||||
));
|
||||
}
|
||||
let data_type =
|
||||
parse_output_arrow_type(output.arrow_type.as_deref().ok_or_else(|| {
|
||||
let output_field = parse_output_arrow_field(
|
||||
name,
|
||||
true,
|
||||
output.arrow_type.as_deref().ok_or_else(|| {
|
||||
invalid_function("scalar Function output is missing its Arrow type")
|
||||
})?)?;
|
||||
})?,
|
||||
)?;
|
||||
outputs.push(FunctionOutputTarget {
|
||||
result_field: WHOLE_RESULT_FIELD.to_string(),
|
||||
output_name: name.to_string(),
|
||||
output_ordinal: 0,
|
||||
});
|
||||
output_fields.push(JsonArrowField::new(name.to_string(), true, data_type));
|
||||
output_fields.push(output_field);
|
||||
}
|
||||
"named_struct" => {
|
||||
if output.fields.is_empty() {
|
||||
@@ -971,13 +1051,7 @@ pub(crate) fn plan_function_application(
|
||||
let fields = output
|
||||
.fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
Ok(JsonArrowField::new(
|
||||
field.name.clone(),
|
||||
false,
|
||||
parse_output_arrow_type(&field.arrow_type)?,
|
||||
))
|
||||
})
|
||||
.map(|field| parse_output_arrow_field(&field.name, false, &field.arrow_type))
|
||||
.collect::<Result<Vec<_>>>()?;
|
||||
let mut data_type = JsonArrowDataType::new("struct".to_string());
|
||||
data_type.fields = Some(fields);
|
||||
@@ -1004,11 +1078,7 @@ pub(crate) fn plan_function_application(
|
||||
output_name: name.clone(),
|
||||
output_ordinal: ordinal as u32,
|
||||
});
|
||||
output_fields.push(JsonArrowField::new(
|
||||
name.clone(),
|
||||
true,
|
||||
parse_output_arrow_type(&field.arrow_type)?,
|
||||
));
|
||||
output_fields.push(parse_output_arrow_field(name, true, &field.arrow_type)?);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2606,6 +2676,44 @@ mod tests {
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
fn blob_function_application() -> FunctionApplication {
|
||||
FunctionApplication::from_json(
|
||||
r#"{
|
||||
"function":{"name":"copy_blob","version":"fv_exact"},
|
||||
"inputs":[
|
||||
{"parameter":"image","kind":"column","value":{"path":"image"}}
|
||||
],
|
||||
"output":{"kind":"scalar","arrow_type":"blob_v2","nullable":false}
|
||||
}"#,
|
||||
)
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blob_function_input_uses_the_semantic_binding_type() {
|
||||
let schema = ArrowSchema::new(vec![crate::blob("image", true)]);
|
||||
let plan =
|
||||
plan_function_application(&schema, &blob_function_application(), Some("copy")).unwrap();
|
||||
|
||||
assert_eq!(plan.input_bindings[0].arrow_type, "blob_v2");
|
||||
let input = lance_namespace::schema::convert_json_arrow_field(&plan.input_schema.fields[0])
|
||||
.unwrap();
|
||||
assert!(input.is_blob_v2());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_blob_function_output_is_plannable() {
|
||||
let schema = ArrowSchema::new(vec![crate::blob("image", true)]);
|
||||
let plan =
|
||||
plan_function_application(&schema, &blob_function_application(), Some("copy")).unwrap();
|
||||
|
||||
let output =
|
||||
lance_namespace::schema::convert_json_arrow_field(&plan.output_schema.fields[0])
|
||||
.unwrap();
|
||||
assert_eq!(output.name(), "copy");
|
||||
assert!(output.is_blob_v2());
|
||||
}
|
||||
|
||||
fn function_binding_schema(title_nullable: bool, body_nullable: bool) -> ArrowSchema {
|
||||
ArrowSchema::new(vec![
|
||||
ArrowField::new("title", DataType::Utf8, title_nullable),
|
||||
|
||||
Reference in New Issue
Block a user