17#ifdef PACS_WITH_DATABASE_SYSTEM
31query_result_stream::query_result_stream(std::unique_ptr<database_cursor> cursor,
33 const core::dicom_dataset& query_keys,
35 : cursor_(std::
move(cursor)),
37 query_keys_(query_keys),
44auto query_result_stream::extract_patient_query(
const core::dicom_dataset& keys)
45 -> storage::patient_query {
46 storage::patient_query
query;
48 auto patient_id = keys.get_string(core::tags::patient_id);
50 query.patient_id = std::string(patient_id);
53 auto patient_name = keys.get_string(core::tags::patient_name);
55 query.patient_name = std::string(patient_name);
58 auto birth_date = keys.get_string(core::tags::patient_birth_date);
59 if (!birth_date.empty()) {
61 if (birth_date.find(
'-') != std::string::npos) {
62 auto pos = birth_date.find(
'-');
64 query.birth_date_from = std::string(birth_date.substr(0, pos));
66 if (pos + 1 < birth_date.length()) {
67 query.birth_date_to = std::string(birth_date.substr(pos + 1));
70 query.birth_date = std::string(birth_date);
74 auto sex = keys.get_string(core::tags::patient_sex);
76 query.sex = std::string(sex);
82auto query_result_stream::extract_study_query(
const core::dicom_dataset& keys)
83 -> storage::study_query {
84 storage::study_query
query;
86 auto patient_id = keys.get_string(core::tags::patient_id);
88 query.patient_id = std::string(patient_id);
91 auto patient_name = keys.get_string(core::tags::patient_name);
93 query.patient_name = std::string(patient_name);
96 auto study_uid = keys.get_string(core::tags::study_instance_uid);
97 if (!study_uid.empty()) {
98 query.study_uid = std::string(study_uid);
101 auto study_id = keys.get_string(core::tags::study_id);
103 query.study_id = std::string(study_id);
106 auto study_date = keys.get_string(core::tags::study_date);
109 if (
study_date.find(
'-') != std::string::npos) {
118 query.study_date = std::string(study_date);
122 auto accession = keys.get_string(core::tags::accession_number);
123 if (!accession.empty()) {
124 query.accession_number = std::string(accession);
127 auto modality = keys.get_string(core::tags::modality);
129 query.modality = std::string(modality);
132 auto referring = keys.get_string(core::tags::referring_physician_name);
133 if (!referring.empty()) {
134 query.referring_physician = std::string(referring);
137 auto description = keys.get_string(core::tags::study_description);
138 if (!description.empty()) {
139 query.study_description = std::string(description);
145auto query_result_stream::extract_series_query(
const core::dicom_dataset& keys)
146 -> storage::series_query {
147 storage::series_query
query;
149 auto study_uid = keys.get_string(core::tags::study_instance_uid);
150 if (!study_uid.empty()) {
151 query.study_uid = std::string(study_uid);
154 auto series_uid = keys.get_string(core::tags::series_instance_uid);
155 if (!series_uid.empty()) {
156 query.series_uid = std::string(series_uid);
159 auto modality = keys.get_string(core::tags::modality);
161 query.modality = std::string(modality);
164 auto series_number_str = keys.get_string(core::tags::series_number);
165 if (!series_number_str.empty()) {
167 query.series_number = std::stoi(std::string(series_number_str));
173 auto description = keys.get_string(core::tags::series_description);
174 if (!description.empty()) {
175 query.series_description = std::string(description);
181auto query_result_stream::extract_instance_query(
const core::dicom_dataset& keys)
182 -> storage::instance_query {
183 storage::instance_query
query;
185 auto series_uid = keys.get_string(core::tags::series_instance_uid);
186 if (!series_uid.empty()) {
187 query.series_uid = std::string(series_uid);
190 auto sop_uid = keys.get_string(core::tags::sop_instance_uid);
191 if (!sop_uid.empty()) {
192 query.sop_uid = std::string(sop_uid);
195 auto sop_class = keys.get_string(core::tags::sop_class_uid);
196 if (!sop_class.empty()) {
197 query.sop_class_uid = std::string(sop_class);
200 auto instance_number_str = keys.get_string(core::tags::instance_number);
201 if (!instance_number_str.empty()) {
203 query.instance_number = std::stoi(std::string(instance_number_str));
216auto query_result_stream::create(storage::index_database* db, query_level level,
217 const core::dicom_dataset& query_keys,
218 const stream_config& config)
219 -> Result<std::unique_ptr<query_result_stream>> {
220 if (!db || !db->is_open()) {
221 return kcenon::common::error_info(std::string(
"Database is not open"));
225 Result<std::unique_ptr<database_cursor>> cursor_result = kcenon::common::error_info(
226 std::string(
"Unknown query level"));
229 auto db_adapter = db->db_adapter();
231 return kcenon::common::error_info(std::string(
"Invalid database adapter"));
235 case query_level::patient: {
236 auto query = extract_patient_query(query_keys);
237 cursor_result = database_cursor::create_patient_cursor(db_adapter, query);
240 case query_level::study: {
241 auto query = extract_study_query(query_keys);
242 cursor_result = database_cursor::create_study_cursor(db_adapter, query);
245 case query_level::series: {
246 auto query = extract_series_query(query_keys);
247 cursor_result = database_cursor::create_series_cursor(db_adapter, query);
250 case query_level::image: {
251 auto query = extract_instance_query(query_keys);
252 cursor_result = database_cursor::create_instance_cursor(db_adapter, query);
257 if (cursor_result.is_err()) {
258 return kcenon::common::error_info(
259 std::string(
"Failed to create cursor: ") + cursor_result.error().message);
262 return std::unique_ptr<query_result_stream>(
new query_result_stream(
263 std::move(cursor_result.value()), level, query_keys, config));
266auto query_result_stream::from_cursor(storage::index_database* db,
267 const std::string& cursor_state, query_level level,
268 const core::dicom_dataset& query_keys,
269 const stream_config& config)
270 -> Result<std::unique_ptr<query_result_stream>> {
272 std::istringstream iss(cursor_state);
277 if (!(iss >> type_int >> colon >> position) || colon !=
':') {
278 return kcenon::common::error_info(std::string(
"Invalid cursor state format"));
282 auto stream_result =
create(db, level, query_keys, config);
283 if (stream_result.is_err()) {
284 return stream_result;
287 auto stream = std::move(stream_result.value());
290 for (
size_t i = 0; i <
position && stream->has_more(); ++i) {
291 (void)stream->cursor_->fetch_next();
301auto query_result_stream::has_more() const noexcept ->
bool {
302 return cursor_ && cursor_->has_more();
305auto query_result_stream::next_batch()
306 -> std::optional<std::vector<core::dicom_dataset>> {
307 if (!cursor_ || !cursor_->has_more()) {
311 auto records = cursor_->fetch_batch(config_.page_size);
312 if (records.empty()) {
316 std::vector<core::dicom_dataset> datasets;
317 datasets.reserve(records.size());
319 for (
const auto& record : records) {
320 datasets.push_back(record_to_dataset(record));
326auto query_result_stream::total_count() const -> std::optional<
size_t> {
330auto query_result_stream::position() const noexcept ->
size_t {
331 return cursor_ ? cursor_->position() : 0;
334auto query_result_stream::level() const noexcept -> query_level {
338auto query_result_stream::cursor() const -> std::
string {
342 return cursor_->serialize();
349auto query_result_stream::record_to_dataset(
const query_record& record)
const
350 -> core::dicom_dataset {
352 [
this](
const auto& rec) -> core::dicom_dataset {
353 using T = std::decay_t<
decltype(rec)>;
354 if constexpr (std::is_same_v<T, storage::patient_record>) {
355 return patient_to_dataset(rec);
356 }
else if constexpr (std::is_same_v<T, storage::study_record>) {
357 return study_to_dataset(rec);
358 }
else if constexpr (std::is_same_v<T, storage::series_record>) {
359 return series_to_dataset(rec);
360 }
else if constexpr (std::is_same_v<T, storage::instance_record>) {
361 return instance_to_dataset(rec);
367auto query_result_stream::patient_to_dataset(
const storage::patient_record& record)
const
368 -> core::dicom_dataset {
369 using namespace core;
370 using namespace encoding;
375 ds.set_string(tags::query_retrieve_level, vr_type::CS,
"PATIENT");
378 ds.set_string(tags::patient_id, vr_type::LO,
record.patient_id);
379 ds.set_string(tags::patient_name, vr_type::PN,
record.patient_name);
380 ds.set_string(tags::patient_birth_date, vr_type::DA,
record.birth_date);
381 ds.set_string(tags::patient_sex, vr_type::CS,
record.sex);
386auto query_result_stream::study_to_dataset(
const storage::study_record& record)
const
387 -> core::dicom_dataset {
388 using namespace core;
389 using namespace encoding;
394 ds.set_string(tags::query_retrieve_level, vr_type::CS,
"STUDY");
397 ds.set_string(tags::study_instance_uid, vr_type::UI,
record.study_uid);
398 ds.set_string(tags::study_id, vr_type::SH,
record.study_id);
399 ds.set_string(tags::study_date, vr_type::DA,
record.study_date);
400 ds.set_string(tags::study_time, vr_type::TM,
record.study_time);
401 ds.set_string(tags::accession_number, vr_type::SH,
record.accession_number);
402 ds.set_string(tags::referring_physician_name, vr_type::PN,
403 record.referring_physician);
404 ds.set_string(tags::study_description, vr_type::LO,
record.study_description);
405 ds.set_string(tags::modalities_in_study, vr_type::CS,
record.modalities_in_study);
408 ds.set_string(tags::number_of_study_related_series, vr_type::IS,
409 std::to_string(
record.num_series));
410 ds.set_string(tags::number_of_study_related_instances, vr_type::IS,
411 std::to_string(
record.num_instances));
416auto query_result_stream::series_to_dataset(
const storage::series_record& record)
const
417 -> core::dicom_dataset {
418 using namespace core;
419 using namespace encoding;
424 ds.set_string(tags::query_retrieve_level, vr_type::CS,
"SERIES");
427 ds.set_string(tags::series_instance_uid, vr_type::UI,
record.series_uid);
428 ds.set_string(tags::modality, vr_type::CS,
record.modality);
429 ds.set_string(tags::series_description, vr_type::LO,
record.series_description);
431 if (
record.series_number.has_value()) {
432 ds.set_string(tags::series_number, vr_type::IS,
433 std::to_string(
record.series_number.value()));
437 ds.set_string(tags::number_of_series_related_instances, vr_type::IS,
438 std::to_string(
record.num_instances));
443auto query_result_stream::instance_to_dataset(
const storage::instance_record& record)
const
444 -> core::dicom_dataset {
445 using namespace core;
446 using namespace encoding;
451 ds.set_string(tags::query_retrieve_level, vr_type::CS,
"IMAGE");
454 ds.set_string(tags::sop_instance_uid, vr_type::UI,
record.sop_uid);
455 ds.set_string(tags::sop_class_uid, vr_type::UI,
record.sop_class_uid);
457 if (
record.instance_number.has_value()) {
458 ds.set_string(tags::instance_number, vr_type::IS,
459 std::to_string(
record.instance_number.value()));
Compile-time constants for commonly used DICOM tags.
PACS index database for metadata storage and retrieval.
@ move
C-MOVE move request/response.
const atna_coded_value query
Query (110112)
@ record
RECORD - Treatment record dose.
@ position
Sort by ImagePositionPatient/SliceLocation.
Streaming query results with pagination support.