@@ -176,6 +176,7 @@ def columns_in_output(self):
176
176
- `query_objects`,
177
177
- `query_region`,
178
178
- `query_catalog`,
179
+ - `query_hierarchy`,
179
180
- `query_bibobj`,
180
181
- `query_criteria`.
181
182
@@ -359,6 +360,7 @@ def add_votable_fields(self, *args):
359
360
- `query_objects`,
360
361
- `query_region`,
361
362
- `query_catalog`,
363
+ - `query_hierarchy`,
362
364
- `query_bibobj`,
363
365
- `query_criteria`.
364
366
@@ -487,6 +489,7 @@ def reset_votable_fields(self):
487
489
- `query_objects`,
488
490
- `query_region`,
489
491
- `query_catalog`,
492
+ - `query_hierarchy`,
490
493
- `query_bibobj`,
491
494
- `query_criteria`.
492
495
@@ -855,6 +858,86 @@ def query_catalog(self, catalog, *, criteria=None, get_query_payload=False,
855
858
return self ._query (top , columns , joins , instance_criteria ,
856
859
get_query_payload = get_query_payload )
857
860
861
+ def query_hierarchy (self , name , hierarchy , * ,
862
+ detailed_hierarchy = False ,
863
+ criteria = None , get_query_payload = False ):
864
+ """Query either the parents or the children of the object.
865
+
866
+ Parameters
867
+ ----------
868
+ name : str
869
+ name of the object
870
+ hierarchy : str
871
+ Can take the values "parents" to return the parents of the object (ex: a
872
+ galaxy cluster is a parent of a galaxy), the value "children" to return
873
+ the children of an object (ex: stars can be children of a globular cluster),
874
+ or the value "siblings" to return the object that share a parent with the
875
+ given one (ex: the stars of an open cluster are all siblings).
876
+ detailed_hierarchy : bool
877
+ Whether to add the two extra columns 'hierarchy_bibcode' that gives the
878
+ article in which the hierarchy link is mentioned, and
879
+ 'membership_certainty'. membership_certainty is an integer that reflects the
880
+ certainty of the hierarchy link according to the authors. Ranges between 0
881
+ and 100 where 100 means that the authors were certain of the classification.
882
+ Defaults to False.
883
+ criteria : str
884
+ Criteria to be applied to the query. These should be written in the ADQL
885
+ syntax in a single string. See example.
886
+ get_query_payload : bool, optional
887
+ When set to `True` the method returns the HTTP request parameters without
888
+ querying SIMBAD. The ADQL string is in the 'QUERY' key of the payload.
889
+ Defaults to `False`.
890
+
891
+ Returns
892
+ -------
893
+ table : `~astropy.table.Table`
894
+ Query results table
895
+
896
+ Examples
897
+ --------
898
+ >>> from astroquery.simbad import Simbad
899
+ >>> parent = Simbad.query_hierarchy("2MASS J18511048-0615470",
900
+ ... hierarchy="parents") # doctest: +REMOTE_DATA
901
+ >>> parent[["main_id", "ra", "dec"]] # doctest: +REMOTE_DATA
902
+ <Table length=1>
903
+ main_id ra dec
904
+ deg deg
905
+ object float64 float64
906
+ --------- ------- -------
907
+ NGC 6705 282.766 -6.272
908
+ """
909
+ top , columns , joins , instance_criteria = self ._get_query_parameters ()
910
+
911
+ sub_query = ("(SELECT oidref FROM ident "
912
+ f"WHERE id = '{ name } ') AS name" )
913
+
914
+ if detailed_hierarchy :
915
+ columns .append (_Column ("h_link" , "link_bibcode" , "hierarchy_bibcode" ))
916
+ columns .append (_Column ("h_link" , "membership" , "membership_certainty" ))
917
+
918
+ if hierarchy == "parents" :
919
+ joins += [_Join ("h_link" , _Column ("basic" , "oid" ), _Column ("h_link" , "parent" ))]
920
+ instance_criteria .append ("h_link.child = name.oidref" )
921
+ elif hierarchy == "children" :
922
+ joins += [_Join ("h_link" , _Column ("basic" , "oid" ), _Column ("h_link" , "child" ))]
923
+ instance_criteria .append ("h_link.parent = name.oidref" )
924
+ elif hierarchy == "siblings" :
925
+ sub_query = ("(SELECT DISTINCT basic.oid FROM "
926
+ f"{ sub_query } , basic JOIN h_link ON basic.oid = h_link.parent "
927
+ "WHERE h_link.child = name.oidref) AS parents" )
928
+ joins += [_Join ("h_link" , _Column ("basic" , "oid" ), _Column ("h_link" , "child" ))]
929
+ instance_criteria .append ("h_link.parent = parents.oid" )
930
+ else :
931
+ raise ValueError ("'hierarchy' can only take the values 'parents', "
932
+ f"'siblings', or 'children'. Got '{ hierarchy } '." )
933
+
934
+ if criteria :
935
+ instance_criteria .append (f"({ criteria } )" )
936
+
937
+ return self ._query (top , columns , joins , instance_criteria ,
938
+ from_table = f"{ sub_query } , basic" , distinct = True ,
939
+ get_query_payload = get_query_payload )
940
+
858
941
@deprecated_renamed_argument (["verbose" ], new_name = [None ],
859
942
since = ['0.4.8' ], relax = True )
860
943
def query_bibobj (self , bibcode , * , criteria = None ,
@@ -1369,7 +1452,7 @@ def _get_query_parameters(self):
1369
1452
"""Get the current building blocks of an ADQL query."""
1370
1453
return tuple (map (copy .deepcopy , (self .ROW_LIMIT , self .columns_in_output , self .joins , self .criteria )))
1371
1454
1372
- def _query (self , top , columns , joins , criteria , from_table = "basic" ,
1455
+ def _query (self , top , columns , joins , criteria , from_table = "basic" , distinct = False ,
1373
1456
get_query_payload = False , ** uploads ):
1374
1457
"""Generate an ADQL string from the given query parameters and executes the query.
1375
1458
@@ -1386,6 +1469,8 @@ def _query(self, top, columns, joins, criteria, from_table="basic",
1386
1469
with an AND clause.
1387
1470
from_table : str, optional
1388
1471
The table after 'FROM' in the ADQL string. Defaults to "basic".
1472
+ distinct : bool, optional
1473
+ Whether to add the DISTINCT instruction to the query.
1389
1474
get_query_payload : bool, optional
1390
1475
When set to `True` the method returns the HTTP request parameters without
1391
1476
querying SIMBAD. The ADQL string is in the 'QUERY' key of the payload.
@@ -1400,6 +1485,7 @@ def _query(self, top, columns, joins, criteria, from_table="basic",
1400
1485
`~astropy.table.Table`
1401
1486
The result of the query to SIMBAD.
1402
1487
"""
1488
+ distinct_results = " DISTINCT" if distinct else ""
1403
1489
top_part = f" TOP { top } " if top != - 1 else ""
1404
1490
1405
1491
# columns
@@ -1433,7 +1519,7 @@ def _query(self, top, columns, joins, criteria, from_table="basic",
1433
1519
else :
1434
1520
criteria = ""
1435
1521
1436
- query = f"SELECT{ top_part } { columns } FROM { from_table } { join } { criteria } "
1522
+ query = f"SELECT{ distinct_results } { top_part } { columns } FROM { from_table } { join } { criteria } "
1437
1523
1438
1524
response = self .query_tap (query , get_query_payload = get_query_payload ,
1439
1525
maxrec = self .hardlimit ,
0 commit comments