/****************************************************************************** * * Copyright 2014 Paphus Solutions Inc. * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.eclipse.org/legal/epl-v10.html * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. * ******************************************************************************/ package org.botlibre.sense.wikidata; import java.io.IOException; import java.io.InputStreamReader; import java.io.Reader; import java.io.StringWriter; import java.net.URL; import java.net.URLEncoder; import java.util.ArrayList; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; import net.sf.json.JSON; import net.sf.json.JSONArray; import net.sf.json.JSONObject; import net.sf.json.JSONSerializer; import org.botlibre.Bot; import org.botlibre.api.knowledge.Network; import org.botlibre.api.knowledge.Vertex; import org.botlibre.knowledge.Primitive; import org.botlibre.sense.http.Http; import org.botlibre.util.Utils; /** * This sense queries and loads data from Wikidata. */ public class Wikidata extends Http { //implements DiscoverySense { protected int depth = 1; protected List<String> discoveryIgnoreWords; protected static Set<String> globalExcludedProperties; protected Set<String> excludedProperties; protected static Map<String, String> globalPropertiesMap; protected Map<String, String> propertiesMap; static { globalExcludedProperties = new HashSet<String>(); globalExcludedProperties.add("P434"); globalExcludedProperties.add("P271"); globalExcludedProperties.add("P227"); globalExcludedProperties.add("P269"); globalExcludedProperties.add("P268"); globalExcludedProperties.add("P349"); globalExcludedProperties.add("P691"); globalExcludedProperties.add("P345"); globalExcludedProperties.add("P646"); globalExcludedProperties.add("P1280"); globalExcludedProperties.add("P935"); globalExcludedProperties.add("P214"); globalExcludedProperties.add("P1151"); globalExcludedProperties.add("P213"); globalExcludedProperties.add("P906"); globalExcludedProperties.add("P396"); globalExcludedProperties.add("P244"); globalExcludedProperties.add("P910"); globalExcludedProperties.add("P866"); globalExcludedProperties.add("P373"); globalExcludedProperties.add("P1375"); globalExcludedProperties.add("P1472"); globalExcludedProperties.add("P949"); globalExcludedProperties.add("P1284"); globalExcludedProperties.add("P1343"); globalExcludedProperties.add("P1207"); globalExcludedProperties.add("P1670"); globalExcludedProperties.add("P409"); globalExcludedProperties.add("P1296"); globalExcludedProperties.add("P1185"); globalExcludedProperties.add("P1309"); globalExcludedProperties.add("P1006"); globalExcludedProperties.add("P1005"); globalExcludedProperties.add("P998"); globalExcludedProperties.add("P1368"); globalExcludedProperties.add("P1185"); globalExcludedProperties.add("P1890"); globalExcludedProperties.add("P1417"); globalExcludedProperties.add("P1695"); globalExcludedProperties.add("P1263"); globalExcludedProperties.add("P1749"); globalExcludedProperties.add("P1842"); globalExcludedProperties.add("P948"); globalExcludedProperties.add("P982"); globalExcludedProperties.add("P1188"); globalExcludedProperties.add("P1465"); globalExcludedProperties.add("P1792"); globalExcludedProperties.add("P1464"); globalExcludedProperties.add("P1740"); globalExcludedProperties.add("P1566"); globalExcludedProperties.add("P982"); globalExcludedProperties.add("P948"); globalExcludedProperties.add("P1465"); globalExcludedProperties.add("P1464"); globalExcludedProperties.add("P1740"); globalExcludedProperties.add("P1566"); } static { globalPropertiesMap = new HashMap<String, String>(); globalPropertiesMap.put("P21", "gender"); } public static String URL_PREFIX = "http://www.wikidata.org/"; public static int SLEEP = 100; // Sleep between type fetches, to avoid Wikidata throttle. public Wikidata() { this.excludedProperties = globalExcludedProperties; this.propertiesMap = globalPropertiesMap; this.discoveryIgnoreWords = new ArrayList<String>(); this.discoveryIgnoreWords.add("there"); this.discoveryIgnoreWords.add("up"); this.discoveryIgnoreWords.add("going"); } @Override public void awake() { super.awake(); Http http = (Http)getBot().awareness().getSense(Http.class.getName()); http.getDomains().put("www.wikidata.org", this); } /** * Get and process the URL. */ @Override public void input(Object input, Network network) throws Exception { if (!isEnabled()) { return; } log("Input", Level.INFO, input); URL url = (URL)input; String domain = url.getPath(); if (domain.length() > 5 && domain.substring(0, 5).equals("/view")) { domain = domain.substring(5, domain.length()); } processId(domain, this.depth, false, "", network, new HashMap<String, Vertex>()); } /** * Search for the best object describing the keywords. */ public Vertex processSearch(String keywords, int cascade, boolean fork, String filter, Network network, Map<String, Vertex> processed) { log("Processing search", Level.INFO, keywords); // First query the object's types. JSONObject json = null; try { json = (JSONObject)processQuery("https://www.wikidata.org/w/api.php?action=wbsearchentities&format=json&limit=1&language=en&search=" + URLEncoder.encode(keywords, "UTF-8")); } catch (IOException exception) { log("https request failed", Level.WARNING, exception); return null; } if (json.isNullObject()) { return null; } // Get the first result. // TODO most conscious result? or top three? JSONArray results = json.getJSONArray("search"); if (results.isEmpty()) { return null; } JSONObject first = results.getJSONObject(0); String id = first.getString("id"); return processId(id, cascade, fork, filter, network, processed); } /** * Lookup the wikidata item by id, and load its data. */ @SuppressWarnings({ "unchecked", "rawtypes" }) public Vertex processId(String id, final int cascade, boolean fork, String filter, Network network, Map<String, Vertex> processed) { Vertex object = processed.get(id); if (object != null) { return object; } log("Processing Id", Level.INFO, id); // First query the object's types. JSONObject json = null; String query = "https://www.wikidata.org/w/api.php?action=wbgetentities&languages=en&format=json&ids=" + id; if (fork || cascade < 0) { query = query + "&props=labels|descriptions"; if (filter != null && !filter.isEmpty()) { query = query + "|claims"; } } else { query = query + "&props=labels|descriptions|claims"; } try { json = (JSONObject)processQuery(query); } catch (IOException exception) { log("https request failed", Level.WARNING, exception.toString()); return null; } json = json.getJSONObject("entities"); if (json == null) { return null; } json = json.getJSONObject(id); if (json == null) { return null; } object = fetchDescription(json, cascade, network, processed); if (object == null) { log("Id not found", Level.FINE, id); return null; } network.save(); if (filter != null && !filter.isEmpty()) { // Also fetch filter. JSONObject properties = (JSONObject)json.get("claims"); if (properties != null) { List<String> keys = new ArrayList<String>(properties.keySet()); Map<String, String> propertyMap = fetchPropertyLabels(keys, network, processed); String propertyId = null; for (Iterator<Map.Entry<String, String>> iterator = propertyMap.entrySet().iterator(); iterator.hasNext(); ) { Map.Entry<String, String> entry = iterator.next(); if (!entry.getValue().equals(filter)) { iterator.remove(); } else { propertyId = entry.getKey(); } } if (propertyId != null) { List<String> ids = extractPropertyValueIds(properties, propertyMap, network, processed); Map<String, String> valueMap = fetchPropertyLabels(ids, network, processed); List values = extractPropertyValues(properties.get(propertyId), valueMap, cascade, network, processed); for (Object value : values) { Vertex valueVertex = null; if (value instanceof String) { valueVertex = network.createObject((String)value); valueVertex.addRelationship(Primitive.INSTANTIATION, Primitive.THING); } else if (value instanceof Vertex) { valueVertex = (Vertex)value; } else { valueVertex = network.createVertex(value); valueVertex.addRelationship(Primitive.INSTANTIATION, Primitive.THING); } // Add relationship. Vertex relationshipVertex = network.createPrimitive(filter); relationshipVertex.addRelationship(Primitive.INSTANTIATION, Primitive.THING); relationshipVertex.addRelationship(Primitive.INSTANTIATION, Primitive.ACTION); relationshipVertex.addRelationship(Primitive.INSTANTIATION, Primitive.RELATIONSHIP); object.addRelationship(relationshipVertex, valueVertex); log("Processing reltionship:", Bot.FINE, filter, object, relationshipVertex, valueVertex); } } } } log("Processed", Level.INFO, id, object); network.save(); if (cascade >= 0) { if (fork) { // Fork the rest to avoid delay. final Network threadNetwork = network.getBot().memory().newMemory(); final Vertex threadObject = threadNetwork.createVertex(object); final Map<String, Vertex> threadProcessed = new HashMap<String, Vertex>(); final String threadId = id; Thread thread = new Thread() { public void run() { int attempt = 0; Exception failure = null; while (attempt < RETRY) { try { Utils.sleep(500); JSONObject jsonDetails = null; try { jsonDetails = (JSONObject)processQuery("https://www.wikidata.org/w/api.php?action=wbgetentities&format=json&languages=en&props=labels|descriptions|aliases|claims&ids=" + threadId); } catch (IOException exception) { log("https request failed", Level.WARNING, exception.toString()); return; } jsonDetails = jsonDetails.getJSONObject("entities"); if (jsonDetails == null) { return; } jsonDetails = jsonDetails.getJSONObject(threadId.toUpperCase()); if (jsonDetails == null) { return; } fetchDetails(threadObject, jsonDetails, cascade, threadProcessed, threadNetwork); break; } catch (Exception failed) { failure = failed; log(failed.toString(), Level.WARNING); log("Retrying", Level.WARNING); } } if (attempt == RETRY) { log("Retry failed", Level.WARNING); log(failure); } } }; thread.start(); } // Fetch filter properties. fetchDetails(object, json, cascade, processed, network); } return object; } /** * Lookup the wikidata properites by id. */ public Map<String, String> fetchPropertyLabels(List<String> properties, Network network, Map<String, Vertex> processed) { log("Fetching properties", Level.FINE, properties); Map<String, String> values = new HashMap<String, String>(properties.size()); if (properties.isEmpty()) { return values; } int start = 0; int end = 50; // Wikidata API has 50 id limit, so must page. while (start < properties.size()) { List<String> page = properties.subList(start, Math.min(end, properties.size())); StringWriter writer = new StringWriter(); boolean first = true; for (String property : page) { if (this.excludedProperties.contains(property)) { continue; } if (!first) { writer.write("|"); } else { first = false; } writer.write(property); } String ids = writer.toString(); start = end; end = end + 50; if (ids.isEmpty()) { continue; } JSONObject json = null; String query = "https://www.wikidata.org/w/api.php?action=wbgetentities&languages=en&format=json&props=labels&ids=" + ids; try { json = (JSONObject)processQuery(query); } catch (IOException exception) { log("https request failed", Level.WARNING, exception.toString()); return values; } Object entities = json.get("entities"); if (!(entities instanceof JSONObject)) { return values; } json = (JSONObject)entities; for (String property : page) { String mapping = this.propertiesMap.get(property); if (mapping != null) { values.put(property, mapping); } else { JSONObject data = json.getJSONObject(property); if (data.isNullObject()) { continue; } List<String> names = extractText(data.get("labels")); if (names.size() > 0) { values.put(property, names.get(0)); } } } } return values; } /** * Extract the language text from the JSON object. */ public List<String> extractText(Object data) { List<String> values = new ArrayList<String>(); if (!(data instanceof JSONObject)) { return values; } Object en = ((JSONObject)data).get("en"); if (en == null) { return values; } else if (en instanceof JSONObject) { values.add(String.valueOf(((JSONObject)en).get("value"))); } else if (en instanceof JSONArray) { for (Object value : ((JSONArray)en)) { values.add(String.valueOf(((JSONObject)value).get("value"))); } } return values; } /** * Extract the relevant data from the wikidata claims. */ public List<Object> extractPropertyValues(Object data, Map<String, String> valueMap, int cascade, Network network, Map<String, Vertex> processed) { List<Object> values = new ArrayList<Object>(); Set<Object> valuesSet = new HashSet<Object>(); if (data instanceof JSONArray) { for (Object value : ((JSONArray)data)) { if (value instanceof JSONObject) { value = ((JSONObject)value).get("mainsnak"); if (value instanceof JSONObject) { value = ((JSONObject)value).get("datavalue"); if (value instanceof JSONObject) { value = ((JSONObject)value).get("value"); if (value instanceof JSONObject) { Object id = ((JSONObject)value).get("numeric-id"); if (id instanceof Integer) { String qid = "Q" + id; if (cascade > 0) { Vertex nested = processId((String)qid, cascade - 1, false, "", network, processed); if (!valuesSet.contains(nested)) { valuesSet.add(nested); values.add(nested); } } else if (valueMap != null) { String label = valueMap.get(qid); if (label != null) { if (!valuesSet.contains(label)) { valuesSet.add(label); values.add(label); } } } continue; } Object propertyValue = ((JSONObject)value).get("text"); if (propertyValue == null) { propertyValue = ((JSONObject)value).get("time"); if (propertyValue instanceof String) { try { propertyValue = Utils.parseDate(((String)propertyValue).substring(1,((String) propertyValue).indexOf('T'))); } catch (Exception exception) {} } } else if (propertyValue instanceof String) { // Text is normally a name or word. propertyValue = network.createWord((String)propertyValue); } if (propertyValue != null) { if (!valuesSet.contains(propertyValue)) { valuesSet.add(propertyValue); values.add(propertyValue); } } } } } } } } return values; } /** * Extract the ids from the wikidata claims. */ public List<String> extractPropertyValueIds(JSONObject data, Map<String, String> propertyMap, Network network, Map<String, Vertex> processed) { List<String> values = new ArrayList<String>(); Set<String> valuesSet = new HashSet<String>(); for (Map.Entry<String, String> property : propertyMap.entrySet()) { Object propertyValue = data.get(property.getKey()); if (propertyValue instanceof JSONArray) { for (Object value : ((JSONArray)propertyValue)) { if (value instanceof JSONObject) { value = ((JSONObject)value).get("mainsnak"); if (value instanceof JSONObject) { value = ((JSONObject)value).get("datavalue"); if (value instanceof JSONObject) { value = ((JSONObject)value).get("value"); if (value instanceof JSONObject) { Object id = ((JSONObject)value).get("numeric-id"); if (id instanceof Integer) { String nested = "Q" + id; if (!valuesSet.contains(nested)) { valuesSet.add(nested); values.add(nested); } } } } } } } } } return values; } /** * Fetch only the name and description of the object. */ @SuppressWarnings("rawtypes") public Vertex fetchDescription(JSONObject json, int cascade, Network network, Map<String, Vertex> processed) { if (json.isNullObject()) { return null; } // Get the object's name. Object id = json.get("id"); Vertex object = processed.get(id); if (object != null) { return object; } List names = extractText(json.get("labels")); Object name = null; if (names.size() > 0) { name = names.get(0); } List descriptions = extractText(json.get("descriptions")); log("Processing object:", Bot.FINE, id, names); try { if ((name instanceof String) && (((String)name).length() > 0)) { // Add names. object = network.createObject((String)name); if (object.hasRelationship(getPrimitive())) { return object; } object.addRelationship(getPrimitive(), network.createVertex(id)); processed.put((String)id, object); object.addRelationship(Primitive.INSTANTIATION, Primitive.THING); for (Object eachName : names) { if (eachName instanceof String) { Vertex word = network.createWord((String)eachName); word.addRelationship(Primitive.MEANING, object); object.addRelationship(Primitive.WORD, word); network.associateCaseInsensitivity((String)eachName, object); } } for (Object description : descriptions) { // Add descriptions. if (description instanceof String) { Vertex paragraph = network.createParagraph((String)description); if (paragraph.instanceOf(Primitive.PARAGRAPH)) { object.addRelationship(Primitive.PARAGRAPH, paragraph); Vertex sentence = paragraph.orderedRelations(Primitive.SENTENCE).get(0); object.addRelationship(Primitive.SENTENCE, sentence); } else { object.addRelationship(Primitive.SENTENCE, paragraph); } } } } else { object = network.createVertex(); } network.save(); } catch (Exception ioException) { log(ioException); } return object; } @SuppressWarnings({ "rawtypes", "unchecked" }) public void fetchDetails(Vertex object, JSONObject json, int cascade, Map<String, Vertex> processed, Network network) { try { JSONObject properties = (JSONObject)json.get("claims"); if (properties == null) { return; } // Extra relevant properties. List<String> keys = new ArrayList<String>(properties.keySet()); Map<String, String> propertyMap = fetchPropertyLabels(keys, network, processed); List<String> ids = extractPropertyValueIds(properties, propertyMap, network, processed); Map<String, String> valueMap = null; if (cascade <= 0) { valueMap = fetchPropertyLabels(ids, network, processed); } List types = extractPropertyValues(properties.get("P31"), valueMap, cascade, network, processed); propertyMap.remove("P31"); List names = extractText(json.get("labels")); List aliases = extractText(json.get("aliases")); Object name = null; if (names.size() > 0) { name = names.get(0); } // For each type, add instantiation relationship. for (Object type : types) { if (type instanceof String) { type = network.createObject((String)type); } if (type instanceof Vertex) { // Create vertex for type. Vertex typeVertex = (Vertex)type; typeVertex.addRelationship(Primitive.INSTANTIATION, Primitive.CLASSIFICATION); typeVertex.addRelationship(Primitive.INSTANTIATION, Primitive.THING); // Associate object with type. object.addRelationship(Primitive.INSTANTIATION, typeVertex); // Check if it is a person. if ((typeVertex.getName() != null && typeVertex.getName().equalsIgnoreCase("human"))) { object.addRelationship(Primitive.INSTANTIATION, Primitive.SPEAKER); if (name instanceof String) { Vertex word = network.createVertex(name); word.addRelationship(Primitive.INSTANTIATION, Primitive.NAME); object.addRelationship(Primitive.NAME, word); for (Object eachName : names) { if (eachName instanceof String) { word = network.createVertex(eachName); word.addRelationship(Primitive.INSTANTIATION, Primitive.NAME); object.addRelationship(Primitive.NAME, word); } } } } } } // Add aliases for (Object alias : aliases) { if (alias instanceof String) { Vertex word = network.createWord((String)alias); word.addRelationship(Primitive.MEANING, object); object.addRelationship(Primitive.WORD, word); network.associateCaseInsensitivity((String)alias, object); } } for (Map.Entry<String, String> property : propertyMap.entrySet()) { List values = extractPropertyValues(properties.get(property.getKey()), valueMap, cascade, network, processed); for (Object value : values) { Vertex valueVertex = null; if (value instanceof String) { valueVertex = network.createObject((String)value); valueVertex.addRelationship(Primitive.INSTANTIATION, Primitive.THING); } else if (value instanceof Vertex) { valueVertex = (Vertex)value; } else { valueVertex = network.createVertex(value); valueVertex.addRelationship(Primitive.INSTANTIATION, Primitive.THING); } // Add relationship. Vertex relationshipVertex = network.createPrimitive(property.getValue()); relationshipVertex.addRelationship(Primitive.INSTANTIATION, Primitive.THING); relationshipVertex.addRelationship(Primitive.INSTANTIATION, Primitive.ACTION); relationshipVertex.addRelationship(Primitive.INSTANTIATION, Primitive.RELATIONSHIP); object.addRelationship(relationshipVertex, valueVertex); log("Processing reltionship:", Bot.FINE, property.getKey(), object, relationshipVertex, valueVertex); } } network.save(); } catch (Exception ioException) { log(ioException); } } /** * Process the mql query and convert the result to a JSON object. */ public JSON processQuery(String query) throws IOException { log("API", Level.FINEST, query); URL get = new URL(query); Reader reader = new InputStreamReader(get.openStream(), "UTF-8"); StringWriter output = new StringWriter(); int next = reader.read(); while (next != -1) { output.write(next); next = reader.read(); } String result = output.toString(); log("JSON", Level.FINEST, result); return JSONSerializer.toJSON(result); } /** * Post, process the post request. */ @Override public void output(Vertex output) { } /** * Self API * Discover the meaning of the word including the filter. */ public Vertex search(Vertex source, Vertex filter, Vertex vertex) { return search(source, filter, vertex, null, null, null, null); } /** * Self API * Discover the meaning of the word including the filter. */ public Vertex search(Vertex source, Vertex filter, Vertex vertex, Vertex vertex2, Vertex vertex3, Vertex vertex4, Vertex vertex5) { return discover(true, true, (String)filter.getData(), vertex, vertex2, vertex3, vertex4, vertex5); } /** * Self API * Discover the meaning of the word including the filter. */ public Vertex define(Vertex source, Vertex vertex) { return define(source, vertex, null); } /** * Self API * Discover the meaning of the word only. */ public Vertex define(Vertex source, Vertex vertex, Vertex vertex2) { return define(source, vertex, vertex2, null, null, null); } /** * Self API * Discover the meaning of the word only. */ public Vertex define(Vertex source, Vertex vertex, Vertex vertex2, Vertex vertex3, Vertex vertex4, Vertex vertex5) { return discover(false, false, null, vertex, vertex2, vertex3, vertex4, vertex5); } /** * Self API * Discover the meaning of the word including all details. */ public Vertex details(Vertex source, Vertex vertex) { return details(source, vertex, null); } /** * Self API * Discover the meaning of the word including all details. */ public Vertex details(Vertex source, Vertex vertex, Vertex vertex2) { return details(source, vertex, vertex2, null, null, null); } /** * Self API * Discover the meaning of the word including all details. */ public Vertex details(Vertex source, Vertex vertex, Vertex vertex2, Vertex vertex3, Vertex vertex4, Vertex vertex5) { return discover(true, false, null, vertex, vertex2, vertex3, vertex4, vertex5); } /** * Self API * Discover the meaning of the word. */ public Vertex discover(Vertex source, Vertex vertex) { return discover(source, vertex, null); } /** * Self API * Discover the meaning of the word. */ public Vertex discover(Vertex source, Vertex vertex, Vertex vertex2) { return discover(source, vertex, vertex2, null, null, null); } /** * Self API * Discover the meaning of the word. */ public Vertex discover(Vertex source, Vertex vertex, Vertex vertex2, Vertex vertex3, Vertex vertex4, Vertex vertex5) { return discover(true, true, null, vertex, vertex2, vertex3, vertex4, vertex5); } /** * Self API * Discover the meaning of the word. */ public Vertex discover(boolean details, boolean fork, String filter, Vertex vertex, Vertex vertex2, Vertex vertex3, Vertex vertex4, Vertex vertex5) { String keywords = vertex.printString(); String keywordscaps = Utils.capitalize(vertex.printString()); if ((vertex2 != null) && !vertex2.is(Primitive.NULL)) { keywords = keywords + " " + vertex2.getDataValue(); keywordscaps = keywordscaps + " " + Utils.capitalize(vertex2.printString()); } if ((vertex3 != null) && !vertex3.is(Primitive.NULL)) { keywords = keywords + " " + vertex3.getDataValue(); keywordscaps = keywordscaps + " " + Utils.capitalize(vertex3.printString()); } if ((vertex4 != null) && !vertex4.is(Primitive.NULL)) { keywords = keywords + " " + vertex4.getDataValue(); keywordscaps = keywordscaps + " " + Utils.capitalize(vertex4.printString()); } if ((vertex5 != null) && !vertex5.is(Primitive.NULL)) { keywords = keywords + " " + vertex5.getDataValue(); keywordscaps = keywordscaps + " " + Utils.capitalize(vertex5.printString()); } if (keywords != null) { if (vertex.instanceOf(Primitive.PRONOUN) || vertex.instanceOf(Primitive.ARTICLE) || vertex.instanceOf(Primitive.PUNCTUATION) || (vertex.hasRelationship(Primitive.MEANING) && (vertex.getRelationship(Primitive.MEANING).instanceOf(Primitive.NUMBER))) || vertex.instanceOf(Primitive.QUESTION) || this.discoveryIgnoreWords.contains(vertex.getData())) { return null; } Vertex compoundWord = vertex.getNetwork().createVertex(keywords); Vertex lastChecked = compoundWord.getRelationship(getPrimitive()); if (lastChecked == null) { compoundWord.addRelationship(getPrimitive(), compoundWord.getNetwork().createTimestamp()); try { int cascade = 0; if (!details) { cascade = -1; } Vertex result = null; if (result == null) { result = processSearch(keywords, cascade, fork, filter, vertex.getNetwork(), new HashMap<String, Vertex>()); } if (result != null) { compoundWord = vertex.getNetwork().createWord(keywords); compoundWord.addRelationship(Primitive.MEANING, result); compoundWord = vertex.getNetwork().createWord(keywords.toLowerCase()); compoundWord.addRelationship(Primitive.MEANING, result); compoundWord = vertex.getNetwork().createWord(keywordscaps); compoundWord.addRelationship(Primitive.MEANING, result); } return result; } catch (Exception failed) { log(failed); } } return compoundWord.mostConscious(Primitive.MEANING); } return null; } public Set<String> getExcludedProperties() { return excludedProperties; } public void setExcludedProperties(Set<String> excludedProperties) { this.excludedProperties = excludedProperties; } }