Refactor autocompletion routes.
This commit is contained in:
parent
86d301ada5
commit
2660c91732
@ -1,6 +1,8 @@
|
||||
use super::{wrap, Context, Result};
|
||||
use crate::schema::people::dsl as h; // h as in human
|
||||
use crate::schema::photo_people::dsl as pp;
|
||||
use crate::schema::photo_places::dsl as lp;
|
||||
use crate::schema::photo_tags::dsl as tp;
|
||||
use crate::schema::photos::dsl as p;
|
||||
use crate::schema::places::dsl as l;
|
||||
use crate::schema::tags::dsl as t;
|
||||
@ -17,127 +19,42 @@ use warp::reply::{json, Json, Response};
|
||||
use warp::Filter;
|
||||
|
||||
pub fn routes(s: BoxedFilter<(Context,)>) -> BoxedFilter<(Response,)> {
|
||||
end()
|
||||
.and(get())
|
||||
.and(s.clone())
|
||||
.and(query())
|
||||
.map(auto_complete_any)
|
||||
.or(path("tag")
|
||||
.and(get())
|
||||
.and(s.clone())
|
||||
.and(query())
|
||||
.map(auto_complete_tag))
|
||||
.unify()
|
||||
.or(path("person")
|
||||
.and(get())
|
||||
.and(s)
|
||||
.and(query())
|
||||
.map(auto_complete_person))
|
||||
.unify()
|
||||
.map(wrap)
|
||||
.boxed()
|
||||
let egs = end().and(get()).and(s);
|
||||
let any = egs.clone().and(query()).map(list_any);
|
||||
let tag = path("tag").and(egs.clone()).and(query()).map(list_tags);
|
||||
let person = path("person").and(egs).and(query()).map(list_people);
|
||||
any.or(tag).unify().or(person).unify().map(wrap).boxed()
|
||||
}
|
||||
|
||||
sql_function!(fn lower(string: Text) -> Text);
|
||||
sql_function!(fn strpos(string: Text, substring: Text) -> Integer);
|
||||
|
||||
fn auto_complete_any(context: Context, term: AcQ) -> Result<Json> {
|
||||
let tpos = strpos(lower(t::tag_name), &term.q);
|
||||
let query = t::tags
|
||||
.select((t::tag_name, t::slug, tpos))
|
||||
.filter(tpos.gt(0))
|
||||
.into_boxed();
|
||||
let query = if context.is_authorized() {
|
||||
query
|
||||
} else {
|
||||
use crate::schema::photo_tags::dsl as tp;
|
||||
query.filter(t::id.eq_any(tp::photo_tags.select(tp::tag_id).filter(
|
||||
tp::photo_id.eq_any(p::photos.select(p::id).filter(p::is_public)),
|
||||
)))
|
||||
};
|
||||
let db = context.db()?;
|
||||
let mut tags = query
|
||||
.order((tpos, t::tag_name))
|
||||
.limit(10)
|
||||
.load::<(String, String, i32)>(&db)?
|
||||
fn list_any(context: Context, term: AcQ) -> Result<Json> {
|
||||
let mut tags = select_tags(&context, &term)?
|
||||
.into_iter()
|
||||
.map(|(t, s, p)| (SearchTag { k: 't', t, s }, p))
|
||||
.chain({
|
||||
select_people(&context, &term)?
|
||||
.into_iter()
|
||||
.map(|(t, s, p)| (SearchTag { k: 'p', t, s }, p))
|
||||
})
|
||||
.chain({
|
||||
select_places(&context, &term)?
|
||||
.into_iter()
|
||||
.map(|(t, s, p)| (SearchTag { k: 'l', t, s }, p))
|
||||
})
|
||||
.collect::<Vec<_>>();
|
||||
tags.extend({
|
||||
let ppos = strpos(lower(h::person_name), &term.q);
|
||||
let query = h::people
|
||||
.select((h::person_name, h::slug, ppos))
|
||||
.filter(ppos.gt(0))
|
||||
.into_boxed();
|
||||
let query =
|
||||
if context.is_authorized() {
|
||||
query
|
||||
} else {
|
||||
query.filter(h::id.eq_any(
|
||||
pp::photo_people.select(pp::person_id).filter(
|
||||
pp::photo_id.eq_any(
|
||||
p::photos.select(p::id).filter(p::is_public),
|
||||
),
|
||||
),
|
||||
))
|
||||
};
|
||||
query
|
||||
.order((ppos, h::person_name))
|
||||
.limit(10)
|
||||
.load::<(String, String, i32)>(&db)?
|
||||
.into_iter()
|
||||
.map(|(t, s, p)| (SearchTag { k: 'p', t, s }, p))
|
||||
});
|
||||
tags.extend({
|
||||
let lpos = strpos(lower(l::place_name), &term.q);
|
||||
let query = l::places
|
||||
.select((l::place_name, l::slug, lpos))
|
||||
.filter(lpos.gt(0))
|
||||
.into_boxed();
|
||||
let query =
|
||||
if context.is_authorized() {
|
||||
query
|
||||
} else {
|
||||
use crate::schema::photo_places::dsl as lp;
|
||||
query.filter(l::id.eq_any(
|
||||
lp::photo_places.select(lp::place_id).filter(
|
||||
lp::photo_id.eq_any(
|
||||
p::photos.select(p::id).filter(p::is_public),
|
||||
),
|
||||
),
|
||||
))
|
||||
};
|
||||
query
|
||||
.order((lpos, l::place_name))
|
||||
.limit(10)
|
||||
.load::<(String, String, i32)>(&db)?
|
||||
.into_iter()
|
||||
.map(|(t, s, p)| (SearchTag { k: 'l', t, s }, p))
|
||||
});
|
||||
tags.sort_by(|a, b| a.1.cmp(&b.1).then_with(|| a.0.cmp(&b.0)));
|
||||
Ok(json(&tags.iter().map(|(t, _)| t).collect::<Vec<_>>()))
|
||||
}
|
||||
|
||||
fn auto_complete_tag(context: Context, query: AcQ) -> Result<Json> {
|
||||
use crate::schema::tags::dsl::{tag_name, tags};
|
||||
let tpos = strpos(lower(tag_name), query.q);
|
||||
let q = tags
|
||||
.select(tag_name)
|
||||
.filter((&tpos).gt(0))
|
||||
.order((&tpos, tag_name))
|
||||
.limit(10);
|
||||
Ok(json(&q.load::<String>(&context.db()?)?))
|
||||
fn list_tags(context: Context, query: AcQ) -> Result<Json> {
|
||||
Ok(json(&names(select_tags(&context, &query)?)))
|
||||
}
|
||||
|
||||
fn auto_complete_person(context: Context, query: AcQ) -> Result<Json> {
|
||||
use crate::schema::people::dsl::{people, person_name};
|
||||
let mpos = strpos(lower(person_name), query.q);
|
||||
let q = people
|
||||
.select(person_name)
|
||||
.filter((&mpos).gt(0))
|
||||
.order((&mpos, person_name))
|
||||
.limit(10);
|
||||
Ok(json(&q.load::<String>(&context.db()?)?))
|
||||
fn list_people(context: Context, query: AcQ) -> Result<Json> {
|
||||
Ok(json(&names(select_people(&context, &query)?)))
|
||||
}
|
||||
|
||||
fn names(data: Vec<NameSlugScore>) -> Vec<String> {
|
||||
data.into_iter().map(|(name, _, _)| name).collect()
|
||||
}
|
||||
|
||||
/// A `q` query argument which must not contain the null character.
|
||||
@ -224,3 +141,69 @@ mod tests {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
sql_function!(fn lower(string: Text) -> Text);
|
||||
sql_function!(fn strpos(string: Text, substring: Text) -> Integer);
|
||||
|
||||
type NameSlugScore = (String, String, i32);
|
||||
|
||||
fn select_tags(context: &Context, term: &AcQ) -> Result<Vec<NameSlugScore>> {
|
||||
let tpos = strpos(lower(t::tag_name), &term.q);
|
||||
let query = t::tags
|
||||
.select((t::tag_name, t::slug, tpos))
|
||||
.filter(tpos.gt(0))
|
||||
.into_boxed();
|
||||
let query = if context.is_authorized() {
|
||||
query
|
||||
} else {
|
||||
query.filter(t::id.eq_any(tp::photo_tags.select(tp::tag_id).filter(
|
||||
tp::photo_id.eq_any(p::photos.select(p::id).filter(p::is_public)),
|
||||
)))
|
||||
};
|
||||
let db = context.db()?;
|
||||
Ok(query.order((tpos, t::tag_name)).limit(10).load(&db)?)
|
||||
}
|
||||
|
||||
fn select_people(context: &Context, term: &AcQ) -> Result<Vec<NameSlugScore>> {
|
||||
let ppos = strpos(lower(h::person_name), &term.q);
|
||||
let query = h::people
|
||||
.select((h::person_name, h::slug, ppos))
|
||||
.filter(ppos.gt(0))
|
||||
.into_boxed();
|
||||
let query = if context.is_authorized() {
|
||||
query
|
||||
} else {
|
||||
query.filter(
|
||||
h::id.eq_any(
|
||||
pp::photo_people.select(pp::person_id).filter(
|
||||
pp::photo_id
|
||||
.eq_any(p::photos.select(p::id).filter(p::is_public)),
|
||||
),
|
||||
),
|
||||
)
|
||||
};
|
||||
let db = context.db()?;
|
||||
Ok(query.order((ppos, h::person_name)).limit(10).load(&db)?)
|
||||
}
|
||||
|
||||
fn select_places(context: &Context, term: &AcQ) -> Result<Vec<NameSlugScore>> {
|
||||
let lpos = strpos(lower(l::place_name), &term.q);
|
||||
let query = l::places
|
||||
.select((l::place_name, l::slug, lpos))
|
||||
.filter(lpos.gt(0))
|
||||
.into_boxed();
|
||||
let query = if context.is_authorized() {
|
||||
query
|
||||
} else {
|
||||
query.filter(
|
||||
l::id.eq_any(
|
||||
lp::photo_places.select(lp::place_id).filter(
|
||||
lp::photo_id
|
||||
.eq_any(p::photos.select(p::id).filter(p::is_public)),
|
||||
),
|
||||
),
|
||||
)
|
||||
};
|
||||
let db = context.db()?;
|
||||
Ok(query.order((lpos, l::place_name)).limit(10).load(&db)?)
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user