unobtanium 3.0.0

Opinioated Web search engine library with crawler and viewer companion.
Documentation
use criterium::CriteriumChain;
use criterium::rusqlite::AssembleRusqliteQuery;
use log::trace;
use mediatype::MediaTypeBuf;
use uuid::Uuid;

use crate::criterium::EntityCriterium;
use crate::database::fields::EntityGenerationField;
use crate::database::fields::FileSummaryField;
use crate::database::id::EntityGenerationId;
use crate::database::id::MimetypeId;
use crate::database::DatabaseError;
use crate::database::EntityComponentTable;
use crate::database::error::SmuggleDatabaseErrorExtension;
use crate::database::Page;
use crate::database::summary::structs::SummaryDatabase;
use crate::database::summary::structs::SummaryDatabaseTransaction;
use crate::summary::DocumentDescription;
use crate::summary::FileSummary;
use crate::summary::TextPile;
use crate::summary::WithEntityGenerationUuid;

impl SummaryDatabase {
	
	pub fn get_file_summary(
		&self, entity_generation_id: EntityGenerationId
	) -> Result<FileSummary, DatabaseError> {
		trace!("summary_db.get_file_summary()");
		return self.connection().query_row(
			"SELECT
				file_size,
				mimetype_id,
				canonical_url_id
			FROM file_summary
			WHERE entity_generation_id = ?
			",(entity_generation_id,),
			|row| {
				Ok(file_summary_row_to_summary(self, row, entity_generation_id, true))
			}
		)?;
	}
	
	pub fn get_file_summaries(
		&self,
		page: &Page,
		fetch_text_pile: bool,
		criterium_chain: CriteriumChain<EntityCriterium>,
	) -> Result<Vec<WithEntityGenerationUuid<FileSummary>>, DatabaseError> {
		trace!("summary_db.get_file_summaries()");
		let mut query = criterium_chain.assemble_rusqlite_query_for_db(
			&EntityComponentTable::FileSummary
		).inner_join(
			None, EntityGenerationField::EntityGenerationId.into(),
			None, FileSummaryField::EntityGenerationId.into()
		);
		trace!("SQL where: {}", query.sql_where_clause);
		trace!("SQL joins: {}", query.joins_to_sql());
		let mut get_file_summaries_statement = self.connection().prepare(
			format!("
			SELECT
				file_summary.file_size,
				file_summary.mimetype_id,
				file_summary.canonical_url_id,
				file_summary.entity_generation_id,
				entity_generation.entity_generation_uuid
			FROM file_summary
			{}
			WHERE {}
			LIMIT ?
			OFFSET ?",
			query.joins_to_sql(),
			query.sql_where_clause).as_str()
		)?;
		query.where_values.push(page.limit().into());
		query.where_values.push(page.offset().into());

		return get_file_summaries_statement.query_map(
			query.where_values_as_params(),
			|row| {
				let id: EntityGenerationId = row.get(3)?;
				let uuid: Uuid = row.get(4)?;
				Ok(WithEntityGenerationUuid {
					entity_generation_uuid: uuid,
					data: file_summary_row_to_summary(self, row, id, fetch_text_pile)
						.smuggle_through_rusqlite()?,
				})
			}
		)?.map(|r| r.map_err(Into::into)).collect();
	}
}

impl SummaryDatabaseTransaction<'_> {
	pub fn store_file_summary_bulk(
		&mut self,
		file_summaries: Vec<WithEntityGenerationUuid<FileSummary>>,
	) -> Result<(), DatabaseError> {

		self.base_transaction.assert_writable("store_file_summary_bulk")?;
		
		trace!("summary_db_transaction.store_file_summary_bulk()");
		let mut mimetype_ids = Vec::with_capacity(file_summaries.len());
		for file_summary in &file_summaries {
			if let Some(ref mimetype) = file_summary.data.mime_type {
				mimetype_ids.push(Some(self.base_transaction.get_mimetype_id(mimetype, true)?));
			} else {
				mimetype_ids.push(None);
			}

		}
		{
			let mut store_file_summary_statement = self.connection().prepare_cached(
				"INSERT INTO file_summary (
					entity_generation_id,
					file_size,
					mimetype_id
				) VALUES (
					?,?,?
				)"
			)?;
			for (n,file_summary) in file_summaries.iter().enumerate() {
				let mut mimetype_id: Option<MimetypeId> = None;
				if let Some(id_opt) = mimetype_ids.get(n) {
					mimetype_id = *id_opt;
				}
				let entity_generation_id = self.get_entity_generation_id(file_summary.entity_generation_uuid)?;
				store_file_summary_statement.execute(
					(
						entity_generation_id,
						file_summary.data.size,
						mimetype_id,
					)
				)?;
			}
		}
		for file_summary in file_summaries {
			self.store_document_description(
				file_summary.entity_generation_uuid,
				file_summary.data.description
			)?;
			if let Some(text_pile) = file_summary.data.text {
				self.store_text_pile(
					file_summary.entity_generation_uuid,
					text_pile
				)?;
			}
		}
		return Ok(());
	}

}


fn file_summary_row_to_summary(
	db: &SummaryDatabase,
	row: &rusqlite::Row,
	entity_generation_id: EntityGenerationId,
	fetch_text_pile: bool,
) -> Result<FileSummary, DatabaseError> {

    // 0: size
    // 1: mimetype_id
    // 2: canonical_url_id
	
	// Assemble Mimetype
	//println!("Assembling Mimtype …");
	let mut mimetype: Option<MediaTypeBuf> = None;
	if let Some(m_id) = row.get::<usize,Option<MimetypeId>>(1)? {
		mimetype = Some(db.base().get_mimetype_by_id(m_id)?);
	}
	// Fetch text and description
	//println!("Fetching text …");
	let text: Option<TextPile> = if fetch_text_pile {
		db.get_text_pile(entity_generation_id).ok()
	} else {
		None
	};
	//println!("Fetching description …");
	let description: DocumentDescription = db.get_document_description(entity_generation_id)?;
	//println!("Assembling File Summary …");
	// Assemble FileSummary
	Ok(FileSummary{
		mime_type: mimetype,
		size: row.get(0)?,
		canonical_url: row.get(2)?,
		text: text,
		link_stats: None,
		description: description,
	})
}