View Javadoc
1   /*
2    * Copyright 2018-2022 Medical Information Systems Research Group (https://medical.zcu.cz),
3    * Department of Computer Science and Engineering, University of West Bohemia.
4    * Address: Univerzitni 8, 306 14 Plzen, Czech Republic.
5    *
6    * Author Petr Vcelak (vcelak@kiv.zcu.cz).
7    *
8    * This file is part of MRECore project.
9    *
10   * MRECore is free software: you can redistribute it and/or modify
11   * it under the terms of the GNU General Public License as published by
12   * the Free Software Foundation, either version 3 of the License.
13   *
14   * MRECore is distributed in the hope that it will be useful,
15   * but WITHOUT ANY WARRANTY; without even the implied warranty of
16   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17   * GNU General Public License for more details.
18   *
19   * You should have received a copy of the GNU General Public License
20   * along with MRECore. If not, see <http://www.gnu.org/licenses/>.
21   */
22  package cz.zcu.mre.service.data;
23  
24  import cz.zcu.mre.data.core.MREData;
25  import cz.zcu.mre.data.core.MREStringLang;
26  import cz.zcu.mre.mrelib.util.DateUtil;
27  import java.time.LocalDate;
28  import java.time.LocalDateTime;
29  import java.util.ArrayList;
30  import java.util.Date;
31  import java.util.Iterator;
32  import java.util.List;
33  import java.util.Set;
34  import org.apache.jena.arq.querybuilder.AbstractQueryBuilder;
35  import org.apache.jena.arq.querybuilder.AskBuilder;
36  import org.apache.jena.arq.querybuilder.ConstructBuilder;
37  import org.apache.jena.arq.querybuilder.DescribeBuilder;
38  import org.apache.jena.arq.querybuilder.SelectBuilder;
39  import org.apache.jena.arq.querybuilder.UpdateBuilder;
40  import org.apache.jena.datatypes.xsd.impl.XSDDateType;
41  import org.apache.jena.graph.Node;
42  import org.apache.jena.graph.NodeFactory;
43  import org.apache.jena.graph.Triple;
44  import org.apache.jena.ontology.Individual;
45  import org.apache.jena.query.Query;
46  import org.apache.jena.rdf.model.Literal;
47  import org.apache.jena.rdf.model.Resource;
48  import org.apache.jena.rdf.model.ResourceFactory;
49  import org.apache.jena.sparql.modify.request.UpdateDeleteInsert;
50  import org.apache.jena.vocabulary.RDF;
51  import org.slf4j.Logger;
52  import org.slf4j.LoggerFactory;
53  
54  /**
55   * 
56   * @author Petr Vcelak (vcelak@kiv.zcu.cz)
57   */
58  public class SPARQLBuilderImpl implements SPARQLBuilder {
59  
60      private static final Logger LOG = LoggerFactory.getLogger(SPARQLBuilderImpl.class);
61  
62      //@Autowired
63      private MREDataService dataService;
64  
65      /**
66       * Default constructor.
67       */
68      public SPARQLBuilderImpl() {
69      }
70  
71      public SPARQLBuilderImpl(MREDataService dataService) {
72          this.dataService = dataService;
73      }
74  
75      public void setDataService(MREDataService dataService) {
76          this.dataService = dataService;
77      }
78  
79      /**
80       * Build ASK query for the instance.
81       *
82       * @param instance The instance.
83       * @return Prepared Query instance.
84       */
85      @Override
86      public Query ask(Object instance) {
87  
88          AbstractQueryBuilder b = builderQuery(QUERY.ASK, instance, false, true);
89          return b.build();
90      }
91  
92      /**
93       * Build ASK query for test if the instance URI and type is
94       * persistent/exist. This method produces only one triple (instance URI,
95       * rdf:type property and class URI for object) in the WHERE part.
96       *
97       * @param instance The instance.
98       * @return The ASK Query with only one triple (instance URI, rdf:type
99       * property and class URI for object).
100      */
101     @Override
102     public Query askExist(Object instance) {
103 
104         AskBuilder builder = (AskBuilder) builderQuery(QUERY.ASK, instance, false, false);
105 
106         return builder.build();
107     }
108 
109     /**
110      * Build ASK query for test if the instance is persistent/exist. This method
111      * produces only one triple (instance URI, rdf:type property and class URI
112      * for object) in the WHERE part.
113      *
114      * @param instance The instance.
115      * @return The ASK Query with only one triple (instance URI, rdf:type
116      * property and class URI for object).
117      */
118     @Override
119     public Query askExistAll(Object instance) {
120 
121         AskBuilder builder = (AskBuilder) builderQuery(QUERY.ASK, instance, false, true);
122 
123         return builder.build();
124     }
125     
126     /**
127      * Generate SELECT for list of instances that is based on the instance
128      * (type) and its attributes.
129      *
130      * This can work as query for "select all instances" and "select instance(s)
131      * with value of one/more attributes".
132      *
133      * @param instance The instance for building a complete general SELECT.
134      * @return Query instance that defines member attributes and rdf:type.
135      */
136     @Override
137     public Query select(Object instance) {
138 
139         return builderQuery(QUERY.SELECT, instance, false, true).build();
140     }
141 
142     /**
143      * Generate SELECT for list all instances that is based on the instance
144      * (type) and NO its attribute values.
145      *
146      * This can work as query for "select all instances" and "select instance(s)
147      * with value of one/more attributes".
148      *
149      * @param instance The instance for building a complete general SELECT.
150      * @return Query instance that defines member attributes and rdf:type.
151      */
152     @Override
153     public Query selectAll(MREData instance) {
154 
155         return selectAllBuilder(instance).build();
156     }
157 
158     @Override
159     public SelectBuilder selectAllBuilder(MREData instance) {
160 
161         return (SelectBuilder) builderQuery(QUERY.SELECT, instance, false, false);
162     }
163 
164     /**
165      * Build query to SELECT the resource rdf:type.
166      *
167      * @param resourceURI The resource URI as a String.
168      * @return Prepared Query instance.
169      */
170     @Override
171     public Query selectType(String resourceURI) {
172 
173         return selectType(ResourceFactory.createResource(resourceURI));
174     }
175 
176     /**
177      * Build query to SELECT the resource rdf:type.
178      *
179      * @param resource The resource URI.
180      * @return Prepared Query instance.
181      */
182     @Override
183     public Query selectType(Resource resource) {
184 
185         SelectBuilder selectBuilder = new SelectBuilder();
186         selectBuilder.setDistinct(true);
187         selectBuilder.addVar("type");
188         selectBuilder.addWhere(Triple.create(resource.asNode(), RDF.type.asNode(), NodeFactory.createVariable("type")));
189 
190         return selectBuilder.build();
191     }
192 
193     /**
194      * Build DELETE update query for the instance removal.
195      *
196      * @param instance The instance to remove.
197      * @return Prepared UpdateBuilder instance.
198      */
199     @Override
200     public UpdateBuilder delete(Object instance) {
201 
202         return builderUpdate(QUERY.DELETE, instance, false);
203     }
204 
205     /**
206      * Build DELETE update query for the deep instance removal. Remove
207      * recursively all sub-instances.
208      *
209      * @param instance The instance to deeply remove.
210      * @return Prepared UpdateBuilder instance.
211      */
212     @Override
213     public UpdateBuilder deleteAll(Object instance) {
214 
215         return builderUpdate(QUERY.DELETE, instance, true);
216     }
217 
218     /**
219      * Build INSERT update query for making instance persistent.
220      *
221      * @param instance The instance for insert.
222      * @return Prepared UpdateBuilder instance.
223      */
224     @Override
225     public UpdateBuilder insert(Object instance) {
226         return builderUpdate(QUERY.INSERT, instance, false);
227     }
228 
229     /**
230      * Build INSERT update query for making the instance persistent. Inserts
231      * recursively all sub-instances.
232      *
233      * @param instance The instance for insert.
234      * @return Prepared UpdateBuilder instance.
235      */
236     @Override
237     public UpdateBuilder insertAll(Object instance) {
238         return builderUpdate(QUERY.INSERT, instance, true);
239     }
240 
241     /**
242      * Build DELETE INSERT update query for updating the instance.
243      *
244      * @param instanceOld The old instance is for delete part.
245      * @param instanceNew The new instance is for insert part.
246      * @return Prepared UpdateDeleteInsert builder instance.
247      */
248     @Override
249     public UpdateDeleteInsert update(Object instanceOld, Object instanceNew) {
250 
251         return update(instanceOld, instanceNew, false);
252     }
253 
254     /**
255      * Build DELETE INSERT update query for updating the instance. Prepare
256      * delete and inserts recursively for all sub-instances.
257      *
258      * @param instanceOld The old instance is for delete part.
259      * @param instanceNew The new instance is for insert part.
260      * @return Prepared UpdateDeleteInsert builder instance.
261      */
262     @Override
263     public UpdateDeleteInsert updateAll(Object instanceOld, Object instanceNew) {
264         return update(instanceOld, instanceNew, true);
265     }
266 
267     /**
268      * Build DELETE INSERT update query based on old and new instances of
269      * object.
270      *
271      * @param instanceOld The old (previous) version.
272      * @param instanceNew The new (actual) version.
273      * @param deep Produce update query on the top level or recursive (deep)
274      * one.
275      * @return UpdateDeleteInsert query builder instance.
276      */
277     private UpdateDeleteInsert update(Object instanceOld, Object instanceNew, boolean deep) {
278 
279         List<Triple> triplesOld = buildTriples(QUERY.DELETE, instanceOld, deep);
280         List<Triple> triplesNew = buildTriples(QUERY.INSERT, instanceNew, deep);
281 
282         UpdateDeleteInsert update = new UpdateDeleteInsert();
283         triplesOld.stream().forEach(t -> update.getDeleteAcc().addTriple(t));
284         triplesNew.stream().forEach(t -> update.getInsertAcc().addTriple(t));
285 
286         return update;
287     }
288 
289     /**
290      * Build the query (ASK, CONSTRUCT, DESCRIBE, SELECT) on the instance.
291      *
292      * It is not possible to make UPDATE queries by this method.
293      *
294      * @param type The query type.
295      * @param instance The instance.
296      * @param deep Indicates if method should generate query for all
297      * sub-instances (deep=true).
298      * @return Instance of builder that implements AbstractQueryBuilder:
299      * AskBuilder, ConstructBuilder, DescribeBuilder or SelectBuilder.
300      */
301     private AbstractQueryBuilder builderQuery(QUERY type, Object instance, boolean deep, boolean withValues) {
302 
303         AbstractQueryBuilder queryBuilder = null;
304         List<Triple> triples = buildTriples(type, instance, deep);
305 
306         switch (type) {
307             case ASK:
308                 AskBuilder askBuilder = new AskBuilder();
309                 //triples.stream().forEach(t -> askBuilder.addWhere(t));
310 
311                 if (!withValues) {
312                     triples.clear();
313 
314                     // rdf:type only, no WHERE triples
315                     MREDataFieldValue[] fields = dataService.builderValuesGet(instance);
316                     addTripleRDFType(triples, fields, instance);
317                 }
318                 triples.stream().forEach(t -> {
319                     askBuilder.addWhere(t);
320                 });
321 
322                 queryBuilder = askBuilder;
323                 break;
324             case CONSTRUCT:
325                 ConstructBuilder constructBuilder = new ConstructBuilder();
326                 triples.stream().forEach(t -> constructBuilder.addWhere(t));
327                 queryBuilder = constructBuilder;
328                 break;
329             case DESCRIBE:
330                 DescribeBuilder describeBuilder = new DescribeBuilder();
331                 triples.stream().forEach(t -> describeBuilder.addWhere(t));
332                 queryBuilder = describeBuilder;
333                 break;
334 
335             case SELECT:
336                 SelectBuilder selectBuilder = new SelectBuilder();
337 
338                 if (!withValues) {
339                     // rdf:type only, no WHERE triples
340                     MREDataFieldValue[] fields = dataService.builderValuesGet(instance);
341                     triples.clear();
342                     addTripleRDFType(triples, fields, instance);
343                 }
344                 triples.stream().forEach(t -> {
345                     selectBuilder.addWhere(t);
346                 });
347 
348                 selectAddVariables(selectBuilder, instance);
349                 selectAddWhereVariables(selectBuilder, instance);
350 
351                 // add VALUE keyword with a resource value for ?mreuri
352                 Resource subject = ((MREData) instance).getResource();
353                 if (subject != null) {
354                     selectBuilder.addValueVar(SPARQLBuilderVariables.VARIABLE_URI, ((MREData) instance).getResource());
355                 }
356 
357                 queryBuilder = selectBuilder;
358                 break;
359 
360             default: {
361                 throw new IllegalArgumentException("Method does not produce update queries.");
362             }
363         }
364 
365         return queryBuilder;
366     }
367 
368     /**
369      * Build the update query (INSERT, DELETE) on the instance.
370      *
371      * It is not possible to make ASK, CONSTRUCT, DESCRIBE or SELECT queries by
372      * this method.
373      *
374      * @param type The query type.
375      * @param instance The instance.
376      * @param deep Indicates if method should generate query for all
377      * sub-instances (deep=true).
378      * @return Instance of builder that implements AbstractQueryBuilder:
379      * AskBuilder, ConstructBuilder, DescribeBuilder or SelectBuilder.
380      */
381     private UpdateBuilder builderUpdate(QUERY type, Object instance, boolean deep) {
382 
383         UpdateBuilder updateBuilder = new UpdateBuilder();
384         List<Triple> triples = buildTriples(type, instance, deep);
385 
386         switch (type) {
387 
388             case INSERT:
389                 triples.stream().forEach(t -> updateBuilder.addInsert(t));
390                 break;
391 
392             case DELETE:
393                 triples.stream().forEach(t -> updateBuilder.addDelete(t));
394                 break;
395 
396             case UPDATE:
397                 // TODO remove all triples and new ones for the instance object.
398                 break;
399 
400             default: {
401                 throw new IllegalArgumentException("Method does not produce update queries.");
402             }
403         }
404 
405         return updateBuilder;
406     }
407 
408     /**
409      * Generate the List of Triple instances for the instance.
410      *
411      * @param type The query type.
412      * @param instance The instance.
413      * @param deep Indicates if method should generate Triple also for all
414      * sub-instances (deep=true).
415      * @return List of Triple instances.
416      */
417     private List<Triple> buildTriples(QUERY type, Object instance, boolean deep) {
418 
419         List<Triple> triples = new ArrayList<>();
420 
421         if (type == null || instance == null) {
422             return triples;
423         }
424 
425         checkMREData(type, instance);
426 
427         // is primitive data type?
428         if (dataService.isPrimitiveDataType(instance)) {
429 
430             LOG.debug("Primitive data type {} has value {}", instance.getClass(), instance);
431             return triples;
432         }
433 
434         MREDataFieldValue[] fields = dataService.builderValuesGet(instance);
435         for (int i = 0; i < fields.length; i++) {
436 
437             Set<Object> fieldValues = fields[i].getValue();
438 
439             // ignore empty fields
440             if (fieldValues.isEmpty()) {
441                 continue;
442             }
443 
444             // add rdf:type only once when i=0
445             if (i == 0) {
446                 addTripleRDFType(triples, fields, instance);
447             }
448 
449             // loop over all values
450             Iterator<Object> it = fieldValues.iterator();
451             while (it.hasNext()) {
452                 Object object = it.next();
453 
454                 // ignore empty values
455                 if (object == null) {
456                     continue;
457                 }
458 
459                 if (object instanceof List) {
460 
461                     // save list of objects
462                     List list = (List) object;
463                     for (Object o : list) {
464                         LOG.debug("Build triples - {}, List field '{}'", o.getClass(), fields[i].getName());
465                         addTriples(triples, type, fields[i], o, deep);
466                         if (deep) {
467                             triples.addAll(buildTriples(type, o, deep));
468                         }
469                     }
470 
471                 } else if (object instanceof Set) {
472 
473                     // save set of objects
474                     Set set = (Set) object;
475                     for (Object o : set) {
476                         LOG.debug("Build triples - {}, Set field '{}'", o.getClass(), fields[i].getName());
477                         addTriples(triples, type, fields[i], o, deep);
478                         if (deep) {
479                             triples.addAll(buildTriples(type, o, deep));
480                         }
481                     }
482 
483                 } else if (object instanceof MREData) {
484 
485                     // FIX initialize if MREData instance has no URI -- need for deep insert/delete
486                     if (!((MREData) object).hasURI() && (type == QUERY.INSERT || type == QUERY.DELETE)) {
487                         ((MREData) object).generateNewResource();
488                     }
489 
490                     LOG.debug("Build triples - {}, MREData field '{}', type {}", instance.getClass(), fields[i].getName(), object.getClass());
491                     addTripleObject(triples, type, fields[i], object);
492 
493                 } else if (object instanceof Individual) {
494 
495                     LOG.debug("Build triples - {}, Individual field '{}', type {}", instance.getClass(), fields[i].getName(), object.getClass());
496                     addTripleObject(triples, type, fields[i], object);
497 
498                 } else {
499                     LOG.debug("Build triples - {}, primitive type field '{}', type {}", instance.getClass(), fields[i].getName(), object.getClass());
500                     addTriples(triples, type, fields[i], object, deep);
501                 }
502             }
503         }
504 
505         // return triples
506         return triples;
507     }
508 
509     /**
510      * Build the rdf:type triple and add to the list.
511      *
512      * @param triples List of Triple instances.
513      * @param fields Preprocessed all fields of the instance.
514      * @param i The number of active field that should be processed.
515      * @param instance The instance.
516      */
517     private static void addTripleRDFType(List<Triple> triples, MREDataFieldValue[] fields, Object instance) {
518 
519         int i = 0;
520 
521         Node s;
522         if (fields[i].getSubject() != null) {
523             s = NodeFactory.createURI(fields[i].getSubject());
524         } else {
525             s = VARIABLE_URI;
526         }
527 
528         Node p = RDF.type.asNode();
529         Node o;
530 
531         String object;
532         if (fields[i].getConfig().getTypeRDF() == null) {
533             object = fields[i].getConfig().getOntology().concat(instance.getClass().getSimpleName());
534         } else {
535             object = fields[i].getConfig().getTypeRDF();
536         }
537         o = NodeFactory.createURI(object);
538 
539         // add triple
540         triples.add(Triple.create(s, p, o));
541     }
542 
543     /**
544      * Build and add triples to the list.
545      *
546      * @param triples List of Triple instances.
547      * @param fields Preprocessed all fields of the instance.
548      * @param i The number of active field that should be processed.
549      * @param instance The instance.
550      */
551     private void addTriples(List<Triple> triples, QUERY type, MREDataFieldValue field, Object object, boolean deep) {
552 
553         if (object == null) {
554             return;
555         }
556 
557         if (object instanceof List) {
558             triples.addAll(buildTriples(type, object, deep));
559         } else if (object instanceof Set) {
560             triples.addAll(buildTriples(type, object, deep));
561         } else if (object instanceof Individual) {
562             addTripleObject(triples, type, field, ((Individual) object).getURI());
563         } else if (object instanceof MREData) {
564             checkMREData(type, (MREData) object);
565             addTripleObject(triples, type, field, ((MREData) object).getUri());
566         } else {
567             addTripleDatatype(triples, type, field, object);
568         }
569     }
570 
571     /**
572      * Check MREData instance if resource URI is defined. Generate a new one If
573      * not.
574      *
575      * @param type Query type.
576      * @param instance The instance.
577      */
578     private static void checkMREData(QUERY type, Object instance) {
579 
580         if (type == QUERY.INSERT && instance instanceof MREData && !((MREData) instance).hasURI()) {
581             ((MREData) instance).generateNewResource();
582             LOG.info("Auto-generated new resource URI for the instance: {}", ((MREData) instance).getResource());
583         }
584 
585     }
586 
587     /**
588      * Build and add triples for objects to the list.
589      *
590      * @param triples List of Triple instances.
591      * @param type The query type.
592      * @param field Preprocessed field of the instance.
593      * @param object The value as an Object.
594      */
595     private static void addTripleObject(List<Triple> triples, QUERY type, MREDataFieldValue field, Object object) {
596 
597         if (object == null) {
598             return;
599         }
600 
601         if (field.getSubject() == null) {
602             throw new IllegalArgumentException("Subject cannot be null.");
603         }
604 
605         Node s = NodeFactory.createURI(field.getSubject());
606         Node p = NodeFactory.createURI(field.getProperty().toString());
607         Node o;
608 
609         if (object instanceof MREData) {
610             // add MREData instance resource URI
611             o = ((MREData) object).getResource().asNode();
612         } else if (object instanceof Individual) {
613             o = ((Individual) object).asNode();
614         } else if (object instanceof String) {
615             o = NodeFactory.createURI(((String) object));
616         } else {
617             LOG.warn("Unsupported Java {} value {}. Using toString() method.", object.getClass(), object);
618             // use toString() for other Java classes
619             o = NodeFactory.createURI(object.toString());
620         }
621 
622         // add triple
623         triples.add(Triple.create(s, p, o));
624     }
625 
626     /**
627      * Build and add triples for objects to the list.
628      *
629      * @param triples List of Triple instances.
630      * @param type The query type.
631      * @param field Preprocessed field of the instance.
632      * @param value The value as an Object.
633      */
634     private static void addTripleDatatype(List<Triple> triples, QUERY type, MREDataFieldValue field, Object value) {
635 
636         Node s;
637         if (field.getSubject() != null) {
638             s = NodeFactory.createURI(field.getSubject());
639         } else {
640             s = VARIABLE_URI;
641         }
642 
643         Node p = NodeFactory.createURI(field.getProperty().toString());
644         Node o = null;
645 
646         Literal literalValue;
647         if (value instanceof Date) {
648             // XSD Date format as String
649             String date = DateUtil.xsdDate((Date) value);
650             literalValue = ResourceFactory.createTypedLiteral(date);
651 
652         } else if (value instanceof LocalDate) {
653             // XSD Date format as String
654             LocalDate localDate = (LocalDate) value;
655             String date = localDate.toString();
656             literalValue = ResourceFactory.createTypedLiteral(date, XSDDateType.XSDdate);
657 
658         } else if (value instanceof LocalDateTime) {
659             // XSD Date format as String
660             LocalDateTime localDateTime = (LocalDateTime) value;
661             String dateTime = localDateTime.toString();
662 
663             // TODO check for dateTime format (due to ignored zeros) to fill in correct date type
664             literalValue = ResourceFactory.createTypedLiteral(dateTime, XSDDateType.XSDdateTime);
665 
666         } else {
667             // literal value
668             literalValue = createLiteralValue(type, field, value);
669         }
670 
671         // output literal value in N-TRIPLE
672         if (literalValue != null) {
673             o = literalValue.asNode();
674         }
675 
676         // add triple
677         triples.add(Triple.create(s, p, o));
678     }
679 
680     /**
681      * Create a Literal value for the value.
682      *
683      * @param type Query type.
684      * @param field Preprocessed field of the instance.
685      * @param value The value as an Object.
686      * @return The Literal instance or null.
687      */
688     private static Literal createLiteralValue(QUERY type, MREDataFieldValue field, Object value) {
689 
690         if (value == null) {
691             return null;
692         }
693 
694         if (value instanceof String) {
695             // just string, no language, (default)
696             return ResourceFactory.createPlainLiteral((String) value);
697 
698         } else if (value instanceof MREStringLang) {            
699 
700             // no lang specified here means plain literal
701             if (((MREStringLang) value).getLang() == null) {
702                 return ResourceFactory.createPlainLiteral(((MREStringLang) value).getString());
703             }
704 
705             // string with language
706             return ResourceFactory.createLangLiteral(((MREStringLang) value).getString(), ((MREStringLang) value).getLang());
707         }
708 
709         // typed literal
710         return ResourceFactory.createTypedLiteral(value);
711     }
712 
713     /**
714      * Get the string with all variable names to the SELECT part.
715      *
716      * @param instance MREData instance.
717      * @return String with all variable names for the instance class/type.
718      */
719     private void selectAddVariables(SelectBuilder selectBuilder, Object instance) {
720 
721         selectBuilder.addVar(VARIABLE_URI);
722         selectBuilder.addVar(VARIABLE_TYPE);
723 
724         MREDataFieldValue[] fields = dataService.builderValuesGet(instance);
725         for (MREDataFieldValue field : fields) {
726             selectBuilder.addVar(field.getName());
727         }
728     }
729 
730     /**
731      * Get WHERE part (triples) with variables definition as a String.
732      *
733      * @param instance MREData instance.
734      * @return String with variables for SELECT clause. The first variable is
735      * ?mreuri (see static field VARIABLE_URI), the second is ?mretype (see
736      * static field VARIABLE_TYPE) and all other variables follows.
737      */
738     private void selectAddWhereVariables(SelectBuilder selectBuilder, Object instance) {
739 
740         MREDataFieldValue[] fields = dataService.builderValuesGet(instance);
741 
742         // add default variables for all generated SELECT queries (URI, rdf:type)
743         selectBuilder.addWhere(VARIABLE_URI, RDF.type, VARIABLE_TYPE);
744 
745         // add instance specific where triples with variable
746         for (MREDataFieldValue field : fields) {
747 
748             // add OPTIONAL keyword for WHERE clause of SELECT
749             selectBuilder.addOptional(VARIABLE_URI, field.getProperty(), NodeFactory.createVariable(field.getName()));
750         }
751     }
752 
753 }