使用动态数量的 .and() 创建 Diesel.rs 个查询
Creating Diesel.rs queries with a dynamic number of .and()'s
在玩 Diesel 时,我被困在编写一个函数,该函数将 String
s 的向量作为输入并执行以下操作:
- 将所有
String
组合成一个大查询
- 运行
Connection
上的查询
- 处理结果
- return一个
Vec
如果我一步构建查询,它工作得很好:
fn get_books(authors: Vec<String>, connection: SqliteConnection) {
use schema::ebook::dsl::*;
let inner = author
.like(format!("%{}%", authors[0]))
.and(author.like(format!("%{}%", authors[1])))
.and(author.like(format!("%{}%", authors[2])));
ebook
.filter(inner)
.load::<Ebook>(&connection)
.expect("Error loading ebook");
}
如果我尝试在更多步骤中生成查询(需要使用可变长度的输入向量)我无法编译它:
fn get_books(authors: Vec<String>, connection: SqliteConnection) {
use schema::ebook::dsl::*;
let mut inner = author
.like(format!("%{}%", authors[0]))
.and(author.like(format!("%{}%", authors[1]))); // <1>
inner = inner.and(author.like(format!("%{}%", authors[2]))); // <2>
ebook
.filter(inner)
.load::<Ebook>(&connection)
.expect("Error loading ebook");
}
这会产生以下错误:
inner = inner.and(author.like(format!("%{}%",authors[2])));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `diesel::expression::operators::Like`, found struct `diesel::expression::operators::And`
我不明白为什么 Rust 期望 Like
而不是 And
。标记为 <1>
return 的函数是一个 And
,因此 And
存储在 inner
中。
我错过了什么?为什么第一段代码可以编译而第二段不能?生成这种查询的正确方法是什么?
您需要做的第一件事是查看 complete 错误消息:
error[E0308]: mismatched types
--> src/main.rs:23:13
|
23 | inner = inner.and(author.like(format!("%{}%", authors[2])));//<2>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `diesel::expression::operators::Like`, found struct `diesel::expression::operators::And`
|
= note: expected type `diesel::expression::operators::And<diesel::expression::operators::Like<_, _>, _>`
found type `diesel::expression::operators::And<diesel::expression::operators::And<diesel::expression::operators::Like<_, _>, diesel::expression::operators::Like<schema::ebook::columns::author, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>>, _>`
它很长,但那是因为它是完全合格的。让我们缩短最后一部分:
expected type `And<Like<_, _>, _>`
found type `And<And<Like<_, _>, Like<author, Bound<Text, String>>>, _>`
如果您查看 and
的文档,您会发现每次调用 and
都会消耗接收器和 returns 一个全新的类型 — And
:
fn and<T: AsExpression<Bool>>(self, other: T) -> And<Self, T::Expression>
这是 Diesel 生成强类型 SQL 表达式且没有 运行 时间开销的能力的核心。所有的工作都委托给了类型系统。事实上,Diesel 的创造者有 an entire talk where he shows how far Diesel pushes the type system and what benefits it has.
回到您的问题,不可能将 And<_, _>
存储在与 And<And<_, _>, _>
相同的位置,因为它们将具有不同的大小并且实际上是不同的类型。从根本上讲,这与尝试将整数存储在布尔值中是一样的。
事实上,没有方法可以知道您需要什么具体类型,因为您甚至不知道有多少条件有——这取决于向量的大小。
在这种情况下,我们必须放弃静态调度并通过 trait 对象 转向动态调度。 Diesel 在这种情况下有一个特定的特征(也有很好的例子):BoxableExpression
.
剩下的部分是将您的作者转换为 like
表达式并将它们组合起来。但是,当 authors
为空时,我们需要一个基本情况。我们为此构建了一个平凡真实的陈述 (author = author
)。
#[macro_use]
extern crate diesel;
use diesel::SqliteConnection;
use diesel::prelude::*;
use diesel::sql_types::Bool;
mod schema {
table! {
ebook (id) {
id -> Int4,
author -> Text,
}
}
}
fn get_books(authors: Vec<String>, connection: SqliteConnection) {
use schema::ebook::dsl::*;
let always_true = Box::new(author.eq(author));
let query: Box<BoxableExpression<schema::ebook::table, _, SqlType = Bool>> = authors
.into_iter()
.map(|name| author.like(format!("%{}%", name)))
.fold(always_true, |query, item| {
Box::new(query.and(item))
});
ebook
.filter(query)
.load::<(i32, String)>(&connection)
.expect("Error loading ebook");
}
fn main() {}
如果没有更好的 SQL 方法,我也不会感到惊讶。例如,PostgreSQL 似乎有 WHERE col LIKE ANY( subselect )
和 WHERE col LIKE ALL( subselect )
形式。
在玩 Diesel 时,我被困在编写一个函数,该函数将 String
s 的向量作为输入并执行以下操作:
- 将所有
String
组合成一个大查询 - 运行
Connection
上的查询
- 处理结果
- return一个
Vec
如果我一步构建查询,它工作得很好:
fn get_books(authors: Vec<String>, connection: SqliteConnection) {
use schema::ebook::dsl::*;
let inner = author
.like(format!("%{}%", authors[0]))
.and(author.like(format!("%{}%", authors[1])))
.and(author.like(format!("%{}%", authors[2])));
ebook
.filter(inner)
.load::<Ebook>(&connection)
.expect("Error loading ebook");
}
如果我尝试在更多步骤中生成查询(需要使用可变长度的输入向量)我无法编译它:
fn get_books(authors: Vec<String>, connection: SqliteConnection) {
use schema::ebook::dsl::*;
let mut inner = author
.like(format!("%{}%", authors[0]))
.and(author.like(format!("%{}%", authors[1]))); // <1>
inner = inner.and(author.like(format!("%{}%", authors[2]))); // <2>
ebook
.filter(inner)
.load::<Ebook>(&connection)
.expect("Error loading ebook");
}
这会产生以下错误:
inner = inner.and(author.like(format!("%{}%",authors[2])));
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `diesel::expression::operators::Like`, found struct `diesel::expression::operators::And`
我不明白为什么 Rust 期望 Like
而不是 And
。标记为 <1>
return 的函数是一个 And
,因此 And
存储在 inner
中。
我错过了什么?为什么第一段代码可以编译而第二段不能?生成这种查询的正确方法是什么?
您需要做的第一件事是查看 complete 错误消息:
error[E0308]: mismatched types
--> src/main.rs:23:13
|
23 | inner = inner.and(author.like(format!("%{}%", authors[2])));//<2>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected struct `diesel::expression::operators::Like`, found struct `diesel::expression::operators::And`
|
= note: expected type `diesel::expression::operators::And<diesel::expression::operators::Like<_, _>, _>`
found type `diesel::expression::operators::And<diesel::expression::operators::And<diesel::expression::operators::Like<_, _>, diesel::expression::operators::Like<schema::ebook::columns::author, diesel::expression::bound::Bound<diesel::sql_types::Text, std::string::String>>>, _>`
它很长,但那是因为它是完全合格的。让我们缩短最后一部分:
expected type `And<Like<_, _>, _>`
found type `And<And<Like<_, _>, Like<author, Bound<Text, String>>>, _>`
如果您查看 and
的文档,您会发现每次调用 and
都会消耗接收器和 returns 一个全新的类型 — And
:
fn and<T: AsExpression<Bool>>(self, other: T) -> And<Self, T::Expression>
这是 Diesel 生成强类型 SQL 表达式且没有 运行 时间开销的能力的核心。所有的工作都委托给了类型系统。事实上,Diesel 的创造者有 an entire talk where he shows how far Diesel pushes the type system and what benefits it has.
回到您的问题,不可能将 And<_, _>
存储在与 And<And<_, _>, _>
相同的位置,因为它们将具有不同的大小并且实际上是不同的类型。从根本上讲,这与尝试将整数存储在布尔值中是一样的。
事实上,没有方法可以知道您需要什么具体类型,因为您甚至不知道有多少条件有——这取决于向量的大小。
在这种情况下,我们必须放弃静态调度并通过 trait 对象 转向动态调度。 Diesel 在这种情况下有一个特定的特征(也有很好的例子):BoxableExpression
.
剩下的部分是将您的作者转换为 like
表达式并将它们组合起来。但是,当 authors
为空时,我们需要一个基本情况。我们为此构建了一个平凡真实的陈述 (author = author
)。
#[macro_use]
extern crate diesel;
use diesel::SqliteConnection;
use diesel::prelude::*;
use diesel::sql_types::Bool;
mod schema {
table! {
ebook (id) {
id -> Int4,
author -> Text,
}
}
}
fn get_books(authors: Vec<String>, connection: SqliteConnection) {
use schema::ebook::dsl::*;
let always_true = Box::new(author.eq(author));
let query: Box<BoxableExpression<schema::ebook::table, _, SqlType = Bool>> = authors
.into_iter()
.map(|name| author.like(format!("%{}%", name)))
.fold(always_true, |query, item| {
Box::new(query.and(item))
});
ebook
.filter(query)
.load::<(i32, String)>(&connection)
.expect("Error loading ebook");
}
fn main() {}
如果没有更好的 SQL 方法,我也不会感到惊讶。例如,PostgreSQL 似乎有 WHERE col LIKE ANY( subselect )
和 WHERE col LIKE ALL( subselect )
形式。