Change Serials to UUIDs, fix translation table
Languages now refer to other languages they are translated to through an additional table rather than an array of identifiers. This ensures no orphan identifier remains when a language is deleted. The primary key of languages is now a unique identifier rather than the name of the language itself. It now allows for multiple languages to have the same name. Their unique identifier is now a v4 UUID. Set Diesel to specific version 2.0.2, since 2.0 apparently does not mean the latest version of 2.0.z and 2.0 has issues with its uuid feature. Cleanup and simplify some code. Some more GraphQL documentation on available queries.
This commit is contained in:
@@ -119,7 +119,7 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn words(&self, language: &str, word: &str) -> Vec<Word> {
|
||||
pub fn words(&self, language: uuid::Uuid, word: &str) -> Vec<Word> {
|
||||
use self::schema::words::dsl;
|
||||
if let Ok(conn) = &mut self.conn() {
|
||||
match dsl::words
|
||||
|
||||
@@ -3,10 +3,12 @@ use diesel::prelude::*;
|
||||
use juniper::GraphQLEnum;
|
||||
use tracing::info;
|
||||
|
||||
use uuid::Uuid;
|
||||
|
||||
use super::super::schema;
|
||||
use super::users::User;
|
||||
|
||||
use schema::{langandagents, languages};
|
||||
use schema::{langandagents, langtranslatesto, languages};
|
||||
|
||||
#[derive(
|
||||
diesel_derive_enum::DbEnum, Debug, Clone, PartialEq, Eq, GraphQLEnum,
|
||||
@@ -43,12 +45,12 @@ pub enum AgentLanguageRelation {
|
||||
Author,
|
||||
}
|
||||
|
||||
#[derive(Queryable, Insertable, Debug, Clone, PartialEq, Eq)]
|
||||
#[derive(Queryable, Insertable, Debug, Clone)]
|
||||
pub struct Language {
|
||||
id: Uuid,
|
||||
name: String,
|
||||
native: Option<String>,
|
||||
release: Release,
|
||||
targetlanguage: Vec<Option<String>>,
|
||||
genre: Vec<Option<DictGenre>>,
|
||||
abstract_: Option<String>,
|
||||
created: chrono::NaiveDateTime,
|
||||
@@ -67,7 +69,7 @@ impl Language {
|
||||
use schema::langandagents::dsl;
|
||||
match &mut context.conn() {
|
||||
Ok(conn) => dsl::langandagents
|
||||
.filter(dsl::language.eq(self.name.clone()))
|
||||
.filter(dsl::language.eq(self.id))
|
||||
.filter(dsl::relationship.eq(relationship))
|
||||
.load::<LangAndAgent>(conn)
|
||||
.unwrap()
|
||||
@@ -97,6 +99,11 @@ impl Language {
|
||||
|
||||
#[juniper::graphql_object(Context = Database)]
|
||||
impl Language {
|
||||
#[graphql(description = "Unique identifier of the language")]
|
||||
fn id(&self) -> String {
|
||||
self.id.to_string()
|
||||
}
|
||||
|
||||
#[graphql(
|
||||
description = "Name in the main target language (often English) of the described language"
|
||||
)]
|
||||
@@ -119,23 +126,16 @@ impl Language {
|
||||
description = "Languages in which the current language is translated"
|
||||
)]
|
||||
fn target_language(&self, context: &Database) -> Vec<Language> {
|
||||
use schema::languages::dsl;
|
||||
use schema::langtranslatesto::dsl;
|
||||
match &mut context.conn() {
|
||||
Ok(conn) => self
|
||||
.targetlanguage
|
||||
.clone()
|
||||
Ok(conn) => dsl::langtranslatesto
|
||||
.filter(dsl::langfrom.eq(self.id))
|
||||
.load::<LangTranslatesTo>(conn)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|l| dsl::languages.find(l).first::<Language>(conn))
|
||||
.filter_map(|l| match l {
|
||||
Ok(language) => Some(language),
|
||||
Err(e) => {
|
||||
info!(
|
||||
"Failed to retrieve language from database: {:?}",
|
||||
e
|
||||
);
|
||||
None
|
||||
}
|
||||
.flat_map(|l| {
|
||||
use schema::languages::dsl;
|
||||
dsl::languages.find(l.langto).first::<Language>(conn)
|
||||
})
|
||||
.collect::<Vec<Language>>(),
|
||||
Err(e) => {
|
||||
@@ -223,6 +223,14 @@ impl Language {
|
||||
pub struct LangAndAgent {
|
||||
id: i32,
|
||||
agent: String,
|
||||
language: String,
|
||||
language: Uuid,
|
||||
relationship: AgentLanguageRelation,
|
||||
}
|
||||
|
||||
#[derive(Queryable, Insertable, Debug, Clone, PartialEq, Eq)]
|
||||
#[diesel(table_name = langtranslatesto)]
|
||||
pub struct LangTranslatesTo {
|
||||
id: i32,
|
||||
langfrom: Uuid,
|
||||
langto: Uuid,
|
||||
}
|
||||
|
||||
@@ -14,7 +14,9 @@ pub enum WordRelationship {
|
||||
Related,
|
||||
}
|
||||
|
||||
#[derive(diesel_derive_enum::DbEnum, Debug, Clone, PartialEq, Eq, GraphQLEnum)]
|
||||
#[derive(
|
||||
diesel_derive_enum::DbEnum, Debug, Clone, PartialEq, Eq, GraphQLEnum,
|
||||
)]
|
||||
#[DieselTypePath = "crate::db::schema::sql_types::Partofspeech"]
|
||||
pub enum PartOfSpeech {
|
||||
Adjective,
|
||||
@@ -44,7 +46,7 @@ pub struct Word {
|
||||
norm: String,
|
||||
native: Option<String>,
|
||||
lemma: Option<String>,
|
||||
language: String,
|
||||
language: uuid::Uuid,
|
||||
partofspeech: PartOfSpeech,
|
||||
audio: Option<String>,
|
||||
video: Option<String>,
|
||||
@@ -56,29 +58,30 @@ pub struct Word {
|
||||
}
|
||||
|
||||
impl Word {
|
||||
fn relationship(&self, context: &Database, relationship: WordRelationship) -> Vec<Word> {
|
||||
fn relationship(
|
||||
&self,
|
||||
context: &Database,
|
||||
relationship: WordRelationship,
|
||||
) -> Vec<Word> {
|
||||
use schema::wordrelation::dsl;
|
||||
match &mut context.conn() {
|
||||
Ok(conn) => {
|
||||
dsl::wordrelation
|
||||
.filter(dsl::wordsource.eq(self.norm.clone()))
|
||||
.filter(dsl::relationship.eq(relationship))
|
||||
.load::<WordRelation>(conn)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.flat_map(|w| {
|
||||
use schema::words::dsl;
|
||||
dsl::words.find(w.wordtarget).first::<Word>(conn)
|
||||
})
|
||||
.collect::<Vec<Word>>()
|
||||
},
|
||||
Ok(conn) => dsl::wordrelation
|
||||
.filter(dsl::wordsource.eq(self.norm.clone()))
|
||||
.filter(dsl::relationship.eq(relationship))
|
||||
.load::<WordRelation>(conn)
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.flat_map(|w| {
|
||||
use schema::words::dsl;
|
||||
dsl::words.find(w.wordtarget).first::<Word>(conn)
|
||||
})
|
||||
.collect::<Vec<Word>>(),
|
||||
Err(e) => {
|
||||
info!("Could not connect to database: {:?}", e);
|
||||
Vec::new()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
#[juniper::graphql_object(Context = Database)]
|
||||
@@ -98,16 +101,18 @@ impl Word {
|
||||
use schema::words::dsl;
|
||||
match self.lemma.clone() {
|
||||
Some(lemma) => match &mut context.conn() {
|
||||
Ok(conn) => match dsl::words.find(lemma.clone()).first::<Word>(conn) {
|
||||
Ok(word) => Some(word),
|
||||
Err(e) => {
|
||||
info!(
|
||||
"Failed to retrieve lemma {} of word {}: {:?}",
|
||||
lemma, self.norm, e
|
||||
);
|
||||
None
|
||||
Ok(conn) => {
|
||||
match dsl::words.find(lemma.clone()).first::<Word>(conn) {
|
||||
Ok(word) => Some(word),
|
||||
Err(e) => {
|
||||
info!(
|
||||
"Failed to retrieve lemma {} of word {}: {:?}",
|
||||
lemma, self.norm, e
|
||||
);
|
||||
None
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
Err(e) => {
|
||||
info!("Could not connect to the database: {:?}", e);
|
||||
None
|
||||
@@ -122,9 +127,7 @@ impl Word {
|
||||
use schema::languages::dsl;
|
||||
match &mut context.conn() {
|
||||
Ok(conn) => {
|
||||
match dsl::languages
|
||||
.find(self.language.clone())
|
||||
.first::<Language>(conn)
|
||||
match dsl::languages.find(self.language).first::<Language>(conn)
|
||||
{
|
||||
Ok(lang) => lang,
|
||||
Err(e) => {
|
||||
@@ -138,7 +141,10 @@ impl Word {
|
||||
}
|
||||
}
|
||||
|
||||
#[graphql(name = "partOfSpeech", description = "Part of speech the word belongs to")]
|
||||
#[graphql(
|
||||
name = "partOfSpeech",
|
||||
description = "Part of speech the word belongs to"
|
||||
)]
|
||||
fn part_of_speech(&self) -> PartOfSpeech {
|
||||
self.partofspeech.clone()
|
||||
}
|
||||
@@ -168,18 +174,25 @@ impl Word {
|
||||
self.lusage.clone()
|
||||
}
|
||||
|
||||
|
||||
#[graphql(description = "Morphology of the word, can be in Markdown format")]
|
||||
#[graphql(
|
||||
description = "Morphology of the word, can be in Markdown format"
|
||||
)]
|
||||
fn morphology(&self) -> Option<String> {
|
||||
self.morphology.clone()
|
||||
}
|
||||
|
||||
#[graphql(name = "related", description = "Words related to the current word")]
|
||||
#[graphql(
|
||||
name = "related",
|
||||
description = "Words related to the current word"
|
||||
)]
|
||||
fn related_words(&self, context: &Database) -> Vec<Word> {
|
||||
self.relationship(context, WordRelationship::Related)
|
||||
}
|
||||
|
||||
#[graphql(name = "definitions", description = "Words that define the current word")]
|
||||
#[graphql(
|
||||
name = "definitions",
|
||||
description = "Words that define the current word"
|
||||
)]
|
||||
fn definitions(&self, context: &Database) -> Vec<Word> {
|
||||
self.relationship(context, WordRelationship::Definition)
|
||||
}
|
||||
|
||||
@@ -29,21 +29,29 @@ diesel::table! {
|
||||
langandagents (id) {
|
||||
id -> Int4,
|
||||
agent -> Varchar,
|
||||
language -> Varchar,
|
||||
language -> Uuid,
|
||||
relationship -> Agentlanguagerelation,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
langtranslatesto (id) {
|
||||
id -> Int4,
|
||||
langfrom -> Uuid,
|
||||
langto -> Uuid,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
use diesel::sql_types::*;
|
||||
use super::sql_types::Release;
|
||||
use super::sql_types::Dictgenre;
|
||||
|
||||
languages (name) {
|
||||
languages (id) {
|
||||
id -> Uuid,
|
||||
name -> Varchar,
|
||||
native -> Nullable<Varchar>,
|
||||
release -> Release,
|
||||
targetlanguage -> Array<Nullable<Text>>,
|
||||
genre -> Array<Nullable<Dictgenre>>,
|
||||
#[sql_name = "abstract"]
|
||||
abstract_ -> Nullable<Text>,
|
||||
@@ -90,7 +98,7 @@ diesel::table! {
|
||||
norm -> Varchar,
|
||||
native -> Nullable<Varchar>,
|
||||
lemma -> Nullable<Varchar>,
|
||||
language -> Varchar,
|
||||
language -> Uuid,
|
||||
partofspeech -> Partofspeech,
|
||||
audio -> Nullable<Varchar>,
|
||||
video -> Nullable<Varchar>,
|
||||
@@ -109,6 +117,7 @@ diesel::joinable!(words -> languages (language));
|
||||
|
||||
diesel::allow_tables_to_appear_in_same_query!(
|
||||
langandagents,
|
||||
langtranslatesto,
|
||||
languages,
|
||||
userfollows,
|
||||
users,
|
||||
|
||||
Reference in New Issue
Block a user