<commit_before>package org.bimserver.database.queries.om;

import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.bimserver.emf.PackageMetaData;
import org.bimserver.shared.QueryException;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EReference;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class JsonQueryObjectModelConverter {
	private PackageMetaData packageMetaData;

	public JsonQueryObjectModelConverter(PackageMetaData packageMetaData) {
		this.packageMetaData = packageMetaData;
	}
	
	public ObjectNode toJson(Query query) {
		ObjectMapper objectMapper = new ObjectMapper();
		ObjectNode queryNode = objectMapper.createObjectNode();
		Map<String, Include> defines = query.getDefines();
		ObjectNode definesNode = objectMapper.createObjectNode();
		queryNode.set("defines", definesNode);
		for (String key : defines.keySet()) {
			ObjectNode includeNode = objectMapper.createObjectNode();
			Include include = defines.get(key);
			ArrayNode fieldsNode = objectMapper.createArrayNode();
			for (EReference eReference : include.getFields()) {
				fieldsNode.add(eReference.getName());
			}
			definesNode.set(key, includeNode);
		}
		ArrayNode queryPartsNode = objectMapper.createArrayNode();
		queryNode.set("queries", queryPartsNode);
		for (QueryPart queryPart : query.getQueryParts()) {
			ObjectNode queryPartNode = objectMapper.createObjectNode();
			if (queryPart.hasTypes()) {
				ArrayNode typesNode = objectMapper.createArrayNode();
				queryPartNode.set("types", typesNode);
				for (EClass type : queryPart.getTypes()) {
					typesNode.add(type.getName());
				}
			}
			if (queryPart.hasOids()) {
				ArrayNode oidsNode = objectMapper.createArrayNode();
				queryPartNode.set("oids", oidsNode);
				for (long oid : queryPart.getOids()) {
					oidsNode.add(oid);
				}
			}
			queryPartsNode.add(queryPartNode);
		}
		return queryNode;
	}
	
	public Query parseJson(String queryName, ObjectNode fullQuery) throws QueryException {
		Query query = new Query(queryName, packageMetaData);
		if (fullQuery.has("defines")) {
			JsonNode defines = fullQuery.get("defines");
			if (defines instanceof ObjectNode) {
				parseDefines(query, (ObjectNode)fullQuery.get("defines"));
			} else {
				throw new QueryException("\"defines\" must be of type object");
			}
		}
		if (fullQuery.has("queries")) {
			JsonNode queriesNode = fullQuery.get("queries");
			if (queriesNode instanceof ArrayNode) {
				ArrayNode queries = (ArrayNode) fullQuery.get("queries");
				if (queries.size() == 0) {
					throw new QueryException("\"queries\" must contain at least one query");
				}
				for (int i=0; i<queries.size(); i++) {
					parseJsonQuery(query, (ObjectNode)queries.get(i));
				}
			} else {
				throw new QueryException("\"queries\" must be of type array");
			}
		} else if (fullQuery.has("query")) {
			JsonNode queryNode = fullQuery.get("query");
			if (queryNode instanceof ObjectNode) {
				parseJsonQuery(query, (ObjectNode) fullQuery.get("query"));
			} else {
				throw new QueryException("\"query\" must be of type object");
			}
		} else if (!fullQuery.has("defines")) {
			parseJsonQuery(query, fullQuery);
		}
		return query;
	}

	private void parseDefines(Query query, ObjectNode jsonNode) throws QueryException {
		Iterator<String> fieldNames = jsonNode.fieldNames();
		int i=0;
		// First pass, get all the name and create stub includes, using two passing to allow the usage of includes defined later in the structure
		while (fieldNames.hasNext()) {
			String fieldName = fieldNames.next();
			JsonNode defineNode = jsonNode.get(fieldName);
			if (defineNode instanceof ObjectNode) {
				Include include = new Include(packageMetaData);
				query.addDefine(fieldName, include);
			} else {
				throw new QueryException("\"defines\"[" + i + "] must be of type object");
			}
			i++;
		}
		// Second pass, actually construct the includes
		fieldNames = jsonNode.fieldNames();
		while (fieldNames.hasNext()) {
			String fieldName = fieldNames.next();
			JsonNode defineNode = jsonNode.get(fieldName);
			ObjectNode define = (ObjectNode)defineNode;
			parseInclude(query, define, query.getDefine(fieldName));
		}
	}
	
	private Include parseInclude(Query query, ObjectNode jsonNode, Include include) throws QueryException {
		if (include == null) {
			include = new Include(packageMetaData);
		}
		if (!jsonNode.has("type") && !jsonNode.has("types")) {
			throw new QueryException("includes require a \"type\" or \"types\" field " + jsonNode);
		}
		if (jsonNode.has("type")) {
			JsonNode typeNode = jsonNode.get("type");
			if (typeNode.isTextual()) {
				EClass eClass = packageMetaData.getEClassIncludingDependencies(typeNode.asText());
				if (eClass == null) {
					throw new QueryException("Cannot find type \"" + typeNode.asText() + "\"");
				}
				include.addType(eClass, false);
			} else {
				throw new QueryException("\"type\" field mst be of type string");
			}
		}
		if (jsonNode.has("types")) {
			JsonNode typesNode = jsonNode.get("types");
			if (typesNode instanceof ArrayNode) {
				ArrayNode types = (ArrayNode)typesNode;
				if (types.size() == 0) {
					throw new QueryException("\"types\" must have a least one element");
				}
				for (int i=0; i<types.size(); i++) {
					JsonNode typeNode = types.get(i);
					if (typeNode.isTextual()) {
						EClass eClass = packageMetaData.getEClass(typeNode.asText());
						include.addType(eClass, false);
					} else {
						throw new QueryException("\"types\"[" + i + "] field mst be of type string");
					}
				}
			} else {
				throw new QueryException("\"types\" must be of type array");
			}
		}
		if (jsonNode.has("outputType")) {
			JsonNode typeNode = jsonNode.get("outputType");
			if (typeNode.isTextual()) {
				EClass eClass = packageMetaData.getEClassIncludingDependencies(typeNode.asText());
				include.addOutputType(eClass);
			} else {
				throw new QueryException("\"outputType\" field mst be of type string");
			}
		}
		if (jsonNode.has("outputTypes")) {
			JsonNode typesNode = jsonNode.get("outputTypes");
			if (typesNode instanceof ArrayNode) {
				ArrayNode types = (ArrayNode)typesNode;
				if (types.size() == 0) {
					throw new QueryException("\"outputTypes\" must have a least one element");
				}
				for (int i=0; i<types.size(); i++) {
					JsonNode typeNode = types.get(i);
					if (typeNode.isTextual()) {
						EClass eClass = packageMetaData.getEClass(typeNode.asText());
						include.addOutputType(eClass);
					} else {
						throw new QueryException("\"outputTypes\"[" + i + "] field mst be of type string");
					}
				}
			} else {
				throw new QueryException("\"outputTypes\" must be of type array");
			}
		}
		if (jsonNode.has("field")) {
			JsonNode fieldNode = jsonNode.get("field");
			if (fieldNode.isTextual()) {
				include.addField(fieldNode.asText());
			} else {
				throw new QueryException("\"field\" must be of type string");
			}
		}
		if (jsonNode.has("fields")) {
			JsonNode fieldsNode = jsonNode.get("fields");
			if (fieldsNode instanceof ArrayNode) {
				ArrayNode fields = (ArrayNode)fieldsNode;
				for (int i=0; i<fields.size(); i++) {
					JsonNode fieldNode = fields.get(i);
					if (fieldNode.isTextual()) {
						include.addField(fieldNode.asText());
					} else {
						throw new QueryException("\"fields\"[" + i + "] must be of type string");
					}
				}
			} else {
				throw new QueryException("\"fields\" must be of type array");
			}
		}
		if (jsonNode.has("include")) {
			JsonNode includeNode = jsonNode.get("include");
			processSubInclude(query, include, includeNode);
		}
		if (jsonNode.has("includes")) {
			JsonNode includesNode = jsonNode.get("includes");
			if (includesNode instanceof ArrayNode) {
				ArrayNode includes = (ArrayNode)includesNode;
				for (int i=0; i<includes.size(); i++) {
					processSubInclude(query, include, includes.get(i));
				}
			} else {
				throw new QueryException("\"includes\" must be of type array");
			}
		}
		return include;
	}

	public Include getDefineFromFile(String includeName) throws QueryException {
		String namespaceString = includeName.substring(0, includeName.indexOf(":"));
		String singleIncludeName = includeName.substring(includeName.indexOf(":") + 1);
		URL resource = getClass().getResource("../json/" + namespaceString + ".json");
		if (resource == null) {
			throw new QueryException("Could not find '" + namespaceString + "' namespace in predefined queries");
		}
		ObjectMapper objectMapper = new ObjectMapper();
		objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
		try {
			ObjectNode predefinedQuery = objectMapper.readValue(resource, ObjectNode.class);
			JsonQueryObjectModelConverter converter = new JsonQueryObjectModelConverter(packageMetaData);
			Query namespace = converter.parseJson(includeName, predefinedQuery);
			Include define2 = namespace.getDefine(singleIncludeName);
			if (define2 == null) {
				throw new QueryException("Could not find '" + singleIncludeName + "' in defines in namespace " + namespace.getName());
			}
			return define2;
		} catch (JsonParseException e) {
			throw new QueryException(e);
		} catch (JsonMappingException e) {
			throw new QueryException(e);
		} catch (IOException e) {
			throw new QueryException(e);
		}
	}
	
	private void processSubInclude(Query query, CanInclude parentInclude, JsonNode includeNode) throws QueryException {
		if (includeNode instanceof ObjectNode) {
			ObjectNode innerInclude = (ObjectNode)includeNode;
			parentInclude.addInclude(parseInclude(query, innerInclude, null));
		} else if (includeNode.isTextual()) {
			String includeName = includeNode.asText();
			if (includeName.contains(":")) {
				parentInclude.addInclude(getDefineFromFile(includeName));
			} else {
				Include otherInclude = query.getDefine(includeName);
				if (otherInclude == null) {
					throw new QueryException("Cannot find define \"" + includeName + "\"");
				}
				parentInclude.addInclude(otherInclude);
			}
		} else {
			throw new QueryException("\"include\" must be of type object or string");
		}
	}

	private void parseJsonQuery(Query query, ObjectNode objectNode) throws QueryException {
		QueryPart queryPart = new QueryPart(packageMetaData);
		if (objectNode.has("type")) {
			JsonNode typeNode = objectNode.get("type");
			if (typeNode.isTextual()) {
				String type = typeNode.asText();
				addType(objectNode, queryPart, type);
			} else {
				throw new QueryException("\"type\" must be of type string");
			}
		}
		if (objectNode.has("types")) {
			JsonNode typesNode = objectNode.get("types");
			if (typesNode instanceof ArrayNode) {
				ArrayNode types = (ArrayNode)typesNode;
				for (int i=0; i<types.size(); i++) {
					JsonNode typeNode = types.get(i);
					if (typeNode.isTextual()) {
						String type = typeNode.asText();
						addType(objectNode, queryPart, type);
					} else {
						throw new QueryException("\"types\"[" + i + "] must be of type string");
					}
				}
			} else {
				throw new QueryException("\"types\" must be of type array");
			}
		}
		if (objectNode.has("includeAllFields") && objectNode.get("includeAllFields").asBoolean()) {
			queryPart.setIncludeAllFields(true);
		}
		if (objectNode.has("oid")) {
			JsonNode oidNode = objectNode.get("oid");
			if (oidNode.isNumber()) {
				queryPart.addOid(oidNode.asInt());
			} else {
				throw new QueryException("\"oid\" must be of type number");
			}
		}
		if (objectNode.has("oids")) {
			JsonNode oidsNode = objectNode.get("oids");
			if (oidsNode instanceof ArrayNode) {
				ArrayNode oids = (ArrayNode)oidsNode;
				for (int i=0; i<oids.size(); i++) {
					JsonNode oidNode = oids.get(i);
					if (oidNode.isNumber()) {
						queryPart.addOid(oidNode.asInt());
					} else {
						throw new QueryException("\"oids\"[" + i + "] must be of type number");
					}
				}
			} else {
				throw new QueryException("\"oids\" must be of type array");
			}
		}
		if (objectNode.has("guid")) {
			JsonNode guidNode = objectNode.get("guid");
			if (guidNode.isTextual()) {<commit_msg>Bugfix<commit_after>package org.bimserver.database.queries.om;

import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;

import org.bimserver.emf.PackageMetaData;
import org.bimserver.shared.QueryException;
import org.eclipse.emf.ecore.EClass;
import org.eclipse.emf.ecore.EReference;

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.JsonParser;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.ObjectNode;

public class JsonQueryObjectModelConverter {
	private PackageMetaData packageMetaData;

	public JsonQueryObjectModelConverter(PackageMetaData packageMetaData) {
		this.packageMetaData = packageMetaData;
	}
	
	public ObjectNode toJson(Query query) {
		ObjectMapper objectMapper = new ObjectMapper();
		ObjectNode queryNode = objectMapper.createObjectNode();
		Map<String, Include> defines = query.getDefines();
		ObjectNode definesNode = objectMapper.createObjectNode();
		queryNode.set("defines", definesNode);
		for (String key : defines.keySet()) {
			ObjectNode includeNode = objectMapper.createObjectNode();
			Include include = defines.get(key);
			ArrayNode fieldsNode = objectMapper.createArrayNode();
			for (EReference eReference : include.getFields()) {
				fieldsNode.add(eReference.getName());
			}
			definesNode.set(key, includeNode);
		}
		ArrayNode queryPartsNode = objectMapper.createArrayNode();
		queryNode.set("queries", queryPartsNode);
		for (QueryPart queryPart : query.getQueryParts()) {
			ObjectNode queryPartNode = objectMapper.createObjectNode();
			if (queryPart.hasTypes()) {
				ArrayNode typesNode = objectMapper.createArrayNode();
				queryPartNode.set("types", typesNode);
				for (EClass type : queryPart.getTypes()) {
					typesNode.add(type.getName());
				}
			}
			if (queryPart.hasOids()) {
				ArrayNode oidsNode = objectMapper.createArrayNode();
				queryPartNode.set("oids", oidsNode);
				for (long oid : queryPart.getOids()) {
					oidsNode.add(oid);
				}
			}
			queryPartsNode.add(queryPartNode);
		}
		return queryNode;
	}
	
	public Query parseJson(String queryName, ObjectNode fullQuery) throws QueryException {
		Query query = new Query(queryName, packageMetaData);
		if (fullQuery.has("defines")) {
			JsonNode defines = fullQuery.get("defines");
			if (defines instanceof ObjectNode) {
				parseDefines(query, (ObjectNode)fullQuery.get("defines"));
			} else {
				throw new QueryException("\"defines\" must be of type object");
			}
		}
		if (fullQuery.has("queries")) {
			JsonNode queriesNode = fullQuery.get("queries");
			if (queriesNode instanceof ArrayNode) {
				ArrayNode queries = (ArrayNode) fullQuery.get("queries");
				if (queries.size() == 0) {
					throw new QueryException("\"queries\" must contain at least one query");
				}
				for (int i=0; i<queries.size(); i++) {
					parseJsonQuery(query, (ObjectNode)queries.get(i));
				}
			} else {
				throw new QueryException("\"queries\" must be of type array");
			}
		} else if (fullQuery.has("query")) {
			JsonNode queryNode = fullQuery.get("query");
			if (queryNode instanceof ObjectNode) {
				parseJsonQuery(query, (ObjectNode) fullQuery.get("query"));
			} else {
				throw new QueryException("\"query\" must be of type object");
			}
		} else if (!fullQuery.has("defines")) {
			parseJsonQuery(query, fullQuery);
		}
		return query;
	}

	private void parseDefines(Query query, ObjectNode jsonNode) throws QueryException {
		Iterator<String> fieldNames = jsonNode.fieldNames();
		int i=0;
		// First pass, get all the name and create stub includes, using two passing to allow the usage of includes defined later in the structure
		while (fieldNames.hasNext()) {
			String fieldName = fieldNames.next();
			JsonNode defineNode = jsonNode.get(fieldName);
			if (defineNode instanceof ObjectNode) {
				Include include = new Include(packageMetaData);
				query.addDefine(fieldName, include);
			} else {
				throw new QueryException("\"defines\"[" + i + "] must be of type object");
			}
			i++;
		}
		// Second pass, actually construct the includes
		fieldNames = jsonNode.fieldNames();
		while (fieldNames.hasNext()) {
			String fieldName = fieldNames.next();
			JsonNode defineNode = jsonNode.get(fieldName);
			ObjectNode define = (ObjectNode)defineNode;
			parseInclude(query, define, query.getDefine(fieldName));
		}
	}
	
	private Include parseInclude(Query query, ObjectNode jsonNode, Include include) throws QueryException {
		if (include == null) {
			include = new Include(packageMetaData);
		}
		if (!jsonNode.has("type") && !jsonNode.has("types")) {
			throw new QueryException("includes require a \"type\" or \"types\" field " + jsonNode);
		}
		if (jsonNode.has("type")) {
			JsonNode typeNode = jsonNode.get("type");
			if (typeNode.isTextual()) {
				EClass eClass = packageMetaData.getEClassIncludingDependencies(typeNode.asText());
				if (eClass == null) {
					throw new QueryException("Cannot find type \"" + typeNode.asText() + "\"");
				}
				include.addType(eClass, false);
			} else {
				throw new QueryException("\"type\" field mst be of type string");
			}
		}
		if (jsonNode.has("types")) {
			JsonNode typesNode = jsonNode.get("types");
			if (typesNode instanceof ArrayNode) {
				ArrayNode types = (ArrayNode)typesNode;
				if (types.size() == 0) {
					throw new QueryException("\"types\" must have a least one element");
				}
				for (int i=0; i<types.size(); i++) {
					JsonNode typeNode = types.get(i);
					if (typeNode.isTextual()) {
						EClass eClass = packageMetaData.getEClass(typeNode.asText());
						include.addType(eClass, false);
					} else {
						throw new QueryException("\"types\"[" + i + "] field mst be of type string");
					}
				}
			} else {
				throw new QueryException("\"types\" must be of type array");
			}
		}
		if (jsonNode.has("outputType")) {
			JsonNode typeNode = jsonNode.get("outputType");
			if (typeNode.isTextual()) {
				EClass eClass = packageMetaData.getEClassIncludingDependencies(typeNode.asText());
				include.addOutputType(eClass);
			} else {
				throw new QueryException("\"outputType\" field mst be of type string");
			}
		}
		if (jsonNode.has("outputTypes")) {
			JsonNode typesNode = jsonNode.get("outputTypes");
			if (typesNode instanceof ArrayNode) {
				ArrayNode types = (ArrayNode)typesNode;
				if (types.size() == 0) {
					throw new QueryException("\"outputTypes\" must have a least one element");
				}
				for (int i=0; i<types.size(); i++) {
					JsonNode typeNode = types.get(i);
					if (typeNode.isTextual()) {
						EClass eClass = packageMetaData.getEClass(typeNode.asText());
						include.addOutputType(eClass);
					} else {
						throw new QueryException("\"outputTypes\"[" + i + "] field mst be of type string");
					}
				}
			} else {
				throw new QueryException("\"outputTypes\" must be of type array");
			}
		}
		if (jsonNode.has("field")) {
			JsonNode fieldNode = jsonNode.get("field");
			if (fieldNode.isTextual()) {
				include.addField(fieldNode.asText());
			} else {
				throw new QueryException("\"field\" must be of type string");
			}
		}
		if (jsonNode.has("fields")) {
			JsonNode fieldsNode = jsonNode.get("fields");
			if (fieldsNode instanceof ArrayNode) {
				ArrayNode fields = (ArrayNode)fieldsNode;
				for (int i=0; i<fields.size(); i++) {
					JsonNode fieldNode = fields.get(i);
					if (fieldNode.isTextual()) {
						include.addField(fieldNode.asText());
					} else {
						throw new QueryException("\"fields\"[" + i + "] must be of type string");
					}
				}
			} else {
				throw new QueryException("\"fields\" must be of type array");
			}
		}
		if (jsonNode.has("include")) {
			JsonNode includeNode = jsonNode.get("include");
			processSubInclude(query, include, includeNode);
		}
		if (jsonNode.has("includes")) {
			JsonNode includesNode = jsonNode.get("includes");
			if (includesNode instanceof ArrayNode) {
				ArrayNode includes = (ArrayNode)includesNode;
				for (int i=0; i<includes.size(); i++) {
					processSubInclude(query, include, includes.get(i));
				}
			} else {
				throw new QueryException("\"includes\" must be of type array");
			}
		}
		return include;
	}

	public Include getDefineFromFile(String includeName) throws QueryException {
		String namespaceString = includeName.substring(0, includeName.indexOf(":"));
		String singleIncludeName = includeName.substring(includeName.indexOf(":") + 1);
		URL resource = getClass().getResource("../json/" + namespaceString + ".json");
		if (resource == null) {
			throw new QueryException("Could not find '" + namespaceString + "' namespace in predefined queries");
		}
		ObjectMapper objectMapper = new ObjectMapper();
		objectMapper.configure(JsonParser.Feature.ALLOW_UNQUOTED_FIELD_NAMES, true);
		try {
			ObjectNode predefinedQuery = objectMapper.readValue(resource, ObjectNode.class);
			JsonQueryObjectModelConverter converter = new JsonQueryObjectModelConverter(packageMetaData);
			Query namespace = converter.parseJson(includeName, predefinedQuery);
			Include define2 = namespace.getDefine(singleIncludeName);
			if (define2 == null) {
				throw new QueryException("Could not find '" + singleIncludeName + "' in defines in namespace " + namespace.getName());
			}
			return define2;
		} catch (JsonParseException e) {
			throw new QueryException(e);
		} catch (JsonMappingException e) {
			throw new QueryException(e);
		} catch (IOException e) {
			throw new QueryException(e);
		}
	}
	
	private void processSubInclude(Query query, CanInclude parentInclude, JsonNode includeNode) throws QueryException {
		if (includeNode instanceof ObjectNode) {
			ObjectNode innerInclude = (ObjectNode)includeNode;
			parentInclude.addInclude(parseInclude(query, innerInclude, null));
		} else if (includeNode.isTextual()) {
			String includeName = includeNode.asText();
			if (includeName.contains(":")) {
				parentInclude.addInclude(getDefineFromFile(includeName));
			} else {
				Include otherInclude = query.getDefine(includeName);
				if (otherInclude == null) {
					throw new QueryException("Cannot find define \"" + includeName + "\"");
				}
				parentInclude.addInclude(otherInclude);
			}
		} else {
			throw new QueryException("\"include\" must be of type object or string");
		}
	}

	private void parseJsonQuery(Query query, ObjectNode objectNode) throws QueryException {
		QueryPart queryPart = new QueryPart(packageMetaData);
		if (objectNode.has("type")) {
			JsonNode typeNode = objectNode.get("type");
			if (typeNode.isTextual()) {
				String type = typeNode.asText();
				addType(objectNode, queryPart, type);
			} else {
				throw new QueryException("\"type\" must be of type string");
			}
		}
		if (objectNode.has("types")) {
			JsonNode typesNode = objectNode.get("types");
			if (typesNode instanceof ArrayNode) {
				ArrayNode types = (ArrayNode)typesNode;
				for (int i=0; i<types.size(); i++) {
					JsonNode typeNode = types.get(i);
					if (typeNode.isTextual()) {
						String type = typeNode.asText();
						addType(objectNode, queryPart, type);
					} else {
						throw new QueryException("\"types\"[" + i + "] must be of type string");
					}
				}
			} else {
				throw new QueryException("\"types\" must be of type array");
			}
		}
		if (objectNode.has("includeAllFields") && objectNode.get("includeAllFields").asBoolean()) {
			queryPart.setIncludeAllFields(true);
		}
		if (objectNode.has("oid")) {
			JsonNode oidNode = objectNode.get("oid");
			if (oidNode.isNumber()) {
				queryPart.addOid(oidNode.asLong());
			} else {
				throw new QueryException("\"oid\" must be of type number");
			}
		}
		if (objectNode.has("oids")) {
			JsonNode oidsNode = objectNode.get("oids");
			if (oidsNode instanceof ArrayNode) {
				ArrayNode oids = (ArrayNode)oidsNode;
				for (int i=0; i<oids.size(); i++) {
					JsonNode oidNode = oids.get(i);
					if (oidNode.isNumber()) {
						queryPart.addOid(oidNode.asLong());
					} else {
						throw new QueryException("\"oids\"[" + i + "] must be of type number");
					}
				}
			} else {
				throw new QueryException("\"oids\" must be of type array");
			}
		}
		if (objectNode.has("guid")) {
			JsonNode guidNode = objectNode.get("guid");
			if (guidNode.isTextual()) {
{'fraction_non_alphanumeric': 0.1347880349450461, 'fraction_numerical': 0.0012882966302991264, 'mean_word_length': 3.9393517597931993, 'pattern_counts': {'":': 6, '<': 21, '<?xml version=': 0, '>': 7, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 14}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5631679', 'n_tokens_mistral': 8919, 'n_tokens_neox': 7451, 'n_words': 1808}