private boolean insertExe(DescriptionManager manager, ExecutableRecord exeRecord)
			throws ElasticException, LSHException, DatabaseNonFatalException {
		RowKeyElastic eKey = updateKey(manager, exeRecord);
		String exeId = eKey.generateExeIdString();

		if (!insertExecutableRecord(exeRecord, exeId)) {	// Try to insert the executable
			JsonObject exeObj = queryMd5ExeMatch(exeRecord.getMd5());
			if (exeObj != null) {		// Try to retrieve the previous version
				ExecutableRecord oldrec = makeExecutableRecordTemp(exeObj);
				int cmp = oldrec.compareMetadata(exeRecord);
				if (cmp != 0) {
					String fatalerror =
						FunctionDatabase.constructFatalError(cmp, exeRecord, oldrec);
					if (fatalerror != null) {
						throw new LSHException(fatalerror);
					}
					throw new DatabaseNonFatalException(
						FunctionDatabase.constructNonfatalError(cmp, exeRecord, oldrec));
				}
				throw new DatabaseNonFatalException(
					exeRecord.getNameExec() + " is already ingested");
			}
			return false;		// Indicate this executable already inserted
		}
		int newIds = 0;
		long baseId = 0;
		Iterator<FunctionDescription> iter = manager.listFunctions(exeRecord);
		while (iter.hasNext()) {			// Count the functions to insert for this executable
			iter.next();
			newIds += 1;
		}
		baseId = allocateFunctionIndexSpace(newIds);		// Allocated the ids we will need
		iter = manager.listFunctions(exeRecord);
		while (iter.hasNext()) {
			manager.setFunctionDescriptionId(iter.next(), new RowKeyElastic(baseId));	// Set the (allocated) ids
			baseId += 1;
		}
		// Collect/dedup vectors and update SignatureRecords with vector ids, before writing FunctionDescriptions
		Set<IdHistogram> vectorContainer =
			IdHistogram.collectVectors(manager, manager.listFunctions(exeRecord));

		iter = manager.listFunctions(exeRecord);
		// Write the FunctionDescriptions now that vector ids are filled in
		while (iter.hasNext()) {
			insertFunctionRange(manager, exeRecord, eKey, exeId, iter, MAX_FUNCTION_BULK);
		}

		// Create/update the vector documents
		Iterator<IdHistogram> viter = vectorContainer.iterator();
		while (viter.hasNext()) {
			insertVectorRange(viter, MAX_VECTOR_BULK);
		}
		return true;
	}
Insert an executable and all of the functions it contains into the database.
The executable must not be a library.  If the executable has been inserted before,
a non-fatal exception is thrown if the previous executable and this new one have
exactly matching meta-data.  A fatal exception is thrown if meta-data has changed.
All functions (FunctionDescriptions) associated with the executable are inserted,
including their feature vectors.
@param manager is the container of the executable
@param exeRecord is the executable to insert
@return false only if the insert was unsuccessful but a previous executable could not be recovered
@throws ElasticException for communication problems with the server
@throws LSHException if update differences cannot reconciled 
@throws DatabaseNonFatalException for non-fatal updates that can't be executed
Ghidra/Features/BSim/src/main/java/ghidra/features/bsim/query/elastic/ElasticDatabase.java