Mercurial > hg > orthanc
comparison OrthancServer/Search/LookupResource.cpp @ 1750:55d52567bebb db-changes
LookupResource implemented
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 27 Oct 2015 12:45:50 +0100 |
parents | 99f4a05f39fa |
children | fb569ee09a69 |
comparison
equal
deleted
inserted
replaced
1749:99f4a05f39fa | 1750:55d52567bebb |
---|---|
32 | 32 |
33 #include "../PrecompiledHeadersServer.h" | 33 #include "../PrecompiledHeadersServer.h" |
34 #include "LookupResource.h" | 34 #include "LookupResource.h" |
35 | 35 |
36 #include "../../Core/OrthancException.h" | 36 #include "../../Core/OrthancException.h" |
37 #include "../../Core/FileStorage/StorageAccessor.h" | |
38 #include "../ServerToolbox.h" | |
39 | |
37 | 40 |
38 namespace Orthanc | 41 namespace Orthanc |
39 { | 42 { |
40 LookupResource::Level::Level(ResourceType level) | 43 LookupResource::Level::Level(ResourceType level) : level_(level) |
41 { | 44 { |
42 const DicomTag* tags = NULL; | 45 const DicomTag* tags = NULL; |
43 size_t size; | 46 size_t size; |
44 | 47 |
45 LookupIdentifierQuery::LoadIdentifiers(tags, size, level); | 48 LookupIdentifierQuery::LoadIdentifiers(tags, size, level); |
77 | 80 |
78 bool LookupResource::Level::Add(std::auto_ptr<IFindConstraint>& constraint) | 81 bool LookupResource::Level::Add(std::auto_ptr<IFindConstraint>& constraint) |
79 { | 82 { |
80 if (identifiers_.find(constraint->GetTag()) != identifiers_.end()) | 83 if (identifiers_.find(constraint->GetTag()) != identifiers_.end()) |
81 { | 84 { |
82 identifiersConstraints_.push_back(constraint.release()); | 85 if (level_ == ResourceType_Patient) |
86 { | |
87 // The filters on the patient level must be cloned to the study level | |
88 identifiersConstraints_.push_back(constraint->Clone()); | |
89 } | |
90 else | |
91 { | |
92 identifiersConstraints_.push_back(constraint.release()); | |
93 } | |
94 | |
83 return true; | 95 return true; |
84 } | 96 } |
85 else if (mainTags_.find(constraint->GetTag()) != mainTags_.end()) | 97 else if (mainTags_.find(constraint->GetTag()) != mainTags_.end()) |
86 { | 98 { |
87 mainTagsConstraints_.push_back(constraint.release()); | 99 if (level_ == ResourceType_Patient) |
100 { | |
101 // The filters on the patient level must be cloned to the study level | |
102 mainTagsConstraints_.push_back(constraint->Clone()); | |
103 } | |
104 else | |
105 { | |
106 mainTagsConstraints_.push_back(constraint.release()); | |
107 } | |
108 | |
88 return true; | 109 return true; |
89 } | 110 } |
90 else | 111 else |
91 { | 112 { |
92 return false; | 113 return false; |
165 unoptimizedConstraints_.push_back(c.release()); | 186 unoptimizedConstraints_.push_back(c.release()); |
166 } | 187 } |
167 } | 188 } |
168 | 189 |
169 | 190 |
170 static int64_t ChooseOneInstance(IDatabaseWrapper& database, | 191 static bool Match(const DicomMap& tags, |
171 int64_t parent, | 192 const IFindConstraint& constraint) |
172 ResourceType type) | 193 { |
173 { | 194 const DicomValue* value = tags.TestAndGetValue(constraint.GetTag()); |
174 for (;;) | 195 |
175 { | 196 if (value == NULL || |
176 if (type == ResourceType_Instance) | 197 value->IsNull() || |
177 { | 198 value->IsBinary()) |
178 return parent; | 199 { |
179 } | 200 return false; |
180 | 201 } |
181 std::list<int64_t> children; | 202 else |
182 database.GetChildrenInternalId(children, parent); | 203 { |
183 | 204 return constraint.Match(value->GetContent()); |
184 if (children.empty()) | 205 } |
185 { | 206 } |
207 | |
208 | |
209 void LookupResource::Level::Apply(SetOfResources& candidates, | |
210 IDatabaseWrapper& database) const | |
211 { | |
212 // First, use the indexed identifiers | |
213 LookupIdentifierQuery query(level_); | |
214 | |
215 for (Constraints::const_iterator it = identifiersConstraints_.begin(); | |
216 it != identifiersConstraints_.end(); ++it) | |
217 { | |
218 (*it)->Setup(query); | |
219 } | |
220 | |
221 query.Apply(candidates, database); | |
222 | |
223 // Secondly, filter using the main DICOM tags | |
224 if (!identifiersConstraints_.empty() || | |
225 !mainTagsConstraints_.empty()) | |
226 { | |
227 std::list<int64_t> source; | |
228 candidates.Flatten(source); | |
229 candidates.Clear(); | |
230 | |
231 std::list<int64_t> filtered; | |
232 for (std::list<int64_t>::const_iterator candidate = source.begin(); | |
233 candidate != source.end(); ++candidate) | |
234 { | |
235 DicomMap tags; | |
236 database.GetMainDicomTags(tags, *candidate); | |
237 | |
238 bool match = true; | |
239 | |
240 // Re-apply the identifier constraints, as their "Setup" | |
241 // method is less restrictive than their "Match" method | |
242 for (Constraints::const_iterator it = identifiersConstraints_.begin(); | |
243 match && it != identifiersConstraints_.end(); ++it) | |
244 { | |
245 if (!Match(tags, **it)) | |
246 { | |
247 match = false; | |
248 } | |
249 } | |
250 | |
251 for (Constraints::const_iterator it = mainTagsConstraints_.begin(); | |
252 match && it != mainTagsConstraints_.end(); ++it) | |
253 { | |
254 if (!Match(tags, **it)) | |
255 { | |
256 match = false; | |
257 } | |
258 } | |
259 | |
260 if (match) | |
261 { | |
262 filtered.push_back(*candidate); | |
263 } | |
264 } | |
265 | |
266 candidates.Intersect(filtered); | |
267 } | |
268 } | |
269 | |
270 | |
271 | |
272 void LookupResource::ApplyUnoptimizedConstraints(SetOfResources& candidates, | |
273 IDatabaseWrapper& database, | |
274 IStorageArea& storageArea) const | |
275 { | |
276 if (unoptimizedConstraints_.empty()) | |
277 { | |
278 // Nothing to do | |
279 return; | |
280 } | |
281 | |
282 std::list<int64_t> source; | |
283 candidates.Flatten(source); | |
284 candidates.Clear(); | |
285 | |
286 StorageAccessor accessor(storageArea); | |
287 | |
288 std::list<int64_t> filtered; | |
289 for (std::list<int64_t>::const_iterator candidate = source.begin(); | |
290 candidate != source.end(); ++candidate) | |
291 { | |
292 if (maxResults_ != 0 && | |
293 filtered.size() >= maxResults_) | |
294 { | |
295 // We have enough results | |
296 break; | |
297 } | |
298 | |
299 int64_t instance; | |
300 FileInfo attachment; | |
301 if (!Toolbox::FindOneChildInstance(instance, database, *candidate, level_) || | |
302 !database.LookupAttachment(attachment, instance, FileContentType_DicomAsJson)) | |
303 { | |
304 continue; | |
305 } | |
306 | |
307 Json::Value content; | |
308 accessor.Read(content, attachment); | |
309 | |
310 bool match = true; | |
311 | |
312 for (Constraints::const_iterator it = unoptimizedConstraints_.begin(); | |
313 match && it != unoptimizedConstraints_.end(); ++it) | |
314 { | |
315 std::string tag = (*it)->GetTag().Format(); | |
316 if (content.isMember(tag) && | |
317 content[tag]["Type"] == "String") | |
318 { | |
319 std::string value = content[tag]["Value"].asString(); | |
320 if (!(*it)->Match(value)) | |
321 { | |
322 match = false; | |
323 } | |
324 } | |
325 else | |
326 { | |
327 match = false; | |
328 } | |
329 } | |
330 | |
331 if (match) | |
332 { | |
333 filtered.push_back(*candidate); | |
334 } | |
335 } | |
336 | |
337 candidates.Intersect(filtered); | |
338 } | |
339 | |
340 | |
341 void LookupResource::ApplyLevel(SetOfResources& candidates, | |
342 ResourceType level, | |
343 IDatabaseWrapper& database) const | |
344 { | |
345 Levels::const_iterator it = levels_.find(level); | |
346 if (it != levels_.end()) | |
347 { | |
348 it->second->Apply(candidates, database); | |
349 } | |
350 } | |
351 | |
352 | |
353 void LookupResource::Apply(std::list<int64_t>& result, | |
354 IDatabaseWrapper& database, | |
355 IStorageArea& storageArea) const | |
356 { | |
357 SetOfResources candidates(database, level_); | |
358 | |
359 switch (level_) | |
360 { | |
361 case ResourceType_Patient: | |
362 ApplyLevel(candidates, ResourceType_Patient, database); | |
363 break; | |
364 | |
365 case ResourceType_Study: | |
366 ApplyLevel(candidates, ResourceType_Study, database); | |
367 break; | |
368 | |
369 case ResourceType_Series: | |
370 ApplyLevel(candidates, ResourceType_Study, database); | |
371 candidates.GoDown(); | |
372 ApplyLevel(candidates, ResourceType_Series, database); | |
373 break; | |
374 | |
375 case ResourceType_Instance: | |
376 ApplyLevel(candidates, ResourceType_Study, database); | |
377 candidates.GoDown(); | |
378 ApplyLevel(candidates, ResourceType_Series, database); | |
379 candidates.GoDown(); | |
380 ApplyLevel(candidates, ResourceType_Instance, database); | |
381 break; | |
382 | |
383 default: | |
186 throw OrthancException(ErrorCode_InternalError); | 384 throw OrthancException(ErrorCode_InternalError); |
187 } | 385 } |
188 | 386 |
189 parent = children.front(); | 387 ApplyUnoptimizedConstraints(candidates, database, storageArea); |
190 type = GetChildResourceType(type); | 388 candidates.Flatten(result); |
191 } | 389 } |
192 } | 390 |
193 | 391 |
392 void LookupResource::Apply(std::list<std::string>& result, | |
393 IDatabaseWrapper& database, | |
394 IStorageArea& storageArea) const | |
395 { | |
396 std::list<int64_t> tmp; | |
397 Apply(tmp, database, storageArea); | |
398 | |
399 result.clear(); | |
400 | |
401 for (std::list<int64_t>::const_iterator | |
402 it = tmp.begin(); it != tmp.end(); ++it) | |
403 { | |
404 result.push_back(database.GetPublicId(*it)); | |
405 } | |
406 } | |
194 } | 407 } |