From f0d697873e26931b0dc89976db9d826b8fc51811 Mon Sep 17 00:00:00 2001
From: Esko Ikkala <esko.ikkala@aalto.fi>
Date: Tue, 11 Sep 2018 12:16:32 +0300
Subject: [PATCH] Add object mapper for SPARQL results

---
 src/client/containers/MapApp.js  |   5 +-
 src/server/Datasets.js           |  31 +++++----
 src/server/SparqlObjectMapper.js | 114 +++++++++++++++++++++++++++++++
 src/server/SparqlSearchEngine.js |   6 +-
 4 files changed, 138 insertions(+), 18 deletions(-)
 create mode 100644 src/server/SparqlObjectMapper.js

diff --git a/src/client/containers/MapApp.js b/src/client/containers/MapApp.js
index 9c90da54..205287fd 100644
--- a/src/client/containers/MapApp.js
+++ b/src/client/containers/MapApp.js
@@ -135,6 +135,7 @@ let MapApp = (props) => {
   // console.log('oneColumnView', oneColumnView)
   // console.log('resultFormat', resultFormat)
   // console.log('mapMode', mapMode)
+  console.log(props.results)
 
   let table = '';
   if ((oneColumnView && options.resultFormat === 'table') || (!oneColumnView)) {
@@ -173,7 +174,6 @@ let MapApp = (props) => {
         />
       );
     } else {
-      console.log(props.results)
       mapElement = (
         <LeafletMap
           results={props.results}
@@ -267,7 +267,8 @@ const mapStateToProps = (state) => {
     browser: state.browser,
     search: state.search,
     map: state.map,
-    results: getVisibleResults(state.search),
+    // results: getVisibleResults(state.search),
+    results: state.search.results,
     resultValues: getVisibleValues(state.search),
   };
 };
diff --git a/src/server/Datasets.js b/src/server/Datasets.js
index 78d13e58..e682d709 100644
--- a/src/server/Datasets.js
+++ b/src/server/Datasets.js
@@ -17,36 +17,39 @@ module.exports = {
       PREFIX mmm-schema: <http://ldf.fi/mmm/schema/>
       PREFIX skos: <http://www.w3.org/2004/02/skos/core#>
       PREFIX sdbm: <https://sdbm.library.upenn.edu/>
-      SELECT ?id ?label ?sdbm_id ?material ?author ?timespan ?place ?lat ?long ?language {
+      SELECT * WHERE {
         ?id a frbroo:F4_Manifestation_Singleton .
         ?id rdfs:label ?label .
         ?id crm:P1_is_identified_by ?sdbm_id .
         OPTIONAL {
-          ?id crm:P45_consists_of ?material .
+          ?id crm:P45_consists_of ?material__id .
+          BIND (?material__id AS ?material__label)
         }
         ?expression_creation frbroo:R18_created ?id .
         OPTIONAL {
-          ?expression_creation crm:P14_carried_out_by ?author_id .
-          ?author_id skos:prefLabel ?author .
-          OPTIONAL { ?author_id mmm-schema:person_place/skos:prefLabel ?author_place . }
+          ?expression_creation crm:P14_carried_out_by ?author .
+          ?author skos:prefLabel ?author__label .
+          OPTIONAL { ?author mmm-schema:person_place/skos:prefLabel ?author__place . }
         }
         OPTIONAL {
-          ?expression_creation crm:P4_has_time_span ?timespan_id .
-          ?timespan_id rdfs:label ?timespan .
-          ?timespan crm:P79_beginning_is_qualified_by ?timespan_start .
-          ?timespan crm:P80_end_is_qualified_by ?timespan_end .
+          ?expression_creation crm:P4_has_time_span ?timespan .
+          ?timespan rdfs:label ?timespan__id .
+          ?timespan crm:P79_beginning_is_qualified_by ?timespan__start .
+          ?timespan crm:P80_end_is_qualified_by ?timespan__end .
+          BIND (?timespan__id AS ?timespan__label)
         }
         OPTIONAL {
-         ?expression_creation crm:P7_took_place_at ?place_id .
-         ?place_id skos:prefLabel ?place .
+         ?expression_creation crm:P7_took_place_at ?creation_place .
+         ?creation_place skos:prefLabel ?creation_place__id .
          OPTIONAL {
-           ?place_id wgs84:lat ?lat .
-           ?place_id wgs84:long ?long .
+           ?creation_place wgs84:lat ?creation_place__lat .
+           ?creation_place wgs84:long ?creation_place__long .
          }
        }
        OPTIONAL {
          ?id crm:P128_carries ?expression .
-         ?expression crm:P72_has_language ?language .
+         ?expression crm:P72_has_language ?language__id .
+         BIND (?language__id AS ?language__label)
        }
       }
       LIMIT 5000
diff --git a/src/server/SparqlObjectMapper.js b/src/server/SparqlObjectMapper.js
new file mode 100644
index 00000000..a04da4d0
--- /dev/null
+++ b/src/server/SparqlObjectMapper.js
@@ -0,0 +1,114 @@
+import _ from 'lodash';
+
+/**
+* @param {Array} objects A list of objects as SPARQL results.
+* @returns {Array} The mapped object list.
+* @description
+* Map the SPARQL results as objects, and return a list where result rows with the same
+* id are merged into one object.
+*/
+export const makeObjectList = (objects) => {
+  let objList = _.transform(objects, function(result, obj) {
+    if (!obj.id) {
+      return null;
+    }
+    //let orig = obj;
+    obj = makeObject(obj);
+    //obj = reviseObject(obj, orig);
+    mergeValueToList(result, obj);
+  });
+  return objList;
+  //return self.postProcess(objList);
+};
+
+/**
+* @param {Object} obj A single SPARQL result row object.
+* @returns {Object} The mapped object.
+* @description
+* Flatten the result object. Discard everything except values.
+* Assume that each property of the obj has a value property with
+* the actual value.
+*/
+const makeObject = (obj) => {
+  let o = new Object;
+  _.forIn(obj, function(value, key) {
+    // If the variable name contains "__", an object
+    // will be created as the value
+    // E.g. { place__id: '1' } -> { place: { id: '1' } }
+    _.set(o, key.replace(/__/g, '.'), value.value);
+  });
+  return o;
+};
+
+/**
+* @param {Array} valueList A list to which the value should be added.
+* @param {Object} value The value to add to the list.
+* @returns {Array} The merged list.
+* @description
+* Add the given value to the given list, merging an object value to and
+* object in the list if both have the same id attribute.
+* A value already present in valueList is discarded.
+*/
+const mergeValueToList = (valueList, value) => {
+  let old;
+  if (_.isObject(value) && value.id) {
+    // Check if this object has been constructed earlier
+    old = _.findLast(valueList, function(e) {
+      return e.id === value.id;
+    });
+    if (old) {
+      // Merge this object to the object constructed earlier
+      mergeObjects(old, value);
+    }
+  } else {
+    // Check if this value is present in the list
+    old = _.findLast(valueList, function(e) {
+      return _.isEqual(e, value);
+    });
+  }
+  if (!old) {
+    // This is a distinct value
+    valueList.push(value);
+  }
+  return valueList;
+};
+
+/**
+* @param {Object} first An object as returned by makeObject.
+* @param {Object} second The object to merge with the first.
+* @returns {Object} The merged object.
+* @description
+* Merges two objects.
+*/
+const mergeObjects = (first, second) => {
+  // Merge two objects into one object.
+  return _.mergeWith(first, second, merger);
+};
+
+const merger = (a, b) => {
+  if (_.isEqual(a, b)) {
+    return a;
+  }
+  if (a && !b) {
+    return a;
+  }
+  if (b && !a) {
+    return b;
+  }
+  if (_.isArray(a)) {
+    if (_.isArray(b)) {
+      b.forEach(function(bVal) {
+        return mergeValueToList(a, bVal);
+      });
+      return a;
+    }
+    return mergeValueToList(a, b);
+  }
+  if (_.isArray(b)) {
+    return mergeValueToList(b, a);
+  }
+  if (!(_.isObject(a) && _.isObject(b) && a.id === b.id)) {
+    return [a, b];
+  }
+  return mergeObjects(a, b);
+};
diff --git a/src/server/SparqlSearchEngine.js b/src/server/SparqlSearchEngine.js
index d0d629b6..7fd96ee3 100644
--- a/src/server/SparqlSearchEngine.js
+++ b/src/server/SparqlSearchEngine.js
@@ -4,6 +4,7 @@ import {
   mapAllResults,
   mergeAllResults
 } from './Mappers';
+import { makeObjectList } from './SparqlObjectMapper';
 
 class SparqlSearchEngine {
 
@@ -13,6 +14,7 @@ class SparqlSearchEngine {
         if (data.results.bindings.length === 0) {
           return [];
         }
+        console.log(data.results.bindings)
         return mapper ? mapper(data.results.bindings) : data.results.bindings;
       });
   }
@@ -20,8 +22,8 @@ class SparqlSearchEngine {
   getAllManuscripts(datasetId) {
     const { endpoint, getAllQuery } = datasetConfig[datasetId];
     const sparqlApi = new SparqlApi({ endpoint });
-    console.log(getAllQuery)
-    return this.doSearch(getAllQuery, sparqlApi, mapAllResults);
+    //console.log(getAllQuery)
+    return this.doSearch(getAllQuery, sparqlApi, makeObjectList);
   }
 
   getFederatedManuscripts(datasets) {
-- 
GitLab