Finished service layer and dao layer. V1 coming soon

This commit is contained in:
Franklin 2023-03-10 19:32:27 -04:00
parent 61b547d7a0
commit 227b0b5372
13 changed files with 367 additions and 54 deletions

1
Cargo.lock generated
View File

@ -1473,6 +1473,7 @@ dependencies = [
"chrono-tz",
"format_num",
"serde",
"serde_json",
"sqlx",
"uuid",
]

View File

@ -1,7 +1,7 @@
CREATE TABLE IF NOT EXISTS "property_details" (
property_id UUID PRIMARY KEY,
meters FLOAT4 NOT NULL,
listing_type BYTEA NOT NULL,
listing_type VARCHAR NOT NULL,
photo_urls BYTEA NOT NULL,
location_id UUID NOT NULL
);

1
sql/agent/get.sql Normal file
View File

@ -0,0 +1 @@
SELECT * FROM agent ORDER BY time_created DESC LIMIT 50 OFFSET $1;

View File

@ -0,0 +1,21 @@
SELECT
p.id,
p.title,
p.description,
p.agent_id,
p.state as "state: _",
p.time_created,
p.last_updated
FROM property p, property_details pd, location l where pd.property_id = p.id AND pd.location_id = l.id
AND (p.time_created <= $1 OR $1 = null) -- Before Filter
AND (p.time_created >= $2 OR $2 = null) -- After Filter
AND (p.title LIKE ('%' || LOWER($3) || '%') OR $3 = null) -- Title filter (like)
AND (p.description LIKE ('%' || LOWER($4) || '%') OR $4 = null) -- Description Filter (like)
AND (pd.meters > $5 OR $5 = null) -- Bigger than or equal to filter
AND (pd.meters < $6 OR $6 = null) -- Smaller than or equal to filter
AND (pd.location_id = $7 OR $7 = null) -- Location Filter
AND (pd.listing_type LIKE $8 || '%' OR $8 = null) -- Listing type filter
AND (split_part(pd.listing_type, ' ', 2)::FLOAT8 >= $9::FLOAT8 OR $9 = null) -- More expensive than filter
AND (split_part(pd.listing_type, ' ', 2)::FLOAT8 <= $10::FLOAT8 OR $10 = null) -- More expensive than filter
ORDER BY p.time_created DESC
LIMIT 25 OFFSET $11

View File

@ -6,4 +6,7 @@ agent_id,
state as "state: _",
time_created,
last_updated
FROM property where id = ANY($1);
FROM property
WHERE id = ANY($1)
ORDER BY time_created DESC
LIMIT 50

View File

@ -1,5 +1,61 @@
{
"db": "PostgreSQL",
"0370da7558790827f09104791a6a15be8b0e9580da39d5f1e8dd2e47a48a692f": {
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Uuid"
},
{
"name": "title",
"ordinal": 1,
"type_info": "Varchar"
},
{
"name": "description",
"ordinal": 2,
"type_info": "Varchar"
},
{
"name": "agent_id",
"ordinal": 3,
"type_info": "Uuid"
},
{
"name": "state: _",
"ordinal": 4,
"type_info": "Bytea"
},
{
"name": "time_created",
"ordinal": 5,
"type_info": "Timestamptz"
},
{
"name": "last_updated",
"ordinal": 6,
"type_info": "Timestamptz"
}
],
"nullable": [
false,
false,
false,
false,
false,
false,
false
],
"parameters": {
"Left": [
"UuidArray"
]
}
},
"query": "SELECT \nid,\ntitle,\ndescription,\nagent_id,\nstate as \"state: _\",\ntime_created,\nlast_updated\nFROM property\nWHERE id = ANY($1)\nORDER BY time_created DESC\nLIMIT 50"
},
"044ecd955f880a5d7e3185b6dc5217c01cb88100d2e94dea5323ee0a10a45fc0": {
"describe": {
"columns": [
@ -211,6 +267,72 @@
},
"query": "INSERT INTO property (\n id,\n title,\n description,\n agent_id,\n state,\n time_created,\n last_updated\n) VALUES (\n $1,\n $2,\n $3,\n $4,\n $5,\n $6,\n $6\n) RETURNING\nid,\ntitle,\ndescription,\nagent_id,\nstate as \"state: _\",\ntime_created,\nlast_updated;"
},
"2bcf024ea2185368a01e109b697bb69564f96b213595d16d0bcde240c2a21b06": {
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Uuid"
},
{
"name": "title",
"ordinal": 1,
"type_info": "Varchar"
},
{
"name": "description",
"ordinal": 2,
"type_info": "Varchar"
},
{
"name": "agent_id",
"ordinal": 3,
"type_info": "Uuid"
},
{
"name": "state: _",
"ordinal": 4,
"type_info": "Bytea"
},
{
"name": "time_created",
"ordinal": 5,
"type_info": "Timestamptz"
},
{
"name": "last_updated",
"ordinal": 6,
"type_info": "Timestamptz"
}
],
"nullable": [
false,
false,
false,
false,
false,
false,
false
],
"parameters": {
"Left": [
"Timestamptz",
"Timestamptz",
"Text",
"Text",
"Float4",
"Float4",
"Uuid",
"Text",
"Float8",
"Float8",
"Int8"
]
}
},
"query": "SELECT \np.id,\np.title,\np.description,\np.agent_id,\np.state as \"state: _\",\np.time_created,\np.last_updated\nFROM property p, property_details pd, location l where pd.property_id = p.id AND pd.location_id = l.id\nAND (p.time_created <= $1 OR $1 = null) -- Before Filter\nAND (p.time_created >= $2 OR $2 = null) -- After Filter\nAND (p.title LIKE ('%' || LOWER($3) || '%') OR $3 = null) -- Title filter (like)\nAND (p.description LIKE ('%' || LOWER($4) || '%') OR $4 = null) -- Description Filter (like)\nAND (pd.meters > $5 OR $5 = null) -- Bigger than or equal to filter\nAND (pd.meters < $6 OR $6 = null) -- Smaller than or equal to filter\nAND (pd.location_id = $7 OR $7 = null) -- Location Filter\nAND (pd.listing_type LIKE $8 || '%' OR $8 = null) -- Listing type filter\nAND (split_part(pd.listing_type, ' ', 2)::FLOAT8 >= $9::FLOAT8 OR $9 = null) -- More expensive than filter\nAND (split_part(pd.listing_type, ' ', 2)::FLOAT8 <= $10::FLOAT8 OR $10 = null) -- More expensive than filter\nORDER BY p.time_created DESC\nLIMIT 25 OFFSET $11"
},
"403be6e4c7b32698eb9dd9c43f9c13f7f03dc95671f1c81a5391df05c51a92fc": {
"describe": {
"columns": [
@ -361,7 +483,7 @@
},
"query": "SELECT * FROM location WHERE id = ANY($1);"
},
"75508bc7b114d6f9915cadf9708c376d2ca1a6dcdd6bff24b1a717cba0ec2fca": {
"9656e56cd7da8d41f98e7746421101900d4926a5ad5aa6dc21da39d227fed28a": {
"describe": {
"columns": [
{
@ -370,40 +492,22 @@
"type_info": "Uuid"
},
{
"name": "title",
"name": "full_name",
"ordinal": 1,
"type_info": "Varchar"
},
{
"name": "description",
"ordinal": 2,
"type_info": "Varchar"
},
{
"name": "agent_id",
"ordinal": 3,
"type_info": "Uuid"
},
{
"name": "state: _",
"ordinal": 4,
"type_info": "Bytea"
},
{
"name": "time_created",
"ordinal": 5,
"ordinal": 2,
"type_info": "Timestamptz"
},
{
"name": "last_updated",
"ordinal": 6,
"ordinal": 3,
"type_info": "Timestamptz"
}
],
"nullable": [
false,
false,
false,
false,
false,
false,
@ -411,11 +515,11 @@
],
"parameters": {
"Left": [
"UuidArray"
"Int8"
]
}
},
"query": "SELECT \nid,\ntitle,\ndescription,\nagent_id,\nstate as \"state: _\",\ntime_created,\nlast_updated\nFROM property where id = ANY($1);"
"query": "SELECT * FROM agent ORDER BY time_created DESC LIMIT 50 OFFSET $1;"
},
"a05953d6326a1fa6da0444234846b6ee761c8c38c69145b84a2f8d321d879289": {
"describe": {
@ -433,7 +537,7 @@
{
"name": "listing_type: _",
"ordinal": 2,
"type_info": "Bytea"
"type_info": "Varchar"
},
{
"name": "photo_urls: _",
@ -457,7 +561,7 @@
"Left": [
"Uuid",
"Float4",
"Bytea",
"Varchar",
"Bytea",
"Uuid"
]
@ -481,7 +585,7 @@
{
"name": "listing_type: _",
"ordinal": 2,
"type_info": "Bytea"
"type_info": "Varchar"
},
{
"name": "photo_urls: _",
@ -525,7 +629,7 @@
{
"name": "listing_type: _",
"ordinal": 2,
"type_info": "Bytea"
"type_info": "Varchar"
},
{
"name": "photo_urls: _",
@ -548,7 +652,7 @@
"parameters": {
"Left": [
"Float4",
"Bytea",
"Varchar",
"Bytea",
"Uuid"
]

View File

@ -25,6 +25,16 @@ pub async fn get_agents_with_ids(
.await
}
pub async fn get_agents_paged(
conn: &PgPool,
page: &i64,
) -> Result<Vec<Agent>, sqlx::error::Error> {
let offset = (page - 1) * 50;
sqlx::query_file_as!(Agent, "sql/agent/get.sql", offset)
.fetch_all(conn)
.await
}
pub async fn update_agent(
conn: &mut Transaction<'_, Postgres>,
agent: &Agent,

View File

@ -1,4 +1,4 @@
use remax_types::domain::property::Property;
use remax_types::domain::{filters::property::PropertyFilter, property::Property};
use sqlx::{PgPool, Postgres, Transaction};
use uuid::Uuid;
@ -19,6 +19,7 @@ pub async fn insert_property(
.fetch_one(conn)
.await
}
pub async fn get_properties_with_ids(
conn: &PgPool,
ids: &Vec<Uuid>,
@ -28,6 +29,79 @@ pub async fn get_properties_with_ids(
.await
}
pub async fn fetch_properties_paged_with_filters(
conn: &PgPool,
filters: &Vec<PropertyFilter>,
page: &i64,
) -> Result<Vec<Property>, sqlx::error::Error> {
let offset = (page - 1) * 25;
let mut before_filter = None;
let mut after_filter = None;
let mut title_filter = None;
let mut description_filter = None;
let mut bigger_filter = None;
let mut smaller_filter = None;
let mut location_filter = None;
let mut listing_type_filter = None;
let mut more_expensive_than_filter = None;
let mut cheaper_than_filter = None;
for filter in filters {
match filter {
PropertyFilter::Before(before_time) => before_filter = Some(before_time),
PropertyFilter::After(after_time) => after_filter = Some(after_time),
PropertyFilter::Title(title_like) => title_filter = Some(title_like),
PropertyFilter::BiggerOrEqualTo(meters) => bigger_filter = Some(meters),
PropertyFilter::SmallerOrEqualTo(meters) => smaller_filter = Some(meters),
PropertyFilter::Description(description_like) => {
description_filter = Some(description_like)
}
PropertyFilter::Location(location_id) => location_filter = Some(location_id),
PropertyFilter::MoreExpensiveThan(listing_type) => {
listing_type_filter = Some(listing_type.to_string().split(" ").collect::<String>());
more_expensive_than_filter = match listing_type {
remax_types::domain::property_details::ListingType::Rent(amount) => {
Some(amount)
}
remax_types::domain::property_details::ListingType::Sale(amount) => {
Some(amount)
}
_ => None,
}
}
PropertyFilter::CheaperThan(listing_type) => {
listing_type_filter = Some(listing_type.to_string().split(" ").collect::<String>());
cheaper_than_filter = match listing_type {
remax_types::domain::property_details::ListingType::Rent(amount) => {
Some(amount)
}
remax_types::domain::property_details::ListingType::Sale(amount) => {
Some(amount)
}
_ => None,
}
}
};
}
sqlx::query_file_as!(
Property,
"sql/property/fetch_paged_with_filters.sql",
before_filter,
after_filter,
title_filter,
description_filter,
bigger_filter,
smaller_filter,
location_filter,
listing_type_filter,
more_expensive_than_filter,
cheaper_than_filter,
offset
)
.fetch_all(conn)
.await
}
pub async fn update_property(
conn: &mut Transaction<'_, Postgres>,
property: &Property,

View File

@ -1,9 +1,2 @@
// TODO: Property
// - Add property
// - Remove Properties by ids
// - Update Property
// TODO: Agent
// - Add agent
// - Remove agents
// - Update agents

View File

@ -1,3 +1,3 @@
pub mod admin;
pub mod customer;
pub mod read;
pub mod main_router;

View File

@ -127,7 +127,7 @@ pub async fn update_listing(
let mut tx = handle_tx!(conn.begin());
let mut persisted_properties = handle_db_read_op!(dao::property::get_properties_with_ids(
conn,
&vec![update_property_payload.property_id]
&vec![update_property_payload.property_id],
));
let mut persisted_properties_details =
handle_db_read_op!(dao::property_details::get_properties_with_ids(

View File

@ -1,21 +1,127 @@
// TODO: Most important method: Get First page of property listings (Filters)
// TODO: Get individual property listing with all the info (contact, location, property, details)
// TODO: Get all agents
use actix_web_utils::extensions::typed_response::TypedHttpResponse;
use remax_types::{
domain::agent::Agent,
dto::property::{ListingContainer, PropertyContainer},
domain::{agent::Agent, filters::property::PropertyFilter},
dto::{
agent::AgentContainer,
property::{ListingContainer, PropertyContainer},
},
};
use sqlx::PgPool;
use uuid::Uuid;
use crate::{
dao,
handle_db_read_op, success, unwrap_or_not_found,
};
pub async fn get_property_listings_paged() -> TypedHttpResponse<PropertyContainer> {
todo!()
/// Method to call when loading in to the homepage or searching anything
/// Pending tests
pub async fn get_property_listings_paged(
conn: &PgPool,
filters: &Vec<PropertyFilter>,
page: &i64,
) -> TypedHttpResponse<Vec<PropertyContainer>> {
let properties_filtered = handle_db_read_op!(
dao::property::fetch_properties_paged_with_filters(conn, filters, page)
);
let property_ids: Vec<Uuid> = properties_filtered
.iter()
.map(|property| property.id)
.collect();
let property_details = handle_db_read_op!(dao::property_details::get_properties_with_ids(
conn,
&property_ids
));
let location_ids: Vec<Uuid> = property_details
.iter()
.map(|property_details| property_details.location_id)
.collect();
let locations = handle_db_read_op!(dao::location::get_locations_with_ids(conn, &location_ids));
let mut property_containers: Vec<PropertyContainer> = Vec::new();
for property in properties_filtered {
let property_details = unwrap_or_not_found!(
property_details
.iter()
.find(|property_detail| property_detail.property_id == property.id),
"filtered property details"
)
.clone();
let location = unwrap_or_not_found!(
locations
.iter()
.find(|loc| loc.id == property_details.location_id),
"filtered location"
)
.clone();
property_containers.push(PropertyContainer {
property,
details: property_details,
location,
});
}
success!(property_containers)
}
pub async fn get_listing_container() -> TypedHttpResponse<ListingContainer> {
todo!()
pub async fn get_listing_container(
conn: &PgPool,
listing_id: &Uuid,
) -> TypedHttpResponse<ListingContainer> {
let property = unwrap_or_not_found!(
handle_db_read_op!(dao::property::get_properties_with_ids(
conn,
&vec![*listing_id]
))
.first(),
"properties"
)
.clone();
let details = unwrap_or_not_found!(
handle_db_read_op!(dao::property_details::get_properties_with_ids(
conn,
&vec![*listing_id]
))
.first(),
"property_details"
)
.clone();
let location = unwrap_or_not_found!(
handle_db_read_op!(dao::location::get_location_with_id(
conn,
&details.location_id
)),
"locations"
);
let agent = unwrap_or_not_found!(
handle_db_read_op!(dao::agent::get_agents_with_ids(
conn,
&vec![property.agent_id]
))
.first(),
"agents"
)
.clone();
let contact = unwrap_or_not_found!(
handle_db_read_op!(dao::contact_info::get_contact_infos_with_ids(
conn,
&vec![agent.id]
))
.first(),
"contact_infos"
)
.clone();
success!(ListingContainer {
property: PropertyContainer {
property,
details,
location,
},
agent: AgentContainer { agent, contact },
})
}
pub async fn get_all_agents() -> TypedHttpResponse<Vec<Agent>> {
todo!()
pub async fn get_all_agents(conn: &PgPool, page: &i64) -> TypedHttpResponse<Vec<Agent>> {
let agents = handle_db_read_op!(dao::agent::get_agents_paged(conn, page));
success!(agents)
}