主题
搜索和查询知识图谱服务
字数统计: 1.3k 字
阅读时长: 约 3 分钟
当前版本: 4.29
搜索和查询都可以从知识图谱中检索记录。搜索是对知识图谱进行文本搜索。查询提供了一种更细致的方式,以 openCypher 查询的方式检索对知识图谱记录。搜索和查询都有两种模式:非流式和流式。流式搜索和流式查询以小的 chunk 块返回结果,允许客户端立即开始处理返回的数据,而不是等待返回整个结果集后再进行处理。流式处理更快、更高效,并且将检索所有匹配的记录,即使总数超过服务定义中设置的搜索和查询限制也是如此。流的另一个好处是请求是编码的,这意味着它比传统的 HTTP GET 或 JSON POST 的 body 小得多。当试图对非常大的参数进行查询时 (例如,大型 ID 集或与复杂几何体相交),这一点尤其重要。
搜索
GraphSearchStreaming 提供了 executeSearchStreaming() 方法来实现搜索图谱中实体和关系的属性。设置可选搜索参数可以进一步限制搜索结果。搜索知识图谱的最有效方法是使用流式搜索。
js
// define the search terms
const search = new StreamingSearch({
searchQuery: "solar",
typeCategoryFilter: "both",
// optional parameters
returnSearchContext: false,
start: 1, //start at the first record
num: 200, //return 200 records.
namedTypesFilter: ["Company", "Supplier", "Part"],
globalIdsFilter: ["aiw-924", "esj-856", "snh-571", "hnk-9751", "pyo-7884", "hjl-2541"]
});
// search the knowledge graph
KnowledgeGraphModule.executeSearchStreaming(
// knowledge graph resources
knowledgeGraph,
// search parameters
search
).then((streamingSearchResult) => {
// the result of a streaming search is a readableStream which must be read to access the data.
readStream(streamingSearchResult);
});
查询
查询实体和关系的子集可以使用executeQueryStreaming() 方法。查询图谱使用 openCypher 查询实现,该查询支持只读操作。从图谱返回的结果和返回的数据结构方面,查询提供了更大的灵活性。例如,假设有一个供应链图谱,该图谱包含制造 Plant
的实体,其制造的 Part
由 Supplier
进行购买;工厂和它生产的零件之间有一种 produces
关系,供应商和它购买零件的工厂之间有一种 buys_part
关系。
查找 Supplier
类型的前十个实体,可以使用查询 MATCH (m:Supplier) RETURN m LIMIT 10
。要发现哪些零件 (p
) 是由哪些工厂 (pl
) 生产的,可以使用查询通过 produces
关系来匹配实体,例如 MATCH (pl:Plant)-[ :produces]->(p) RETURN pl,p
。GraphQueryStreaming 还提供了地图范围查询。例如,要查找位于某区域的所有供应商,可使用交集查询并该区域的面作为参数进行查询。有关其他查询参数,请参阅 GraphQueryStreaming。
js
// select all Supplier's that are in the Washington DC area and the parts that they buy
const query = `MATCH (s:Supplier)-[:buys_part]-(p:Part)
esri.graph.ST_Intersects($geom, s.geometry)
RETURN s,p`;
KnowledgeGraphModule.executeQueryStreaming(knowledgeGraph, {
openCypherQuery: query,
bindParameters: {
//bounding box around an area
geom: new Polygon({
rings: [
[
[38, -78],
[39, -78],
[39, -76],
[-38, -76],
[-38, -78]
]
]
})
}
}).then((streamingQueryResult) => {
// the result of a streaming query is a readableStream which must be read to access the data.
readStream(streamingQueryResult);
});
使用流结果
流式搜索或流式查询的结果是 GraphQueryStreamingResult,其中包含一个可读流,必须使用 getReader() 函数读取该流才能访问数据。可读流将返回记录块,直到返回所有记录。每个块都可立即处理,例如将数据添加到表或将具有几何的实体添加到地图。
js
// a function to read the stream returned from the search
const readStream = async (streamingQueryResult) => {
let time = Date.now();
let reader = streamingQueryResult.resultRowsStream.getReader();
try {
while (true) {
const { done, value } = await reader.read();
if (done) {
console.log(`Stream completed and closed: ${(Date.now() - time) / 1000} seconds`, value);
break;
}
// begin working with the data from the returned chunk immediately.
// list the parts bought by each supplier
let supplierParts = [];
// each element of the result array will contain one supplier and one part it buys
for (let v in value){
let supplier = value[v][0].properties.Name
let part = value [v][1].properties.Name
if(!(supplier in supplierParts)){
supplierParts[supplier] = [];
}
// collect parts by supplier that buys them
supplierParts[supplier].push(part);
}
// make a table that lists the suppliers and the parts they buy
let table = document.getElementById('supplierTableBody');
for (let supplier in supplierParts){
table.appendChild(`<tr><td>${supplier}</td><td>${supplierParts.supplier.join(', ')}</td>`);
}
// Entities that have geometry can be drawn on the map
addToMap(value);
// Since
}
// if there is an error in returning the stream or the stream is aborted
} catch (err) {
if (err.name === "AbortError") {
console.log("Request aborted as expected");
} else {
throw err;
}
}
};
// function to add entities with geometry to a map
function addToMap(records) {
let features = [];
//extract the geometry from the returned results and add it to a feature layer
for (let i = 0; i < records.length; i++) {
let item = records[i][0];
let props = item.properties;
// if the returned item contains geometry,
//extract the geometry information to create features
if ("shape" in props) {
features.push({
geometry: {
type: props.shape.type,
x: props.shape.longitude,
y: props.shape.latitude
},
attributes: props
});
}
}
// create feature layer
let featureLayer = new FeatureLayer({
source: features,
renderer: {
type: "unique-value", // autocasts as new UniqueValueRenderer()
defaultSymbol: {
type: "simple-marker", // autocasts as new SimpleMarkerSymbol()
size: 2,
color: "#009816",
outline: null
}
}
});
map.add(featureLayer);
}