Index: src/org/openstreetmap/josm/gui/dialogs/relation/ChildRelationBrowser.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/relation/ChildRelationBrowser.java	(revision 15745)
+++ src/org/openstreetmap/josm/gui/dialogs/relation/ChildRelationBrowser.java	(working copy)
@@ -12,13 +12,10 @@
 import java.awt.event.ActionEvent;
 import java.awt.event.MouseEvent;
 import java.io.IOException;
-import java.net.HttpURLConnection;
 import java.util.Arrays;
 import java.util.HashSet;
-import java.util.Iterator;
 import java.util.List;
 import java.util.Set;
-import java.util.Stack;
 import java.util.stream.Collectors;
 
 import javax.swing.AbstractAction;
@@ -37,7 +34,6 @@
 import org.openstreetmap.josm.data.osm.DataSetMerger;
 import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.data.osm.RelationMember;
 import org.openstreetmap.josm.gui.ExceptionDialogUtil;
@@ -47,10 +43,10 @@
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.gui.progress.swing.PleaseWaitProgressMonitor;
+import org.openstreetmap.josm.gui.util.GuiHelper;
 import org.openstreetmap.josm.gui.widgets.PopupMenuLauncher;
+import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
 import org.openstreetmap.josm.io.OsmApi;
-import org.openstreetmap.josm.io.OsmApiException;
-import org.openstreetmap.josm.io.OsmServerObjectReader;
 import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.ImageProvider;
@@ -339,15 +335,37 @@
             OsmApi.getOsmApi().cancel();
         }
 
-        protected void refreshView(Relation relation) {
-            for (int i = 0; i < childTree.getRowCount(); i++) {
-                Relation reference = (Relation) childTree.getPathForRow(i).getLastPathComponent();
-                if (reference == relation) {
-                    model.refreshNode(childTree.getPathForRow(i));
+        protected MultiFetchServerObjectReader createReader() {
+            return MultiFetchServerObjectReader.create().setRecurseDownAppended(false).setRecurseDownRelations(true);
+        }
+
+        /**
+         * Merges the primitives in <code>ds</code> to the dataset of the edit layer
+         *
+         * @param ds the data set
+         */
+        protected void mergeDataSet(DataSet ds) {
+            if (ds != null) {
+                final DataSetMerger visitor = new DataSetMerger(getLayer().getDataSet(), ds);
+                visitor.merge();
+                if (!visitor.getConflicts().isEmpty()) {
+                    getLayer().getConflicts().add(visitor.getConflicts());
+                    conflictsCount += visitor.getConflicts().size();
                 }
             }
         }
 
+        protected void refreshView(Relation relation) {
+            GuiHelper.runInEDT(() -> {
+                for (int i = 0; i < childTree.getRowCount(); i++) {
+                    Relation reference = (Relation) childTree.getPathForRow(i).getLastPathComponent();
+                    if (reference == relation) {
+                        model.refreshNode(childTree.getPathForRow(i));
+                    }
+                }
+            });
+        }
+
         @Override
         protected void finish() {
             if (canceled)
@@ -374,14 +392,11 @@
      * The asynchronous task for downloading relation members.
      */
     class DownloadAllChildrenTask extends DownloadTask {
-        private final Stack<Relation> relationsToDownload;
-        private final Set<Long> downloadedRelationIds;
+        private final Relation relation;
 
         DownloadAllChildrenTask(Dialog parent, Relation r) {
             super(tr("Download relation members"), parent);
-            relationsToDownload = new Stack<>();
-            downloadedRelationIds = new HashSet<>();
-            relationsToDownload.push(r);
+            relation = r;
         }
 
         /**
@@ -405,64 +420,16 @@
             );
         }
 
-        /**
-         * Remembers the child relations to download
-         *
-         * @param parent the parent relation
-         */
-        protected void rememberChildRelationsToDownload(Relation parent) {
-            downloadedRelationIds.add(parent.getId());
-            for (RelationMember member: parent.getMembers()) {
-                if (member.isRelation()) {
-                    Relation child = member.getRelation();
-                    if (!downloadedRelationIds.contains(child.getId())) {
-                        relationsToDownload.push(child);
-                    }
-                }
-            }
-        }
-
-        /**
-         * Merges the primitives in <code>ds</code> to the dataset of the edit layer
-         *
-         * @param ds the data set
-         */
-        protected void mergeDataSet(DataSet ds) {
-            if (ds != null) {
-                final DataSetMerger visitor = new DataSetMerger(getLayer().getDataSet(), ds);
-                visitor.merge();
-                if (!visitor.getConflicts().isEmpty()) {
-                    getLayer().getConflicts().add(visitor.getConflicts());
-                    conflictsCount += visitor.getConflicts().size();
-                }
-            }
-        }
-
         @Override
         protected void realRun() throws SAXException, IOException, OsmTransferException {
             try {
-                while (!relationsToDownload.isEmpty() && !canceled) {
-                    Relation r = relationsToDownload.pop();
-                    if (r.isNew()) {
-                        continue;
-                    }
-                    rememberChildRelationsToDownload(r);
-                    progressMonitor.setCustomText(tr("Downloading relation {0}", r.getDisplayName(DefaultNameFormatter.getInstance())));
-                    OsmServerObjectReader reader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION,
-                            true);
-                    DataSet dataSet = null;
-                    try {
-                        dataSet = reader.parseOsm(progressMonitor
-                                .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
-                    } catch (OsmApiException e) {
-                        if (e.getResponseCode() == HttpURLConnection.HTTP_GONE) {
-                            warnBecauseOfDeletedRelation(r);
-                            continue;
-                        }
-                        throw e;
-                    }
-                    mergeDataSet(dataSet);
-                    refreshView(r);
+                MultiFetchServerObjectReader reader = createReader();
+                reader.append(relation.getMemberPrimitives());
+                DataSet dataSet = reader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+                mergeDataSet(dataSet);
+                Utils.filteredCollection(reader.getMissingPrimitives(), Relation.class).forEach(this::warnBecauseOfDeletedRelation);
+                for (Relation rel : dataSet.getRelations()) {
+                    refreshView((Relation) getLayer().getDataSet().getPrimitiveById(rel));
                 }
                 SwingUtilities.invokeLater(MainApplication.getMap()::repaint);
             } catch (OsmTransferException e) {
@@ -486,34 +453,18 @@
             this.relations = relations;
         }
 
-        protected void mergeDataSet(DataSet dataSet) {
-            if (dataSet != null) {
-                final DataSetMerger visitor = new DataSetMerger(getLayer().getDataSet(), dataSet);
-                visitor.merge();
-                if (!visitor.getConflicts().isEmpty()) {
-                    getLayer().getConflicts().add(visitor.getConflicts());
-                    conflictsCount += visitor.getConflicts().size();
-                }
-            }
-        }
-
         @Override
         protected void realRun() throws SAXException, IOException, OsmTransferException {
             try {
-                Iterator<Relation> it = relations.iterator();
-                while (it.hasNext() && !canceled) {
-                    Relation r = it.next();
-                    if (r.isNew()) {
-                        continue;
-                    }
-                    progressMonitor.setCustomText(tr("Downloading relation {0}", r.getDisplayName(DefaultNameFormatter.getInstance())));
-                    OsmServerObjectReader reader = new OsmServerObjectReader(r.getId(), OsmPrimitiveType.RELATION,
-                            true);
-                    DataSet dataSet = reader.parseOsm(progressMonitor
-                            .createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
-                    mergeDataSet(dataSet);
-                    refreshView(r);
+                MultiFetchServerObjectReader reader = createReader();
+                reader.append(relations);
+                DataSet dataSet = reader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+                mergeDataSet(dataSet);
+
+                for (Relation rel : dataSet.getRelations()) {
+                    refreshView((Relation) getLayer().getDataSet().getPrimitiveById(rel));
                 }
+
             } catch (OsmTransferException e) {
                 if (canceled) {
                     Logging.warn(tr("Ignoring exception because task was canceled. Exception: {0}", e.toString()));
Index: src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationMemberTask.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationMemberTask.java	(revision 15745)
+++ src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationMemberTask.java	(working copy)
@@ -129,6 +129,7 @@
                 if (canceled) return;
                 objectReader = MultiFetchServerObjectReader.create();
             }
+            objectReader.setRecurseDownAppended(false).setRecurseDownRelations(true);
             objectReader.append(children);
             progressMonitor.indeterminateSubTask(
                     buildDownloadFeedbackMessage()
Index: src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationTask.java
===================================================================
--- src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationTask.java	(revision 15745)
+++ src/org/openstreetmap/josm/gui/dialogs/relation/DownloadRelationTask.java	(working copy)
@@ -11,13 +11,14 @@
 
 import org.openstreetmap.josm.data.osm.DataSet;
 import org.openstreetmap.josm.data.osm.DataSetMerger;
-import org.openstreetmap.josm.data.osm.DefaultNameFormatter;
 import org.openstreetmap.josm.data.osm.Relation;
 import org.openstreetmap.josm.gui.ExceptionDialogUtil;
 import org.openstreetmap.josm.gui.MainApplication;
 import org.openstreetmap.josm.gui.PleaseWaitRunnable;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
-import org.openstreetmap.josm.io.OsmServerObjectReader;
+import org.openstreetmap.josm.gui.progress.ProgressMonitor;
+import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
+import org.openstreetmap.josm.io.OsmServerReader;
 import org.openstreetmap.josm.io.OsmTransferException;
 import org.openstreetmap.josm.tools.CheckParameterUtil;
 import org.openstreetmap.josm.tools.Logging;
@@ -33,7 +34,7 @@
     private Exception lastException;
     private final Collection<Relation> relations;
     private final OsmDataLayer layer;
-    private OsmServerObjectReader objectReader;
+    private OsmServerReader objectReader;
 
     /**
      * Creates the download task
@@ -77,30 +78,23 @@
     protected void realRun() throws SAXException, IOException, OsmTransferException {
         try {
             final DataSet allDownloads = new DataSet();
-            int i = 0;
             getProgressMonitor().setTicksCount(relations.size());
-            for (Relation relation: relations) {
-                i++;
-                getProgressMonitor().setCustomText(tr("({0}/{1}): Downloading relation ''{2}''...", i, relations.size(),
-                        relation.getDisplayName(DefaultNameFormatter.getInstance())));
-                synchronized (this) {
-                    if (canceled) return;
-                    objectReader = new OsmServerObjectReader(relation.getPrimitiveId(), true /* full download */);
-                }
-                DataSet dataSet = objectReader.parseOsm(
-                        getProgressMonitor().createSubTaskMonitor(0, false)
-                );
-                if (dataSet == null)
+            MultiFetchServerObjectReader multiObjectReader;
+            synchronized (this) {
+                if (canceled)
                     return;
-                synchronized (this) {
-                    if (canceled) return;
-                    objectReader = null;
-                }
-                DataSetMerger merger = new DataSetMerger(allDownloads, dataSet);
-                merger.merge();
-                getProgressMonitor().worked(1);
+                multiObjectReader = MultiFetchServerObjectReader.create();
             }
-
+            multiObjectReader.setRecurseDownRelations(true).setRecurseDownAppended(false);
+            multiObjectReader.append(relations);
+            DataSet dataSet = multiObjectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
+            if (dataSet == null)
+                return;
+            synchronized (this) {
+                if (canceled)
+                    return;
+            }
+            new DataSetMerger(allDownloads, dataSet).merge();
             SwingUtilities.invokeAndWait(() -> {
                 layer.mergeFrom(allDownloads);
                 layer.onPostDownloadFromServer();
Index: src/org/openstreetmap/josm/gui/io/AbstractPrimitiveTask.java
===================================================================
--- src/org/openstreetmap/josm/gui/io/AbstractPrimitiveTask.java	(revision 15745)
+++ src/org/openstreetmap/josm/gui/io/AbstractPrimitiveTask.java	(working copy)
@@ -41,8 +41,7 @@
     protected OsmServerObjectReader objectReader;
 
     private boolean zoom;
-    private boolean downloadRelations;
-    private boolean fullRelation;
+    protected boolean fullRelation;
 
     protected AbstractPrimitiveTask(String title, OsmDataLayer layer) {
         this(title, new PleaseWaitProgressMonitor(title), layer);
@@ -70,14 +69,13 @@
     }
 
     /**
-     * Sets whether .
-     * @param downloadRelations {@code true} if
+     * Sets whether all members of the relation should be downloaded completely.
+     * @param downloadRelations ignored since xxx
      * @param fullRelation {@code true} if a full download is required,
      *                     i.e., a download including the immediate children of a relation.
      * @return {@code this}
      */
     public final AbstractPrimitiveTask setDownloadRelations(boolean downloadRelations, boolean fullRelation) {
-        this.downloadRelations = downloadRelations;
         this.fullRelation = fullRelation;
         return this;
     }
@@ -100,7 +98,7 @@
             synchronized (this) {
                 if (canceled)
                     return;
-                multiObjectReader = MultiFetchServerObjectReader.create();
+                multiObjectReader = MultiFetchServerObjectReader.create().setRecurseDownRelations(fullRelation);
             }
             initMultiFetchReader(multiObjectReader);
             theirDataSet = multiObjectReader.parseOsm(progressMonitor.createSubTaskMonitor(ProgressMonitor.ALL_TICKS, false));
@@ -110,7 +108,7 @@
             }
             new DataSetMerger(ds, theirDataSet).merge();
 
-            if (downloadRelations) {
+            if (fullRelation) {
                 loadIncompleteRelationMembers();
             }
 
Index: src/org/openstreetmap/josm/gui/io/DownloadPrimitivesTask.java
===================================================================
--- src/org/openstreetmap/josm/gui/io/DownloadPrimitivesTask.java	(revision 15745)
+++ src/org/openstreetmap/josm/gui/io/DownloadPrimitivesTask.java	(working copy)
@@ -5,11 +5,7 @@
 
 import java.util.List;
 
-import org.openstreetmap.josm.data.osm.Node;
-import org.openstreetmap.josm.data.osm.OsmPrimitive;
 import org.openstreetmap.josm.data.osm.PrimitiveId;
-import org.openstreetmap.josm.data.osm.Relation;
-import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.gui.progress.ProgressMonitor;
 import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
@@ -58,23 +54,7 @@
     @Override
     protected void initMultiFetchReader(MultiFetchServerObjectReader reader) {
         getProgressMonitor().indeterminateSubTask(tr("Initializing nodes to download ..."));
-        for (PrimitiveId id : ids) {
-            OsmPrimitive osm = layer.data.getPrimitiveById(id);
-            if (osm == null) {
-                switch (id.getType()) {
-                    case NODE:
-                        osm = new Node(id.getUniqueId());
-                        break;
-                    case WAY:
-                        osm = new Way(id.getUniqueId());
-                        break;
-                    case RELATION:
-                        osm = new Relation(id.getUniqueId());
-                        break;
-                    default: throw new AssertionError();
-                }
-            }
-            reader.append(osm);
-        }
+        reader.setRecurseDownRelations(fullRelation);
+        reader.appendIds(ids);
     }
 }
Index: src/org/openstreetmap/josm/gui/io/UpdatePrimitivesTask.java
===================================================================
--- src/org/openstreetmap/josm/gui/io/UpdatePrimitivesTask.java	(revision 15745)
+++ src/org/openstreetmap/josm/gui/io/UpdatePrimitivesTask.java	(working copy)
@@ -5,11 +5,9 @@
 
 import java.util.Collection;
 import java.util.Collections;
+import java.util.stream.Collectors;
 
-import org.openstreetmap.josm.data.osm.Node;
 import org.openstreetmap.josm.data.osm.OsmPrimitive;
-import org.openstreetmap.josm.data.osm.Relation;
-import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.layer.OsmDataLayer;
 import org.openstreetmap.josm.io.MultiFetchServerObjectReader;
 
@@ -32,41 +30,12 @@
     public UpdatePrimitivesTask(OsmDataLayer layer, Collection<? extends OsmPrimitive> toUpdate) {
         super(tr("Update objects"), layer);
         this.toUpdate = toUpdate != null ? toUpdate : Collections.<OsmPrimitive>emptyList();
+        // complete updated objects, e.g. download all members of relations
+        this.setDownloadRelations(false, true);
     }
 
-    protected void initMultiFetchReaderWithNodes(MultiFetchServerObjectReader reader) {
-        getProgressMonitor().indeterminateSubTask(tr("Initializing nodes to update ..."));
-        for (OsmPrimitive primitive : toUpdate) {
-            if (primitive instanceof Node && !primitive.isNew()) {
-                reader.append(primitive);
-            }
-        }
-    }
-
-    protected void initMultiFetchReaderWithWays(MultiFetchServerObjectReader reader) {
-        getProgressMonitor().indeterminateSubTask(tr("Initializing ways to update ..."));
-        for (OsmPrimitive primitive : toUpdate) {
-            if (primitive instanceof Way && !primitive.isNew()) {
-                // this also adds way nodes
-                reader.append(primitive);
-            }
-        }
-    }
-
-    protected void initMultiFetchReaderWithRelations(MultiFetchServerObjectReader reader) {
-        getProgressMonitor().indeterminateSubTask(tr("Initializing relations to update ..."));
-        for (OsmPrimitive primitive : toUpdate) {
-            if (primitive instanceof Relation && !primitive.isNew()) {
-                // this also adds relation members
-                reader.append(primitive);
-            }
-        }
-    }
-
     @Override
     protected void initMultiFetchReader(MultiFetchServerObjectReader reader) {
-        initMultiFetchReaderWithNodes(reader);
-        initMultiFetchReaderWithWays(reader);
-        initMultiFetchReaderWithRelations(reader);
+        reader.append(toUpdate.stream().filter(p -> !p.isNew()).collect(Collectors.toList()));
     }
 }
Index: src/org/openstreetmap/josm/io/MultiFetchOverpassObjectReader.java
===================================================================
--- src/org/openstreetmap/josm/io/MultiFetchOverpassObjectReader.java	(revision 15745)
+++ src/org/openstreetmap/josm/io/MultiFetchOverpassObjectReader.java	(working copy)
@@ -5,6 +5,7 @@
 import java.util.stream.Collectors;
 
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
+import org.openstreetmap.josm.tools.Logging;
 import org.openstreetmap.josm.tools.Utils;
 
 /**
@@ -12,16 +13,25 @@
  *
  * @since 9241
  */
-class MultiFetchOverpassObjectReader extends MultiFetchServerObjectReader {
+public class MultiFetchOverpassObjectReader extends MultiFetchServerObjectReader {
 
     @Override
     protected String buildRequestString(final OsmPrimitiveType type, Set<Long> idPackage) {
-        final String query = idPackage.stream()
-                .map(x -> type.getAPIName() + '(' + x + ");>;")
-                .collect(Collectors.joining("", "(", ");out meta;"));
+        final String query = type.getAPIName() + "(id:"
+                    + idPackage.stream().map(String::valueOf).collect(Collectors.joining(",")) + ");"
+                    + getRecurseOption(type) + "out meta;";
+        Logging.debug("{0} {1}", "Overpass query:", query);
         return "interpreter?data=" + Utils.encodeUrl(query);
     }
 
+    private String getRecurseOption(final OsmPrimitiveType type) {
+        if (type == OsmPrimitiveType.WAY)
+            return ">;";
+        if (type == OsmPrimitiveType.RELATION && recurseDownRelations)
+            return ">>;"; // >>; needed for sub relations
+        return "";
+    }
+
     @Override
     protected String getBaseUrl() {
         return OverpassDownloadReader.OVERPASS_SERVER.get();
@@ -33,4 +43,5 @@
         // accomplished using >; in the query string above
         return true;
     }
+
 }
Index: src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java
===================================================================
--- src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java	(revision 15745)
+++ src/org/openstreetmap/josm/io/MultiFetchServerObjectReader.java	(working copy)
@@ -73,6 +73,9 @@
     private Set<PrimitiveId> missingPrimitives;
     private final DataSet outputDataSet;
 
+    protected boolean recurseDownRelations;
+    protected boolean recurseDownAppended = true;
+
     /**
      * Constructs a {@code MultiFetchServerObjectReader}.
      */
@@ -159,7 +162,7 @@
      * @return this
      */
     public MultiFetchServerObjectReader appendNode(Node node) {
-        if (node == null) return this;
+        if (node == null || node.isNew()) return this;
         remember(node.getPrimitiveId());
         return this;
     }
@@ -171,11 +174,12 @@
      * @return this
      */
     public MultiFetchServerObjectReader appendWay(Way way) {
-        if (way == null) return this;
-        if (way.isNew()) return this;
-        for (Node node: !recursesDown() ? way.getNodes() : Collections.<Node>emptyList()) {
-            if (!node.isNew()) {
-                remember(node.getPrimitiveId());
+        if (way == null || way.isNew()) return this;
+        if (recurseDownAppended && !recursesDown()) {
+            for (Node node : way.getNodes()) {
+                if (!node.isNew()) {
+                    remember(node.getPrimitiveId());
+                }
             }
         }
         remember(way.getPrimitiveId());
@@ -189,18 +193,19 @@
      * @return this
      */
     protected MultiFetchServerObjectReader appendRelation(Relation relation) {
-        if (relation == null) return this;
-        if (relation.isNew()) return this;
+        if (relation == null || relation.isNew()) return this;
         remember(relation.getPrimitiveId());
-        for (RelationMember member : !recursesDown() ? relation.getMembers() : Collections.<RelationMember>emptyList()) {
-            // avoid infinite recursion in case of cyclic dependencies in relations
-            if (OsmPrimitiveType.from(member.getMember()) == OsmPrimitiveType.RELATION
-                    && relations.contains(member.getMember().getId())) {
-                continue;
+        if (recurseDownAppended && !recursesDown()) {
+            for (RelationMember member : relation.getMembers()) {
+                // avoid infinite recursion in case of cyclic dependencies in relations
+                if (OsmPrimitiveType.from(member.getMember()) == OsmPrimitiveType.RELATION
+                        && relations.contains(member.getMember().getId())) {
+                    continue;
+                }
+                if (!member.getMember().isIncomplete()) {
+                    append(member.getMember());
+                }
             }
-            if (!member.getMember().isIncomplete()) {
-                append(member.getMember());
-            }
         }
         return this;
     }
@@ -238,6 +243,38 @@
     }
 
     /**
+     * appends a list of {@link PrimitiveId} to the list of ids which will be fetched from the server.
+     *
+     * @param ids the list of primitive Ids (ignored, if null)
+     * @return this
+     * @since xxx
+     *
+     */
+    public MultiFetchServerObjectReader appendIds(Collection<PrimitiveId> ids) {
+        if (ids == null)
+            return this;
+        for (PrimitiveId id : ids) {
+            if (id.isNew()) continue;
+            switch (id.getType()) {
+            case NODE:
+                nodes.add(id.getUniqueId());
+                break;
+            case WAY:
+            case CLOSEDWAY:
+                ways.add(id.getUniqueId());
+                break;
+            case MULTIPOLYGON:
+            case RELATION:
+                relations.add(id.getUniqueId());
+                break;
+            default:
+                throw new AssertionError();
+            }
+        }
+        return this;
+    }
+
+    /**
      * extracts a subset of max {@link #MAX_IDS_PER_REQUEST} ids from <code>ids</code> and
      * replies the subset. The extracted subset is removed from <code>ids</code>.
      *
@@ -382,16 +419,48 @@
         progressMonitor.beginTask(trn("Downloading {0} object from ''{1}''",
                 "Downloading {0} objects from ''{1}''", n, n, OsmApi.getOsmApi().getBaseUrl()));
         try {
+            boolean ignoreAlreadyDownloaded = outputDataSet != null && outputDataSet.isEmpty();
             missingPrimitives = new HashSet<>();
+            Set<Long> origRelations = new LinkedHashSet<>(relations);
+            Set<Relation> incompleteRelations = new LinkedHashSet<>();
+            do {
+                if (isCanceled())
+                    return null;
+                incompleteRelations.clear();
+                fetchPrimitives(relations, OsmPrimitiveType.RELATION, progressMonitor);
+                if (outputDataSet != null && recurseDownRelations) {
+                    incompleteRelations = getIncompleteRelations(outputDataSet);
+                    relations.clear();
+                    append(incompleteRelations);
+                }
+            } while (!incompleteRelations.isEmpty());
+            if (recurseDownRelations && !recursesDown()) {
+                Set<PrimitiveId> members = new LinkedHashSet<>();
+                for (Relation r : outputDataSet.getRelations()) {
+                    for (OsmPrimitive p : r.getMemberPrimitives()) {
+                        if (!(p instanceof Relation)) {
+                            members.add(p.getPrimitiveId());
+                        }
+
+                    }
+                }
+                appendIds(members);
+            }
+            relations.clear();
+            relations.addAll(origRelations);
             if (isCanceled()) return null;
             fetchPrimitives(ways, OsmPrimitiveType.WAY, progressMonitor);
+            if (outputDataSet != null && ignoreAlreadyDownloaded) {
+                // avoid to download data that was already downloaded
+                nodes.removeAll(outputDataSet.getNodes().stream().filter(node -> !node.isIncomplete())
+                        .map(OsmPrimitive::getUniqueId).collect(Collectors.toList()));
+            }
             if (isCanceled()) return null;
             fetchPrimitives(nodes, OsmPrimitiveType.NODE, progressMonitor);
-            if (isCanceled()) return null;
-            fetchPrimitives(relations, OsmPrimitiveType.RELATION, progressMonitor);
             if (outputDataSet != null) {
                 outputDataSet.deleteInvisible();
             }
+
             return outputDataSet;
         } finally {
             progressMonitor.finishTask();
@@ -398,6 +467,21 @@
         }
     }
 
+    /** Collect incomplete relations in the dataset.
+     *
+     * @param ds the dataset
+     * @return set of incomplete relations
+     */
+    private Set<Relation> getIncompleteRelations(DataSet ds) {
+        Set<Relation> incomplete = new LinkedHashSet<>();
+        for (Relation r : ds.getRelations()) {
+            if (r.isIncomplete() && !relations.contains(r.getUniqueId())) {
+                incomplete.add(r);
+            }
+        }
+        return incomplete;
+    }
+
     /**
      * replies the set of ids of all primitives for which a fetch request to the
      * server was submitted but which are not available from the server (the server
@@ -419,6 +503,28 @@
     }
 
     /**
+     * Should downloaded relations be complete?
+     * @param recurseDownRelations true: yes, recurse down to retrieve the complete relation
+     * @return this
+     * @since xxx
+     */
+    public MultiFetchServerObjectReader setRecurseDownRelations(boolean recurseDownRelations) {
+        this.recurseDownRelations = recurseDownRelations;
+        return this;
+    }
+
+    /**
+     * Determine how appended objects are treated. By default, all children of an appended object are also appended.
+     * @param recurseAppended false: do not append known children of appended objects, i.e. all nodes of way and all members of a relation
+     * @return this
+     * @since xxx
+     */
+    public MultiFetchServerObjectReader setRecurseDownAppended(boolean recurseAppended) {
+        this.recurseDownAppended = recurseAppended;
+        return this;
+    }
+
+    /**
      * The class holding the results given by {@link Fetcher}.
      * It is only a wrapper of the resulting {@link DataSet} and the collection of {@link PrimitiveId} that could not have been loaded.
      */
@@ -614,4 +720,5 @@
             return result;
         }
     }
+
 }
Index: test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java
===================================================================
--- test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java	(revision 15745)
+++ test/functional/org/openstreetmap/josm/io/MultiFetchServerObjectReaderTest.java	(working copy)
@@ -42,9 +42,9 @@
 import org.openstreetmap.josm.data.osm.Way;
 import org.openstreetmap.josm.gui.progress.NullProgressMonitor;
 import org.openstreetmap.josm.spi.preferences.Config;
+import org.openstreetmap.josm.testutils.JOSMTestRules;
 
 import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
-import org.openstreetmap.josm.testutils.JOSMTestRules;
 
 /**
  * Unit tests of {@link MultiFetchServerObjectReader}.
Index: test/unit/org/openstreetmap/josm/io/MultiFetchOverpassObjectReaderTest.java
===================================================================
--- test/unit/org/openstreetmap/josm/io/MultiFetchOverpassObjectReaderTest.java	(revision 15745)
+++ test/unit/org/openstreetmap/josm/io/MultiFetchOverpassObjectReaderTest.java	(working copy)
@@ -6,7 +6,6 @@
 import java.util.Arrays;
 import java.util.TreeSet;
 
-import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
 import org.junit.Rule;
 import org.junit.Test;
 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
@@ -13,6 +12,8 @@
 import org.openstreetmap.josm.testutils.JOSMTestRules;
 import org.openstreetmap.josm.tools.Utils;
 
+import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
+
 /**
  * Unit tests of {@link MultiFetchOverpassObjectReader}.
  */
@@ -29,10 +30,26 @@
      * Test {@link MultiFetchOverpassObjectReader#buildRequestString}
      */
     @Test
-    public void testBuildRequestString() {
+    public void testBuildRequestWaysString() {
         String requestString = new MultiFetchOverpassObjectReader()
                 .buildRequestString(OsmPrimitiveType.WAY, new TreeSet<>(Arrays.asList(130L, 123L, 126L)));
-        assertEquals("interpreter?data=" + Utils.encodeUrl("(way(123);>;way(126);>;way(130);>;);out meta;"), requestString);
+        assertEquals("interpreter?data=" + Utils.encodeUrl("way(id:123,126,130);>;out meta;"), requestString);
     }
 
+    /**
+     * Test {@link MultiFetchOverpassObjectReader#buildRequestString}
+     */
+    @Test
+    public void testBuildRequestRelationsString() {
+        MultiFetchOverpassObjectReader reader = new MultiFetchOverpassObjectReader();
+        reader.setRecurseDownRelations(true);
+        String requestString = reader.buildRequestString(OsmPrimitiveType.RELATION,
+                new TreeSet<>(Arrays.asList(130L, 123L, 126L)));
+        assertEquals("interpreter?data=" + Utils.encodeUrl("relation(id:123,126,130);>>;out meta;"), requestString);
+        reader.setRecurseDownRelations(false);
+        requestString = reader.buildRequestString(OsmPrimitiveType.RELATION,
+                new TreeSet<>(Arrays.asList(130L, 123L, 126L)));
+        assertEquals("interpreter?data=" + Utils.encodeUrl("relation(id:123,126,130);out meta;"), requestString);
+    }
+
 }
