Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make CSW query type and fields configurable #912

Merged
merged 5 commits into from
Jan 27, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 12 additions & 12 deletions service-csw/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
</dependency>
<dependency>
<dependency>
<groupId>org.oskari</groupId>
<artifactId>service-control</artifactId>
</dependency>
<dependency>
<groupId>javax.vecmath</groupId>
<artifactId>vecmath</artifactId>
</dependency>
<artifactId>service-control</artifactId>
</dependency>
<dependency>
<groupId>javax.vecmath</groupId>
<artifactId>vecmath</artifactId>
</dependency>

<dependency>
<groupId>junit</groupId>
Expand All @@ -51,16 +51,16 @@
<artifactId>postgresql</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
</dependency>

<dependency>
<groupId>org.oskari</groupId>
<artifactId>shared-test-resources</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</dependencies>

</project>
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,8 @@
*
* Configurable by properties:
* - Server: service.metadata.url (for example "https://www.paikkatietohakemisto.fi/geonetwork/srv/fin/csw")
* - localized urls for images: search.channel.METADATA_CATALOGUE_CHANNEL.image.url.[lang code] (as contentURL in conjunction with resourceId)
* - localized urls for service: search.channel.METADATA_CATALOGUE_CHANNEL.fetchpage.url.[lang code] (as actionURL in conjunction with resourceId)
* - Query type: search.channel.METADATA_CATALOGUE_CHANNEL.queryType (defaults to "summary")
* - Query fields: search.channel.METADATA_CATALOGUE_CHANNEL.queryFields (comma-separted list like "Title, Abstract" - defaults to "csw:anyText")
* - advanced filter fields (dropdowns) that are available on form based on the service:
* search.channel.METADATA_CATALOGUE_CHANNEL.fields (Note! Uses GetDomain Operation on CSW to populate values for fields)
* - per field processing definitions (each property prefixed with "search.channel.METADATA_CATALOGUE_CHANNEL.field.[field name]."):
Expand All @@ -61,32 +61,22 @@ public class MetadataCatalogueChannelSearchService extends SearchChannel {

public static final String ID = "METADATA_CATALOGUE_CHANNEL";
private static String serverURL = PropertyUtil.get(PROP_SERVICE_URL);
private String queryType;
private String[] queryFields;

private final static List<MetadataField> fields = new ArrayList<>();

private MetadataCatalogueResultParser RESULT_PARSER = null;
private final MetadataCatalogueQueryHelper QUERY_HELPER = new MetadataCatalogueQueryHelper();

private OskariLayerService mapLayerService = OskariComponentManager.getComponentOfType(OskariLayerService.class);
private static final String PROPERTY_RESULTPARSER = "search.channel.METADATA_CATALOGUE_CHANNEL.resultparser";

@Override
public void init() {
super.init();
// hook for customized parsing
// TODO: this was only used for ELF to inject rating for metadata. We can probably remove it now
final String customResultParser = PropertyUtil.getOptional(PROPERTY_RESULTPARSER);
if (customResultParser != null) {
try {
final Class clazz = Class.forName(customResultParser);
RESULT_PARSER = (MetadataCatalogueResultParser) clazz.newInstance();
} catch (Exception e) {
log.error(e, "Error instantiating custom metadata result parser:", customResultParser);
}
}
if (RESULT_PARSER == null) {
RESULT_PARSER = new MetadataCatalogueResultParser();
}
queryType = getProperty("queryType", "summary");
queryFields = getProperty("queryFields", "csw:anyText").split("\\s*,\\s*");
RESULT_PARSER = new MetadataCatalogueResultParser();
}

/**
Expand Down Expand Up @@ -176,19 +166,10 @@ public ChannelSearchResult parseResults(Element root, SearchCriteria searchCrite
getResults(root).forEach(metadata -> {
try {
final SearchResultItem item = RESULT_PARSER.parseResult(metadata);
/*
// done in frontend now?
final List<OskariLayer> oskariLayers = getOskariLayerWithUuid(item);
for(OskariLayer oskariLayer : oskariLayers){
log.debug("METAID: " + oskariLayer.getMetadataId());
item.addUuId(oskariLayer.getMetadataId());
}
*/

item.addValue("geom", getWKT(item, WKTHelper.PROJ_EPSG_4326, srs));
channelSearchResult.addItem(item);
} catch (Exception e) {
log.warn(e);
log.info("Error parsing metadata search result item", e);
}
});

Expand Down Expand Up @@ -238,7 +219,7 @@ private String getWKT(final SearchResultItem item, final String sourceSRS, final

private Element makeQuery(SearchCriteria searchCriteria) throws Exception {
final long start = System.currentTimeMillis();
final String payload = QUERY_HELPER.getQueryPayload(searchCriteria);
final String payload = QUERY_HELPER.getQueryPayload(searchCriteria, queryType, queryFields);
if (payload == null) {
// no point in making the query without payload
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,8 @@
import java.util.List;
import java.util.Map;

import static org.oskari.csw.request.GetRecords.DEFAULT_QUERY_TYPE;

/**
* Helper class for creating search queries for MetadataCatalogue
*/
Expand All @@ -35,6 +37,7 @@ public class MetadataCatalogueQueryHelper {
public final static String WILDCARD_CHARACTER = "*";
public final static String SINGLE_WILDCARD_CHARACTER = "?";
public final static String ESCAPE_CHARACTER = "/";
private static final String[] DEFAULT_QUERY_FIELDS = new String[] { "csw:anyText" };

private static final Logger log = LogFactory.getLogger(MetadataCatalogueQueryHelper.class);
private FilterFactory2 filterFactory;
Expand All @@ -45,7 +48,10 @@ public MetadataCatalogueQueryHelper() {
}

public String getQueryPayload(SearchCriteria searchCriteria) {
final List<Filter> filters = getFiltersForQuery(searchCriteria);
return getQueryPayload(searchCriteria, DEFAULT_QUERY_TYPE, DEFAULT_QUERY_FIELDS);
}
public String getQueryPayload(SearchCriteria searchCriteria, String queryType, String... queryFields) {
final List<Filter> filters = getFiltersForQuery(searchCriteria, queryFields);
if (filters.isEmpty()) {
// no point in making the query without GetRecords, but throw exception instead?
//throw new ServiceRuntimeException("Can't create GetRecords request without filters");
Expand All @@ -58,19 +64,27 @@ public String getQueryPayload(SearchCriteria searchCriteria) {
filter = filterFactory.and(filters);
}

return GetRecords.createRequest(filter);
return GetRecords.createRequest(filter, queryType);
}

private List<Filter> getFiltersForQuery(SearchCriteria searchCriteria) {
private List<Filter> getFiltersForQuery(SearchCriteria searchCriteria, String... queryFields) {
final List<Filter> list = new ArrayList<>();
final List<Filter> theOrList = new ArrayList<>();

// user input
Filter userInput = createLikeFilter(searchCriteria.getSearchString(), "csw:anyText");
if (userInput != null) {
list.add(userInput);
if (queryFields == null || queryFields.length == 0) {
queryFields = DEFAULT_QUERY_FIELDS;
}

List<Filter> queryFilters = new ArrayList<>();
for (String field: queryFields) {
queryFilters.add(createLikeFilter(searchCriteria.getSearchString(), field));
}
if (queryFilters.size() == 1) {
list.add(queryFilters.get(0));
} else if (queryFilters.size() > 1) {
list.add(filterFactory.or(queryFilters));
}
// "advanced fields"
for (MetadataField field : MetadataCatalogueChannelSearchService.getFields()) {
final Filter operation = getFilterForField(searchCriteria, field);
if (operation == null) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,6 @@
*/
public class MetadataCatalogueResultParser {

private static final Logger log = LogFactory.getLogger(MetadataCatalogueResultParser.class);

public static final String KEY_IDENTIFICATION = "identification";
public static final String KEY_IDENTIFICATION_DATE = "date";
public static final String KEY_IDENTIFICATION_CODELIST = "code";
Expand All @@ -42,7 +40,6 @@ public SearchResultItem parseResult(final Element elem) throws Exception {
XmlHelper.getFirstChild(elem, "fileIdentifier"),
"CharacterString");
item.setResourceId(uuid);
//item.addUuId(uuid);
// lang
Element languageCode = XmlHelper.getFirstChild(
XmlHelper.getFirstChild(elem, "language"),
Expand Down
22 changes: 17 additions & 5 deletions service-csw/src/main/java/org/oskari/csw/request/GetRecords.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,21 +18,25 @@ public class GetRecords {

private static final String CSW_VERSION = "2.0.2";
private static final String CONSTRAINT_VERSION = "1.0.0";
public static final String DEFAULT_QUERY_TYPE = "summary";

private GetRecords() {}

public static String createRequest(Filter filter) {
return createRequest(filter, DEFAULT_QUERY_TYPE);
}
/**
* Builds a GetRecords request payload for CSW-service with the given filters.
* @param filter required
* @return
*/
public static String createRequest(Filter filter) {
public static String createRequest(Filter filter, String queryType) {
if (filter == null) {
throw new ServiceRuntimeException("Filter is required");
}
final StringWriter writer = new StringWriter();
XMLStreamWriter xsw = getXMLWriter(writer);
startDocument(xsw);
startDocument(xsw, queryType);
writeRawXMLUnsafe(xsw, writer, getFilterAsString(filter));
endDocument(xsw);
return writer.toString();
Expand All @@ -59,7 +63,7 @@ private static XMLStreamWriter getXMLWriter(Writer writer) {
<csw:Constraint version="1.1.0">
...
*/
private static void startDocument(XMLStreamWriter xsw) {
private static void startDocument(XMLStreamWriter xsw, String queryType) {
try {
xsw.writeStartDocument();
xsw.writeStartElement("csw", "GetRecords", CSW_URI);
Expand All @@ -72,7 +76,7 @@ private static void startDocument(XMLStreamWriter xsw) {
xsw.writeAttribute("service", "CSW");
xsw.writeAttribute("version", CSW_VERSION);

xsw.writeAttribute("maxRecords", "10000");
xsw.writeAttribute("maxRecords", "100");
xsw.writeAttribute("startPosition", "1");

xsw.writeAttribute("resultType", "results"); // or "validate" or "hits"
Expand All @@ -90,7 +94,15 @@ private static void startDocument(XMLStreamWriter xsw) {
// parse but it's safe.
xsw.writeStartElement(CSW_URI, "ElementSetName");
// changes from full to summary for performance reasons. "brief" would be even faster but doesn't include dates
xsw.writeCharacters("summary");
String type = queryType;
// these are the valid values
if (type == null) {
type = "summary";
}
if (!("summary".equals(type) || "brief".equals(type) || "full".equals(type))) {
throw new IllegalArgumentException("Type needs to be one of: summary, brief, full. Was: " + type);
}
xsw.writeCharacters(type);
xsw.writeEndElement(); // ElementSetName


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,28 @@ public void testWithNoFilter() {
fail("Should have thrown exception");
}

@Test
public void testRequestType() {
// build filter
Filter filter = createEqualsFilter("csw:Any", "testing");
String request = org.oskari.csw.request.GetRecords.createRequest(filter);

assertTrue("Should get 'summary' as request type", request.contains("<csw:ElementSetName>summary</csw:ElementSetName>"));

request = org.oskari.csw.request.GetRecords.createRequest(filter, "brief");
assertTrue("Should get 'brief' as request type", request.contains("<csw:ElementSetName>brief</csw:ElementSetName>"));

request = org.oskari.csw.request.GetRecords.createRequest(filter, "full");
assertTrue("Should get 'full' as request type", request.contains("<csw:ElementSetName>full</csw:ElementSetName>"));
}

@Test(expected = IllegalArgumentException.class)
public void testRequestTypeInvalid() {
// build filter
Filter filter = createEqualsFilter("csw:Any", "testing");
org.oskari.csw.request.GetRecords.createRequest(filter, "dummy");
}

@Test
public void testSimpleFilter() throws IOException, SAXException {
// build filter
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" xmlns:gmd="http://www.isotc211.org/2005/gmd"
xmlns:gml="http://www.opengis.net/gml" xmlns:ows="http://www.opengis.net/ows"
xmlns:ogc="http://www.opengis.net/ogc"
service="CSW" version="2.0.2" maxRecords="10000" startPosition="1" resultType="results"
service="CSW" version="2.0.2" maxRecords="100" startPosition="1" resultType="results"
outputFormat="application/xml" outputSchema="http://www.isotc211.org/2005/gmd">
<csw:Query typeNames="gmd:MD_Metadata">
<csw:ElementSetName>summary</csw:ElementSetName>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" xmlns:gmd="http://www.isotc211.org/2005/gmd"
xmlns:ogc="http://www.opengis.net/ogc" service="CSW" version="2.0.2" maxRecords="10000"
xmlns:ogc="http://www.opengis.net/ogc" service="CSW" version="2.0.2" maxRecords="100"
startPosition="1" resultType="results" outputFormat="application/xml"
outputSchema="http://www.isotc211.org/2005/gmd">
<csw:Query typeNames="gmd:MD_Metadata">
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?xml version='1.0' encoding='UTF-8'?>
<csw:GetRecords xmlns:csw="http://www.opengis.net/cat/csw/2.0.2" xmlns:gmd="http://www.isotc211.org/2005/gmd"
xmlns:ogc="http://www.opengis.net/ogc" service="CSW" version="2.0.2" maxRecords="10000"
xmlns:ogc="http://www.opengis.net/ogc" service="CSW" version="2.0.2" maxRecords="100"
startPosition="1" resultType="results" outputFormat="application/xml"
outputSchema="http://www.isotc211.org/2005/gmd">
<csw:Query typeNames="gmd:MD_Metadata">
Expand Down