private void queryExecutableRecordById(DescriptionManager manager,
			Iterator<RowKeyElastic> iter1, Iterator<RowKeyElastic> iter2, int maxDocuments)
			throws ElasticException, LSHException {
		StringBuilder buffer = new StringBuilder();
		if (!iter1.hasNext()) {
			return;		// Nothing to do
		}
		String path = '/' + repository + "_executable/_msearch";
		int count = 0;
		for (int i = 0; i < maxDocuments; ++i) {
			String exeId = iter1.next().generateExeIdString();
			buffer.append("{}\n");		// Keep default index and type
			buffer.append("{ \"query\": { \"bool\": { \"filter\": { \"term\": { \"_id\": \"");
			buffer.append(exeId);
			buffer.append("\" }}}}}\n");
			count += 1;
			if (!iter1.hasNext()) {
				break;
			}
		}
		JsonObject bulkobj = connection.executeBulk(path, buffer.toString());
		JsonArray responses = (JsonArray) bulkobj.get("responses");
		for (int i = 0; i < count; ++i) {
			JsonObject subquery = (JsonObject) responses.get(i);
			JsonElement hits = subquery.get("hits");
			if (ElasticConnection.isNull(hits)) {
				throw new ElasticException("Multi-search for exe records failed");
			}
			JsonObject totalRec = (JsonObject) ((JsonObject) hits).get("total");
			long total = totalRec.get("value").getAsLong();
			if (total != 1) {
				throw new ElasticException("Could not recover unique executable via id");
			}
		}
		for (int i = 0; i < count; ++i) {
			JsonObject subquery = (JsonObject) responses.get(i);
			JsonObject hits = (JsonObject) subquery.get("hits");
			JsonArray hitsArray = (JsonArray) hits.get("hits");
			hits = (JsonObject) hitsArray.get(0);
			ExecutableRecord newExe = makeExecutableRecord(manager, hits);
			RowKey rowKey = iter2.next();
			manager.cacheExecutableByRow(newExe, rowKey);
		}
	}
Retrieve a sequence of ExecutableRecords by id. The ids are queried in bulk,
up to a given maximum number of documents to fetch. Two iterators pointing to
the same list of RowKeys must be provided.  One is used while building the query
document. The other is used to assign the matching RowKey to the new ExecutableRecords
@param manager is the container to store new ExecutableRecord results
@param iter1 is an iterator over ids
@param iter2 is a copy of the iterator over ids
@param maxDocuments is the maximum number of ids to fetch in this request
@throws ElasticException for communication problems with the server
@throws LSHException for problems adding new records to the container
Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/elastic/ElasticDatabase.java