Level | Traditional RAG | GraphRAG |
Search Methods | Pure vector similarity | Graph traversal + vector hybrid |
Multi-hop Reasoning | Not supported | Reasoning via graph relationships |
Entity Relationships | Implicit in text | Explicitly stored as a graph schema |
Global Comprehension | Limited to a single chunk | Obtains a global perspective via graphs |
Applicable Scenario | Simple Q&A | Complex reasoning, relationship querying |
┌──────────────────────────────────────────────────────┐│ GraphRAG System │├──────────────────────────────────────────────────────┤│ ││ Document Input → Entity Extraction → Graph Construction → Index Creation ││ ││ User Query → Entity Recognition → Graph Search + Vector Search → Fusion Generation ││ │├──────────────────────────────────────────────────────┤│ Storage Layer ││ ┌──────────────┐ ┌──────────────┐ ┌───────────┐ ││ │ Apache AGE │ │ pgvector │ │ Relational Table │ ││ │ (Knowledge Graph) │ │ (Document Vector) │ │ (Metadata) │ ││ └──────────────┘ └──────────────┘ └───────────┘ ││ ││ AI Layer ││ ┌─────────────────────────────────────────────┐ ││ │ tencentdb_ai (Entity Extraction / Relation Recognition / Generation) │ ││ └─────────────────────────────────────────────┘ │└──────────────────────────────────────────────────────┘
-- Create the extensionCREATE EXTENSION IF NOT EXISTS vector;CREATE EXTENSION IF NOT EXISTS age;-- Set search_pathSET search_path = ag_catalog, "$user", public;-- Create a knowledge graphSELECT create_graph('knowledge_graph');-- Document storage tableCREATE TABLE graphrag.documents (id BIGSERIAL PRIMARY KEY,title TEXT,content TEXT NOT NULL,embedding vector(1024),metadata JSONB DEFAULT '{}',created_at TIMESTAMPTZ DEFAULT NOW());CREATE INDEX ON graphrag.documentsUSING hnsw (embedding vector_cosine_ops);-- Entity table (auxiliary index)CREATE TABLE graphrag.entities (id BIGSERIAL PRIMARY KEY,name TEXT NOT NULL,entity_type TEXT NOT NULL,description TEXT,embedding vector(1024),source_doc_id BIGINT REFERENCES graphrag.documents(id),UNIQUE(name, entity_type));CREATE INDEX ON graphrag.entitiesUSING hnsw (embedding vector_cosine_ops);
-- Use a large model to extract entities and relations from documentsCREATE OR REPLACE FUNCTION graphrag.extract_entities(doc_content TEXT)RETURNS JSONB AS $$DECLAREresult TEXT;BEGINSELECT tencentdb_ai.chat_completions('<your-llm-model>','Extract entities and relations from the following text and return them in JSON format.' ||'Format: {"entities": [{"name": "entity name", "type": "type", "description": "description"}], ' ||"relations": [{"source": "source entity", "target": "target entity", "relation": "relation type", "description": "description"}]}' ||E'\\n\\nText: ' || doc_content) INTO result;RETURN result::JSONB;END;$$ LANGUAGE plpgsql;
entity.type / relation.relation from the LLM are used as Cypher Tags in queries. You must validate them against an allowlist to prevent arbitrary strings from entering Cypher:-- Allowlist for permitted entity types and relation typesCREATE TABLE IF NOT EXISTS graphrag.allowed_entity_types (type TEXT PRIMARY KEY);CREATE TABLE IF NOT EXISTS graphrag.allowed_relation_types (type TEXT PRIMARY KEY);INSERT INTO graphrag.allowed_entity_types(type) VALUES('Person'), ('Company'), ('Product'), ('Technology'), ('Concept')ON CONFLICT DO NOTHING;INSERT INTO graphrag.allowed_relation_types(type) VALUES('WORKS_AT'), ('USES'), ('HAS_FEATURE'), ('RELATED_TO'), ('PART_OF')ON CONFLICT DO NOTHING;-- Store the extracted entities in the graphCREATE OR REPLACE FUNCTION graphrag.build_graph(doc_id BIGINT)RETURNS VOID AS $$DECLAREdoc_content TEXT;extracted JSONB;entity JSONB;relation JSONB;e_type TEXT;r_type TEXT;BEGINSELECT content INTO doc_content FROM graphrag.documents WHERE id = doc_id;extracted := graphrag.extract_entities(doc_content);-- Create entity nodes (with allowlist validation for types)FOR entity IN SELECT * FROM jsonb_array_elements(extracted->'entities')LOOPe_type := entity->>'type';IF NOT EXISTS (SELECT 1 FROM graphrag.allowed_entity_types WHERE type = e_type) THENRAISE NOTICE 'Skip entity with disallowed type: %', e_type;CONTINUE;END IF;PERFORM * FROM cypher('knowledge_graph', format($$MERGE (n:%I {name: %L})SET n.description = %L, n.doc_id = %s$$, e_type, entity->>'name', entity->>'description', doc_id))AS (v agtype);END LOOP;-- Create relations (with allowlist validation for relation types)FOR relation IN SELECT * FROM jsonb_array_elements(extracted->'relations')LOOPr_type := relation->>'relation';IF NOT EXISTS (SELECT 1 FROM graphrag.allowed_relation_types WHERE type = r_type) THENRAISE NOTICE 'Skip relation with disallowed type: %', r_type;CONTINUE;END IF;PERFORM * FROM cypher('knowledge_graph', format($$MATCH (a {name: %L}), (b {name: %L})MERGE (a)-[r:%I]->(b)SET r.description = %L$$, relation->>'source', relation->>'target',r_type, relation->>'description'))AS (r agtype);END LOOP;END;$$ LANGUAGE plpgsql;
-- Hybrid Search: Graph Traversal + Vector SearchCREATE OR REPLACE FUNCTION graphrag.hybrid_retrieve(query TEXT, top_k INT DEFAULT 5)RETURNS TABLE(source TEXT, content TEXT, score FLOAT) AS $$BEGIN-- Vector Search DocumentationRETURN QUERYSELECT 'vector'::TEXT, d.content,(1 - (d.embedding <=> tencentdb_ai.get_embedding('bge-m3', query)))::FLOATFROM graphrag.documents dORDER BY d.embedding <=> tencentdb_ai.get_embedding('bge-m3', query)LIMIT top_k;-- Graph Search for Relevant EntitiesRETURN QUERYSELECT 'graph'::TEXT,(a->>'name' || ' --[' || r->>'relation' || ']--> ' || b->>'name')::TEXT,0.8::FLOATFROM cypher('knowledge_graph', format($$MATCH (a)-[r]->(b)WHERE a.name CONTAINS %L OR b.name CONTAINS %LRETURN properties(a) as a, properties(r) as r, properties(b) as bLIMIT %s$$, query, query, top_k)) AS (a agtype, r agtype, b agtype);END;$$ LANGUAGE plpgsql;
-- GraphRAG Q&ACREATE OR REPLACE FUNCTION graphrag.answer(question TEXT)RETURNS TEXT AS $$DECLAREcontext TEXT;answer TEXT;BEGINSELECT string_agg('[' || source || '] ' || content, E'\\n')INTO contextFROM graphrag.hybrid_retrieve(question, 5);SELECT tencentdb_ai.chat_completions('<your-llm-model>',Answer the question based on the following knowledge graph information and document content. Mark knowledge graph information as [graph] and document content as [vector].E'\\n\\nContext:\\n' || COALESCE(context, 'None') ||E'\\n\\nQuestion: ' || question) INTO answer;RETURN answer;END;$$ LANGUAGE plpgsql;
-- Import Documents and Build GraphINSERT INTO graphrag.documents (title, content, embedding)VALUES ('PostgreSQL and AI',TencentDB for PostgreSQL supports directly invoking large models within SQL through the tencentdb_ai plugin...tencentdb_ai.get_embedding('bge-m3', 'Tencent Cloud PostgreSQL supports directly calling large models within SQL through the tencentdb_ai plugin'));-- Build GraphSELECT graphrag.build_graph(1);-- GraphRAG Q&ASELECT graphrag.answer('What AI capabilities does PostgreSQL support?');
MERGE to avoid duplicate entities.Was this page helpful?
You can also Contact sales or Submit a Ticket for help.
Help us improve! Rate your documentation experience in 5 mins.
Feedback