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 }