001package org.biopax.paxtools.query; 002 003import org.biopax.paxtools.model.BioPAXElement; 004import org.biopax.paxtools.model.BioPAXLevel; 005import org.biopax.paxtools.model.Model; 006import org.biopax.paxtools.model.level3.*; 007import org.biopax.paxtools.query.algorithm.*; 008import org.biopax.paxtools.query.model.Graph; 009import org.biopax.paxtools.query.model.GraphObject; 010import org.biopax.paxtools.query.model.Node; 011import org.biopax.paxtools.query.wrapperL3.Filter; 012import org.biopax.paxtools.query.wrapperL3.GraphL3; 013import org.biopax.paxtools.query.wrapperL3undirected.GraphL3Undirected; 014 015import java.util.*; 016 017/** 018 * This class provides static methods to execute graph queries. These cover only the most frequent 019 * use cases. Users can use these methods as example for executing the query they need. 020 * 021 * @author Ozgun Babur 022 */ 023public class QueryExecuter 024{ 025 /** 026 * Gets neighborhood of the source set. 027 * 028 * @param sourceSet seed to the query 029 * @param model BioPAX model 030 * @param limit neigborhood distance to get 031 * @param direction UPSTREAM, DOWNSTREAM or BOTHSTREAM 032 * @param filters for filtering graph elements 033 * @return BioPAX elements in the result set 034 */ 035 public static Set<BioPAXElement> runNeighborhood( 036 Set<BioPAXElement> sourceSet, 037 Model model, 038 int limit, 039 Direction direction, 040 Filter... filters) 041 { 042 Graph graph; 043 044 if (model.getLevel() == BioPAXLevel.L3) 045 { 046 if (direction == Direction.UNDIRECTED) 047 { 048 graph = new GraphL3Undirected(model, filters); 049 direction = Direction.BOTHSTREAM; 050 } 051 else 052 { 053 graph = new GraphL3(model, filters); 054 } 055 } 056 else return Collections.emptySet(); 057 058 Set<Node> source = prepareSingleNodeSet(sourceSet, graph); 059 060 if (sourceSet.isEmpty()) return Collections.emptySet(); 061 062 NeighborhoodQuery query = new NeighborhoodQuery(source, direction, limit); 063 Set<GraphObject> resultWrappers = query.run(); 064 return convertQueryResult(resultWrappers, graph, true); 065 } 066 067 /** 068 * Gets the graph constructed by the paths between the given seed nodes. Does not get paths 069 * between physical entities that belong the same entity reference. 070 * @param sourceSet Seed to the query 071 * @param model BioPAX model 072 * @param limit Length limit for the paths to be found 073 * @param filters optional filters - for filtering graph elements 074 * @return BioPAX elements in the result 075 */ 076 public static Set<BioPAXElement> runPathsBetween(Set<BioPAXElement> sourceSet, Model model, 077 int limit, Filter... filters) 078 { 079 Graph graph; 080 081 if (model.getLevel() == BioPAXLevel.L3) 082 { 083 graph = new GraphL3(model, filters); 084 } 085 else return Collections.emptySet(); 086 087 Collection<Set<Node>> sourceWrappers = prepareNodeSets(sourceSet, graph); 088 089 if (sourceSet.size() < 2) return Collections.emptySet(); 090 091 PathsBetweenQuery query = new PathsBetweenQuery(sourceWrappers, limit); 092 Set<GraphObject> resultWrappers = query.run(); 093 return convertQueryResult(resultWrappers, graph, true); 094 } 095 096 /** 097 * Gets paths between the seed nodes. 098 * @param sourceSet Seed to the query 099 * @param model BioPAX model 100 * @param limit Length limit for the paths to be found 101 * @param filters for filtering graph elements 102 * @return BioPAX elements in the result 103 * @deprecated Use runPathsBetween instead 104 */ 105 public static Set<BioPAXElement> runGOI( 106 Set<BioPAXElement> sourceSet, 107 Model model, 108 int limit, 109 Filter... filters) 110 { 111 return runPathsFromTo(sourceSet, sourceSet, model, LimitType.NORMAL, limit, filters); 112 } 113 114 /** 115 * Gets paths the graph composed of the paths from a source node, and ends at a target node. 116 * @param sourceSet Seeds for start points of paths 117 * @param targetSet Seeds for end points of paths 118 * @param model BioPAX model 119 * @param limitType either NORMAL or SHORTEST_PLUS_K 120 * @param limit Length limit fothe paths to be found 121 * @param filters for filtering graph elements 122 * @return BioPAX elements in the result 123 */ 124 public static Set<BioPAXElement> runPathsFromTo( 125 Set<BioPAXElement> sourceSet, 126 Set<BioPAXElement> targetSet, 127 Model model, 128 LimitType limitType, 129 int limit, 130 Filter... filters) 131 { 132 Graph graph; 133 134 if (model.getLevel() == BioPAXLevel.L3) 135 { 136 graph = new GraphL3(model, filters); 137 } 138 else return Collections.emptySet(); 139 140 Set<Node> source = prepareSingleNodeSet(sourceSet, graph); 141 Set<Node> target = prepareSingleNodeSet(targetSet, graph); 142 143 PathsFromToQuery query = new PathsFromToQuery(source, target, limitType, limit, true); 144 Set<GraphObject> resultWrappers = query.run(); 145 return convertQueryResult(resultWrappers, graph, true); 146 } 147 148 /** 149 * Gets the elements in the common upstream or downstream of the seed 150 * @param sourceSet Seed to the query 151 * @param model BioPAX model 152 * @param direction UPSTREAM or DOWNSTREAM 153 * @param limit Length limit for the search 154 * @param filters for filtering graph elements 155 * @return BioPAX elements in the result 156 */ 157 public static Set<BioPAXElement> runCommonStream( 158 Set<BioPAXElement> sourceSet, 159 Model model, 160 Direction direction, 161 int limit, 162 Filter... filters) 163 { 164 Graph graph; 165 166 if (model.getLevel() == BioPAXLevel.L3) 167 { 168 graph = new GraphL3(model, filters); 169 } 170 else return Collections.emptySet(); 171 172 Collection<Set<Node>> source = prepareNodeSets(sourceSet, graph); 173 174 if (sourceSet.size() < 2) return Collections.emptySet(); 175 176 CommonStreamQuery query = new CommonStreamQuery(source, direction, limit); 177 178 Set<GraphObject> resultWrappers = query.run(); 179 return convertQueryResult(resultWrappers, graph, false); 180 } 181 182 /** 183 * First finds the common stream, then completes it with the paths between seed and common 184 * stream. 185 * @param sourceSet Seed to the query 186 * @param model BioPAX model 187 * @param direction UPSTREAM or DOWNSTREAM 188 * @param limit Length limit for the search 189 * @param filters for filtering graph elements 190 * @return BioPAX elements in the result 191 */ 192 public static Set<BioPAXElement> runCommonStreamWithPOI( 193 Set<BioPAXElement> sourceSet, 194 Model model, 195 Direction direction, 196 int limit, 197 Filter... filters) 198 { 199 Graph graph; 200 201 if (model.getLevel() == BioPAXLevel.L3) 202 { 203 graph = new GraphL3(model, filters); 204 } 205 else return Collections.emptySet(); 206 207 Collection<Set<Node>> sourceSets = prepareNodeSets(sourceSet, graph); 208 209 if (sourceSet.size() < 2) return Collections.emptySet(); 210 211 // Run a common stream query 212 213 CommonStreamQuery commStream = new CommonStreamQuery(sourceSets, direction, limit); 214 215 Set<GraphObject> resultWrappers = commStream.run(); 216 217 // Stop if they have no common stream. 218 if (resultWrappers.isEmpty()) return Collections.emptySet(); 219 220 // Extract nodes from the result 221 222 Set<Node> target = new HashSet<Node>(); 223 224 for (GraphObject go : resultWrappers) 225 { 226 if (go instanceof Node) target.add((Node) go); 227 } 228 229 // Take union of the sources 230 231 Set<Node> source = new HashSet<Node>(); 232 for (Set<Node> set : sourceSets) 233 { 234 source.addAll(set); 235 } 236 237 // Run a paths-of-interest query between source set and result set 238 239 PathsFromToQuery poi; 240 241 if (direction == Direction.DOWNSTREAM) 242 { 243 poi = new PathsFromToQuery(source, target, LimitType.NORMAL, limit, true); 244 } 245 else 246 { 247 poi = new PathsFromToQuery(target, source, LimitType.NORMAL, limit, true); 248 } 249 250 resultWrappers = poi.run(); 251 return convertQueryResult(resultWrappers, graph, true); 252 } 253 254 /** 255 * Converts the query result from wrappers to wrapped BioPAX elements. 256 * @param resultWrappers Wrappers of the result set 257 * @param graph Queried graph 258 * @param removeDisconnected whether to remove disconnected non-complex type physical entities 259 * @return Set of elements in the result 260 */ 261 private static Set<BioPAXElement> convertQueryResult( 262 Set<GraphObject> resultWrappers, Graph graph, boolean removeDisconnected) 263 { 264 Set<Object> result = graph.getWrappedSet(resultWrappers); 265 266 Set<BioPAXElement> set = new HashSet<BioPAXElement>(); 267 for (Object o : result) 268 { 269 set.add((BioPAXElement) o); 270 } 271 272 // remove disconnected simple physical entities 273 if (removeDisconnected) 274 { 275 Set<BioPAXElement> remove = new HashSet<BioPAXElement>(); 276 277 for (BioPAXElement ele : set) 278 { 279 if (ele instanceof SimplePhysicalEntity && 280 isDisconnected((SimplePhysicalEntity) ele, set)) 281 { 282 remove.add(ele); 283 } 284 } 285 set.removeAll(remove); 286 } 287 288 return set; 289 } 290 291 private static boolean isDisconnected(SimplePhysicalEntity spe, Set<BioPAXElement> resultSet) 292 { 293 for (Interaction inter : spe.getParticipantOf()) 294 { 295 if (resultSet.contains(inter)) return false; 296 } 297 298 for (Complex complex : spe.getComponentOf()) 299 { 300 if (resultSet.contains(complex)) return false; 301 } 302 303 for (PhysicalEntity pe : spe.getMemberPhysicalEntityOf()) 304 { 305 if (resultSet.contains(pe)) return false; 306 } 307 308 for (PhysicalEntity pe : spe.getMemberPhysicalEntity()) 309 { 310 if (resultSet.contains(pe)) return false; 311 } 312 313 return true; 314 } 315 316 /** 317 * Gets the related wrappers of the given elements in a set. 318 * @param elements Elements to get the related wrappers 319 * @param graph Owner graph 320 * @return Related wrappers in a set 321 */ 322 public static Set<Node> prepareSingleNodeSet(Set<BioPAXElement> elements, Graph graph) 323 { 324 Map<BioPAXElement, Set<PhysicalEntity>> map = getRelatedPhysicalEntityMap(elements); 325 326 Set<PhysicalEntity> pes = new HashSet<PhysicalEntity>(); 327 for (Set<PhysicalEntity> valueSet : map.values()) 328 { 329 pes.addAll(valueSet); 330 } 331 332 Set<Node> nodes = graph.getWrapperSet(pes); 333 334 // If there are interactions in the seed add them too 335 336 Set<Node> inters = getSeedInteractions(elements, graph); 337 nodes.addAll(inters); 338 339 return nodes; 340 } 341 342 /** 343 * Gets the related wrappers of the given elements in individual sets. An object can be related 344 * to more than one wrapper and they will appear in the same set. This method created a set for 345 * each parameter element that has a related wrapper. 346 * @param elements Elements to get the related wrappers 347 * @param graph Owner graph 348 * @return Related wrappers in individual sets 349 */ 350 private static Collection<Set<Node>> prepareNodeSets(Set<BioPAXElement> elements, Graph graph) 351 { 352 Collection<Set<Node>> sets = new HashSet<Set<Node>>(); 353 354 Map<BioPAXElement, Set<PhysicalEntity>> map = getRelatedPhysicalEntityMap(elements); 355 356 for (Set<PhysicalEntity> pes : map.values()) 357 { 358 Set<Node> set = graph.getWrapperSet(pes); 359 360 if (!set.isEmpty()) sets.add(set); 361 } 362 363 // Add interactions in the seed as single node set 364 365 Set<Node> inters = getSeedInteractions(elements, graph); 366 for (Node node : inters) 367 { 368 sets.add(Collections.singleton(node)); 369 } 370 371 return sets; 372 } 373 374 /** 375 * Maps each BioPAXElement to its related PhysicalEntity objects. 376 * 377 * @param elements Elements to map 378 * @return The mapping 379 */ 380 public static Map<BioPAXElement, Set<PhysicalEntity>> getRelatedPhysicalEntityMap( 381 Collection<BioPAXElement> elements) 382 { 383 replaceXrefsWithRelatedER(elements); 384 Map<BioPAXElement, Set<PhysicalEntity>> map = new HashMap<BioPAXElement, Set<PhysicalEntity>>(); 385 386 for (BioPAXElement ele : elements) 387 { 388 Set<PhysicalEntity> ents = getRelatedPhysicalEntities(ele, null); 389 390 if (!ents.isEmpty()) 391 { 392 map.put(ele, ents); 393 } 394 } 395 return map; 396 } 397 398 /** 399 * Replaces Xref objects with the related EntityReference objects. This is required for the use 400 * case when user provides multiple xrefs that point to the same ER. 401 * @param elements elements to send to a query as source or target 402 */ 403 protected static void replaceXrefsWithRelatedER( 404 Collection<BioPAXElement> elements) 405 { 406 Set<EntityReference> ers = new HashSet<EntityReference>(); 407 Set<Xref> xrefs = new HashSet<Xref>(); 408 for (BioPAXElement element : elements) 409 { 410 if (element instanceof Xref) 411 { 412 xrefs.add((Xref) element); 413 for (XReferrable able : ((Xref) element).getXrefOf()) 414 { 415 if (able instanceof EntityReference) 416 { 417 ers.add((EntityReference) able); 418 } 419 } 420 } 421 } 422 423 elements.removeAll(xrefs); 424 for (EntityReference er : ers) 425 { 426 if (!elements.contains(er)) elements.add(er); 427 } 428 } 429 430 /** 431 * Gets the related PhysicalEntity objects of the given BioPAXElement, in level 3 models. 432 * 433 * @param element Element to get related PhysicalEntity objects 434 * @param pes Result set. If not supplied, a new set will be initialized. 435 * @return Related PhysicalEntity objects 436 */ 437 public static Set<PhysicalEntity> getRelatedPhysicalEntities(BioPAXElement element, 438 Set<PhysicalEntity> pes) 439 { 440 if (pes == null) pes = new HashSet<PhysicalEntity>(); 441 442 if (element instanceof Complex) 443 { 444 Complex cpx = (Complex) element; 445 446 if (!pes.contains(cpx)) 447 { 448 pes.add(cpx); 449 for (Complex parent : cpx.getComponentOf()) 450 { 451 getRelatedPhysicalEntities(parent, pes); 452 } 453 454 // This is a hack for BioPAX graph. Equivalence relations do not link members and 455 // complexes because members cannot be addressed. Below call makes sure that if the 456 // source node has a generic parents or children and they appear in a complex, we 457 // include the complex in the sources. 458 addEquivalentsComplexes(cpx, pes); 459 } 460 } 461 else if (element instanceof PhysicalEntity) 462 { 463 PhysicalEntity pe = (PhysicalEntity) element; 464 if (!pes.contains(pe)) 465 { 466 pes.add(pe); 467 468 for (Complex cmp : pe.getComponentOf()) 469 { 470 getRelatedPhysicalEntities(cmp, pes); 471 } 472 473 // This is a hack for BioPAX graph. Equivalence relations do not link members and 474 // complexes because members cannot be addressed. Below call makes sure that if the 475 // source node has a generic parents or children and they appear in a complex, we 476 // include the complex in the sources. 477 addEquivalentsComplexes(pe, pes); 478 } 479 } 480 else if (element instanceof Xref) 481 { 482 for (XReferrable xrable : ((Xref) element).getXrefOf()) 483 { 484 getRelatedPhysicalEntities(xrable, pes); 485 } 486 } 487 else if (element instanceof EntityReference) 488 { 489 EntityReference er = (EntityReference) element; 490 491 for (SimplePhysicalEntity spe : er.getEntityReferenceOf()) 492 { 493 getRelatedPhysicalEntities(spe, pes); 494 } 495 496 for (EntityReference parentER : er.getMemberEntityReferenceOf()) 497 { 498 getRelatedPhysicalEntities(parentER, pes); 499 } 500 } 501 502 return pes; 503 } 504 505 /** 506 * Adds equivalents and parent complexes of the given PhysicalEntity to the parameter set. 507 * @param pe The PhysicalEntity to add its equivalents and complexes 508 * @param pes Set to collect equivalents and complexes 509 */ 510 private static void addEquivalentsComplexes(PhysicalEntity pe, Set<PhysicalEntity> pes) 511 { 512 addEquivalentsComplexes(pe, true, pes); 513 addEquivalentsComplexes(pe, false, pes); 514 } 515 516 /** 517 * Adds equivalents and parent complexes of the given PhysicalEntity to the parameter set. This 518 * method traverses homologies only to one direction (either towards parents or to the 519 * children). 520 * @param pe The PhysicalEntity to add its equivalents and complexes 521 * @param outer Give true if towards parents, false if to the children 522 * @param pes Set to collect equivalents and complexes 523 */ 524 private static void addEquivalentsComplexes(PhysicalEntity pe, boolean outer, 525 Set<PhysicalEntity> pes) 526 { 527 Set<PhysicalEntity> set = outer ? 528 pe.getMemberPhysicalEntityOf() : pe.getMemberPhysicalEntity(); 529 530 for (PhysicalEntity related : set) 531 { 532 for (Complex cmp : related.getComponentOf()) 533 { 534 getRelatedPhysicalEntities(cmp, pes); 535 } 536 537 addEquivalentsComplexes(related, outer, pes); 538 } 539 } 540 541 /** 542 * Extracts the querible interactions from the elements. 543 * 544 * @param elements BioPAX elements to search 545 * @param graph graph model 546 * @return Querible Interactions (nodes) 547 */ 548 public static Set<Node> getSeedInteractions(Collection<BioPAXElement> elements, Graph graph) 549 { 550 Set<Node> nodes = new HashSet<Node>(); 551 552 for (BioPAXElement ele : elements) 553 { 554 if (ele instanceof Conversion || ele instanceof TemplateReaction || 555 ele instanceof Control) 556 { 557 GraphObject go = graph.getGraphObject(ele); 558 559 if (go instanceof Node) 560 { 561 nodes.add((Node) go); 562 } 563 } 564 } 565 return nodes; 566 } 567}