搜索和查询知识图谱服务

字数统计: 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 的实体,其制造的 PartSupplier 进行购买;工厂和它生产的零件之间有一种 produces 关系,供应商和它购买零件的工厂之间有一种 buys_part 关系。

查找 Supplier 类型的前十个实体,可以使用查询 MATCH (m:Supplier) RETURN m LIMIT 10。要发现哪些零件 (p) 是由哪些工厂 (pl) 生产的,可以使用查询通过 produces 关系来匹配实体,例如 MATCH (pl:Plant)-[ :produces]->(p) RETURN pl,pGraphQueryStreaming 还提供了地图范围查询。例如,要查找位于某区域的所有供应商,可使用交集查询并该区域的面作为参数进行查询。有关其他查询参数,请参阅 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);
    }