Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(psql): introduce column type ltree #604

Merged
merged 2 commits into from
Oct 4, 2023

Conversation

EstebanBorai
Copy link
Contributor

@EstebanBorai EstebanBorai commented Feb 15, 2023

PR Info

Introduces the ltree type for PostgreSQL.

  • Closes

Aims to close: SeaQL/sea-orm#1476

New Features

  • PostgreSQL ltree support

@EstebanBorai
Copy link
Contributor Author

Thanks for this amazing library! I wanted to help introducing ltree to the library and decided to go ahead and implement support for PostgreSQL. Theres still some bits that require some decisions.

  • How can we have the CREATE EXTENSION ltree statement to be executed when using this type?
  • Is it possible to have certain operators on this type? Turns out ltree comes with poweful operators as well: https://www.postgresql.org/docs/current/ltree.html#LTREE-OP-TABLE
  • It would be great to have this type available for sea-orm as well, my goal is to have this type being supported in the orm as well.

Thanks in advance!

Copy link
Member

@billy1624 billy1624 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hey @EstebanBorai, sorry for the delay and thanks for contributing!!

src/extension/postgres/ltree.rs Outdated Show resolved Hide resolved
@billy1624
Copy link
Member

billy1624 commented Feb 24, 2023

  • How can we have the CREATE EXTENSION ltree statement to be executed when using this type?

I'd suggest adding a ExtensionCreateStatement similar to the TypeCreateStatement that we already had https://docs.rs/sea-query/latest/sea_query/extension/postgres/struct.TypeCreateStatement.html

Sure! Postgres specific operators goes to PgBinOper:

/// Binary operator
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PgBinOper {
ILike,
NotILike,
Matches,
Contains,
Contained,
Concatenate,
Similarity,
WordSimilarity,
StrictWordSimilarity,
SimilarityDistance,
WordSimilarityDistance,
StrictWordSimilarityDistance,
}

And its construct goes to PgExpr:

use super::PgBinOper;
use crate::{expr::private::Expression, Expr, IntoLikeExpr, SimpleExpr};
pub trait PgExpr: Expression {
/// Express an postgres concatenate (`||`) expression.
///
/// # Examples
///
/// ```
/// use sea_query::{extension::postgres::PgExpr, tests_cfg::*, *};
///
/// let query = Query::select()
/// .columns([Font::Name, Font::Variant, Font::Language])
/// .from(Font::Table)
/// .and_where(Expr::val("a").concatenate("b").concat("c").concat("d"))
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "name", "variant", "language" FROM "font" WHERE 'a' || 'b' || 'c' || 'd'"#
/// );
/// ```
fn concatenate<T>(self, right: T) -> SimpleExpr
where
T: Into<SimpleExpr>,
{
self.bin_op(PgBinOper::Concatenate, right)
}
/// Alias of [`PgExpr::concatenate`]
fn concat<T>(self, right: T) -> SimpleExpr
where
T: Into<SimpleExpr>,
{
self.concatenate(right)
}
/// Express an postgres fulltext search matches (`@@`) expression.
///
/// # Examples
///
/// ```
/// use sea_query::{*, tests_cfg::*, extension::postgres::PgExpr};
///
/// let query = Query::select()
/// .columns([Font::Name, Font::Variant, Font::Language])
/// .from(Font::Table)
/// .and_where(Expr::val("a & b").matches("a b"))
/// .and_where(Expr::col(Font::Name).matches("a b"))
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "name", "variant", "language" FROM "font" WHERE 'a & b' @@ 'a b' AND "name" @@ 'a b'"#
/// );
/// ```
fn matches<T>(self, expr: T) -> SimpleExpr
where
T: Into<SimpleExpr>,
{
self.bin_op(PgBinOper::Matches, expr)
}
/// Express an postgres fulltext search contains (`@>`) expression.
///
/// # Examples
///
/// ```
/// use sea_query::{*, tests_cfg::*, extension::postgres::PgExpr};
///
/// let query = Query::select()
/// .columns([Font::Name, Font::Variant, Font::Language])
/// .from(Font::Table)
/// .and_where(Expr::val("a & b").contains("a b"))
/// .and_where(Expr::col(Font::Name).contains("a b"))
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "name", "variant", "language" FROM "font" WHERE 'a & b' @> 'a b' AND "name" @> 'a b'"#
/// );
/// ```
fn contains<T>(self, expr: T) -> SimpleExpr
where
T: Into<SimpleExpr>,
{
self.bin_op(PgBinOper::Contains, expr)
}
/// Express an postgres fulltext search contained (`<@`) expression.
///
/// # Examples
///
/// ```
/// use sea_query::{*, tests_cfg::*, extension::postgres::PgExpr};
///
/// let query = Query::select()
/// .columns([Font::Name, Font::Variant, Font::Language])
/// .from(Font::Table)
/// .and_where(Expr::val("a & b").contained("a b"))
/// .and_where(Expr::col(Font::Name).contained("a b"))
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "name", "variant", "language" FROM "font" WHERE 'a & b' <@ 'a b' AND "name" <@ 'a b'"#
/// );
/// ```
fn contained<T>(self, expr: T) -> SimpleExpr
where
T: Into<SimpleExpr>,
{
self.bin_op(PgBinOper::Contained, expr)
}
/// Express a `ILIKE` expression.
///
/// # Examples
///
/// ```
/// use sea_query::{*, tests_cfg::*, extension::postgres::PgExpr};
///
/// let query = Query::select()
/// .columns([Char::Character, Char::SizeW, Char::SizeH])
/// .from(Char::Table)
/// .and_where(Expr::col((Char::Table, Char::Character)).ilike("Ours'%"))
/// .to_owned();
///
/// assert_eq!(
/// query.to_string(PostgresQueryBuilder),
/// r#"SELECT "character", "size_w", "size_h" FROM "character" WHERE "character"."character" ILIKE E'Ours\'%'"#
/// );
/// ```
fn ilike<L>(self, like: L) -> SimpleExpr
where
L: IntoLikeExpr,
{
self.like_like(PgBinOper::ILike, like.into_like_expr())
}
/// Express a `NOT ILIKE` expression
fn not_ilike<L>(self, like: L) -> SimpleExpr
where
L: IntoLikeExpr,
{
self.like_like(PgBinOper::NotILike, like.into_like_expr())
}
}
impl PgExpr for Expr {}
impl PgExpr for SimpleExpr {}

  • It would be great to have this type available for sea-orm as well, my goal is to have this type being supported in the orm as well.

That would be nice! But one step at a time, we should focus on bring it onto SeaQuery first.

@EstebanBorai
Copy link
Contributor Author

I'd suggest adding a ExtensionCreateStatement similar to the TypeCreateStatement that we already had https://docs.rs/sea-query/latest/sea_query/extension/postgres/struct.TypeCreateStatement.html

Just to be clear, the user will be responsible of running this code as part of writing migrations when using the PgLTree type, right?

@billy1624
Copy link
Member

I'd suggest adding a ExtensionCreateStatement similar to the TypeCreateStatement that we already had https://docs.rs/sea-query/latest/sea_query/extension/postgres/struct.TypeCreateStatement.html

Just to be clear, the user will be responsible of running this code as part of writing migrations when using the PgLTree type, right?

You're right!

@billy1624
Copy link
Member

Hey @EstebanBorai, would you like to introduce the follow things as discussed above?

  • Adding ExtensionCreateStatement
  • Adding LTree related operators to PgBinOper
  • Adding constructs for LTree related opertros to PgExpr

@EstebanBorai
Copy link
Contributor Author

Hey @EstebanBorai, would you like to introduce the follow things as discussed above?

  • Adding ExtensionCreateStatement
  • Adding LTree related operators to PgBinOper
  • Adding constructs for LTree related opertros to PgExpr

Hi @billy1624!

Here is a PR for the "Adding ExtensionCreateStatement" task!

Copy link
Member

@tyt2y3 tyt2y3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is a Postgres only feature, we have to put it under extensions. Please refer to the pg Type statement.

@ikrivosheev
Copy link
Member

@EstebanBorai hello! Any updates?

@EstebanBorai
Copy link
Contributor Author

@EstebanBorai hello! Any updates?

Hi @ikrivosheev actually this PR is waiting for this other: #616 where Create Extension Statement is being introduced for PSQL!

After that is merged I will be able to continue working on this!

@EstebanBorai EstebanBorai force-pushed the feat/postgresql-ltree branch from 33436e4 to 3094255 Compare July 15, 2023 22:12
@EstebanBorai
Copy link
Contributor Author

Hi @billy1624 and @ikrivosheev !

Given that we landed #616 Im planning to move forward and introduce the ltree extension.
Do you guys have any preferences or suggestions on how to accomplish this?

@EstebanBorai EstebanBorai changed the title feat: introduce column type ltree feat(psql): introduce column type ltree Jul 15, 2023
src/table/column.rs Outdated Show resolved Hide resolved
@EstebanBorai
Copy link
Contributor Author

Hi @billy1624 and @ikrivosheev!

Im working on the LTree type now, the missing work to have this implementation ready is providing support to the LTree specialized operations and functions.

LTree supports widely used comparison operators such as <, >, >=, <=, <> =.
But theres also functions and other operators which are available for this column type only.

Is there a place where we could introduce them? I saw sea-query-postgres has specialized types for PostgreSQL Column types, like MacAddress, UUID and Date/Time. Perhaps thats a place for this purpose?

With regards to operators, it would be awesome to have access to them on WHERE clauses, to be able to use on use cases like:

ltreetest=> SELECT path FROM test WHERE path <@ 'Top.Science';
                path
------------------------------------
 Top.Science
 Top.Science.Astronomy
 Top.Science.Astronomy.Astrophysics
 Top.Science.Astronomy.Cosmology
(4 rows)

Then for functions I think its a bit more complex, we should be able to create queries like:

ltreetest=> SELECT subpath(path,0,2)||'Space'||subpath(path,2) FROM test WHERE path <@ 'Top.Science.Astronomy';
                 ?column?
------------------------------------------
 Top.Science.Space.Astronomy
 Top.Science.Space.Astronomy.Astrophysics
 Top.Science.Space.Astronomy.Cosmology
(3 rows)

Perhaps theres a way to partially introduce LTree support? Perhaps we could have operators support on a release and then have functions support?

Thanks in advance!

@EstebanBorai
Copy link
Contributor Author

@ikrivosheev I just introduced the PgLTree here type in case we want to use it for Query specialized operators.

@EstebanBorai
Copy link
Contributor Author

Hi guys! @billy1624 @ikrivosheev any updates on this? It would be awesome to merge this and move into SeoORM 🚀

Copy link
Member

@tyt2y3 tyt2y3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice and cleanly done. I'd probably add a doc test

@tyt2y3 tyt2y3 merged commit cc8e78c into SeaQL:master Oct 4, 2023
@EstebanBorai EstebanBorai deleted the feat/postgresql-ltree branch October 5, 2023 18:32
Copy link

🎉 Released In 0.30.3 🎉

Thank you everyone for the contribution!
This feature is now available in the latest release. Now is a good time to upgrade!
Your participation is what makes us unique; your adoption is what drives us forward.
You can support SeaQL 🌊 by starring our repos, sharing our libraries and becoming a sponsor ⭐.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

4 participants