Skip to content

Commit

Permalink
vo: use RegTap ivo_string_agg function to query subjects better
Browse files Browse the repository at this point in the history
String aggregate function added to the RegTAP standard makes it
possible to write more efficient and less baroque registry queries.
  • Loading branch information
mbtaylor authored and mmpcn committed Nov 27, 2014
1 parent 70bc748 commit 49cf7db
Showing 1 changed file with 44 additions and 42 deletions.
86 changes: 44 additions & 42 deletions vo/src/main/uk/ac/starlink/vo/RegTapRegistryQuery.java
Expand Up @@ -4,13 +4,11 @@
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.logging.Logger;
import org.xml.sax.SAXException;
import uk.ac.starlink.table.DefaultValueInfo;
Expand All @@ -37,6 +35,13 @@ public class RegTapRegistryQuery implements RegistryQuery {
private static final Logger logger_ =
Logger.getLogger( "uk.ac.starlink.vo" );

/**
* Used for string aggregation of subjects.
* Doesn't have to be human-readable, but it does have to be recognised
* as a word delimiter by ivo_hasword() and should not be something
* that will crop up in an actual subject value. */
private static final String SUBJECT_DELIM = ", ";

/** TAP endpoint for GAVO registry currently hosted at ARI Heidelberg. */
public static final String GAVO_REG = "http://dc.g-vo.org/tap";

Expand All @@ -63,9 +68,6 @@ public class RegTapRegistryQuery implements RegistryQuery {
new DefaultValueInfo( "Registry Query", String.class,
"ADQL text of query made to the registry" );

/** Configuration flag indicating whether res_subject is queried. */
private static final boolean USE_SUBJECT = true;

/**
* Query restriction restricting results to standard interfaces.
* This string is suitable for use in a sequence of ANDed conditions
Expand Down Expand Up @@ -105,8 +107,7 @@ public RegTapRegistryQuery( String tapurl, String standardId,
/* SELECT clause. The columns are required both to support the
* restrictions we need to make and to provide information to
* populate the RegResources generated from the result table. */
List<String> selCols = new ArrayList<String>();
selCols.addAll( Arrays.asList( new String[] {
String[] selCols = new String[] {
"ivoid",
"short_name",
"res_title",
Expand All @@ -120,32 +121,36 @@ public RegTapRegistryQuery( String tapurl, String standardId,
"cap_type",
"cap_description",
"std_version",
} ) );
if ( USE_SUBJECT ) {
selCols.add( "res_subject" );
}
"res_subjects",
};
StringBuffer abuf = new StringBuffer()
.append( "SELECT" );
for ( Iterator<String> it = selCols.iterator(); it.hasNext(); ) {
abuf.append( " " )
.append( it.next() );
if ( it.hasNext() ) {
abuf.append( "," );
}
}
for ( int i = 0; i < selCols.length; i++ ) {
abuf.append( i > 0 ? ", " : " " )
.append( selCols[ i ] );
}

/* FROM clause. Join all the tables we're going to need.
* LEFT OUTER joins are required in some cases where the
* relevant fields might be absent. */
/* FROM clause. Join all the tables we're going to need. */
abuf.append( " FROM rr.resource AS res" );
if ( standardId != null && standardId.trim().length() > 0 ) {
abuf.append( " NATURAL JOIN rr.interface" )
.append( " NATURAL JOIN rr.capability" );
}
abuf.append( " NATURAL LEFT OUTER JOIN rr.res_role" );
if ( USE_SUBJECT ) {
abuf.append( " NATURAL LEFT OUTER JOIN rr.res_subject" );
}

/* Strictly speaking, these two joins do not need to be LEFT OUTER,
* since VOResource requires at least one curation/contact and
* at least one content/subject. However, we put them in to pick
* up resources which may be invalid in this respect. */
abuf.append( " NATURAL LEFT OUTER JOIN rr.res_role" )
.append( " NATURAL LEFT OUTER JOIN" )
.append( " (SELECT" )
.append( " ivoid, " )
.append( " ivo_string_agg(res_subject, " )
.append( adqlCharLiteral( SUBJECT_DELIM ) )
.append( ")" )
.append( " AS res_subjects" )
.append( " FROM rr.res_subject" )
.append( " GROUP BY ivoid) AS sbj" );

/* WHERE clause. Restrict by standard_id if required,
* and throw out any roles we are not interested in. */
Expand Down Expand Up @@ -272,14 +277,9 @@ else if ( field == ResourceField.PUBLISHER ) {
.toString();
}
else if ( field == ResourceField.SUBJECTS ) {
assert USE_SUBJECT;
return new StringBuffer()
.append( "EXISTS (" )
.append( "SELECT 1 FROM rr.res_subject AS sub2" )
.append( " WHERE sub2.ivoid=res.ivoid" )
.append( " AND 1=ivo_nocasematch(sub2.res_subject, " )
.append( adqlCharLiteral( "%" + keyword + "%" ) )
.append( ")" )
.append( "1=ivo_hasword(res_subjects, " )
.append( adqlCharLiteral( keyword ) )
.append( ")" )
.toString();
}
Expand Down Expand Up @@ -346,7 +346,7 @@ private static class RegTapResource implements RegResource {
String contactName_;
String contactEmail_;
String publisherName_;
Collection<String> subjectSet_;
String[] subjects_;
Map<Integer,RegCapabilityInterface> capMap_;

/**
Expand All @@ -356,14 +356,15 @@ private static class RegTapResource implements RegResource {
* @param shortName resource short name
* @param title resource title
* @param refUrl resource reference URL
* @param subjects array of subject strings
*/
RegTapResource( String ivoid, String shortName, String title,
String refUrl ) {
String refUrl, String[] subjects ) {
ivoid_ = ivoid;
shortName_ = shortName;
title_ = title;
refUrl_ = refUrl;
subjectSet_ = new TreeSet<String>();
subjects_ = subjects;
capMap_ = new LinkedHashMap<Integer,RegCapabilityInterface>();
}

Expand Down Expand Up @@ -407,7 +408,7 @@ public String getPublisher() {
}

public String[] getSubjects() {
return subjectSet_.toArray( new String[ 0 ] );
return subjects_;
}

public RegCapabilityInterface[] getCapabilities() {
Expand Down Expand Up @@ -477,13 +478,17 @@ public void acceptRow( Object[] row ) {
final String capType = getString( row, "cap_type" );
final String capDescription = getString( row, "cap_description" );
final String stdVersion = getString( row, "std_version" );
final String subject = getString( row, "res_subject" );
final String subjectTxt = getString( row, "res_subjects" );

/* Update this object's data structures in accordance with the
* information received from this row. */
if ( ! resMap_.containsKey( ivoid ) ) {
resMap_.put( ivoid, new RegTapResource( ivoid, shortName,
title, refUrl ) );
String[] subjects = subjectTxt == null
? new String[ 0 ]
: subjectTxt.split( SUBJECT_DELIM );
resMap_.put( ivoid,
new RegTapResource( ivoid, shortName, title,
refUrl, subjects ) );
}
RegTapResource resource = resMap_.get( ivoid );
if ( "contact".equals( baseRole ) ) {
Expand All @@ -493,9 +498,6 @@ public void acceptRow( Object[] row ) {
else if ( "publisher".equals( baseRole ) ) {
resource.publisherName_ = roleName;
}
if ( subject != null ) {
resource.subjectSet_.add( subject );
}
if ( intfIndex != null ) {
Integer ix = new Integer( intfIndex.intValue() );
if ( ! resource.capMap_.containsKey( ix ) ) {
Expand Down

0 comments on commit 49cf7db

Please sign in to comment.