Skip to main content

Documentation Index

Fetch the complete documentation index at: https://mintlify.com/jbarrasa/goingmeta/llms.txt

Use this file to discover all available pages before exploring further.

Session 2 of Going Meta (broadcast March 1, 2022) demonstrates how to move from keyword search to genuine semantic search by connecting article content to a controlled vocabulary. The working example loads dev.to articles as CSV, imports a SKOS taxonomy with neosemantics (n10s), uses the Google Cloud Natural Language API to tag each article with Wikidata concepts, and then traverses the taxonomy hierarchy to find articles related to a query at any level of specificity — not just exact matches.

What You Will Learn

  • Loading article data from CSV directly into Neo4j with LOAD CSV
  • Importing a SKOS concept scheme with n10s.skos.import.fetch (with broader hierarchy)
  • Cleaning up redundant transitive relationships from Wikidata extracts
  • Using apoc.nlp.gcp.entities.stream in batch mode to tag articles with Wikidata concepts
  • Writing Cypher path queries that traverse broader hierarchies for semantic search
  • Using n10s.inference.nodesInCategory as a declarative alternative to manual path traversal

Building the Knowledge Graph

1

Load articles from CSV

LOAD CSV WITH HEADERS FROM
  'https://raw.githubusercontent.com/jbarrasa/goingmeta/main/session02/resources/devto-articles.csv'
AS row
CREATE (a:Article { uri: row.uri })
SET a.title = row.title,
    a.body  = row.body,
    a.datetime = datetime(row.date);
2

Create the uniqueness constraint and initialise n10s

CREATE CONSTRAINT n10s_unique_uri ON (r:Resource) ASSERT r.uri IS UNIQUE;

CALL n10s.graphconfig.init({
  handleVocabUris: "IGNORE",
  classLabel: "Concept",
  subClassOfRel: "broader"
});
3

Import the SKOS taxonomy

CALL n10s.skos.import.fetch(
  "https://raw.githubusercontent.com/jbarrasa/goingmeta/main/session02/resources/goingmeta-skos.ttl",
  "Turtle"
);
4

Remove redundant broader relationships

Wikidata extracts sometimes include shortcut broader links that skip intermediate levels. Remove them to keep the hierarchy clean:
MATCH (s:Concept)-[shortcut:broader]->(:Concept)<-[:broader*2..]-(s)
DELETE shortcut;
5

Tag articles with GCP NLP (batch mode)

:params key => ("<insert-key-here>")

CALL apoc.periodic.iterate(
  "MATCH (a:Article)
   WHERE not(exists(a.processed))
   RETURN a",
  "CALL apoc.nlp.gcp.entities.stream([item in $_batch | item.a], {
     nodeProperty: 'body',
     key: $key
   })
   YIELD node, value
   SET node.processed = true
   WITH node, value
   UNWIND value.entities AS entity
   WITH entity, node
   WHERE not(entity.metadata.wikipedia_url is null)
   MATCH (c:Concept {altLabel: entity.metadata.wikipedia_url})
   MERGE (node)-[:refers_to]->(c)",
  {batchMode: "BATCH_SINGLE", batchSize: 10, params: {key: $key}}
) YIELD batches, total, timeTaken, committedOperations
RETURN batches, total, timeTaken, committedOperations;
You can also enrich the graph further by importing an additional ontology — for example, the software-stack ontology — with CALL n10s.onto.import.fetch("http://www.nsmntx.org/2020/08/swStacks", "Turtle").

Semantic Search Queries

With the taxonomy-linked knowledge graph in place, you can now search beyond exact matches by traversing the broader hierarchy.

Articles on a Concept — Direct Path Traversal

MATCH path = (:Concept { prefLabel: "NoSQL database management system" })<-[:broader*0..]-(sc)<-[:refers_to]-(art:Article)
RETURN art.title,
       [x IN nodes(path) WHERE x:Concept | coalesce(x.prefLabel, "") + coalesce(x.label, "")]

Same Query Using n10s Inference

MATCH (c:Concept { prefLabel: "NoSQL database management system" })
CALL n10s.inference.nodesInCategory(c, { inCatRel: "refers_to" }) YIELD node AS article
RETURN article.title AS result
n10s.inference.nodesInCategory is the declarative way to express taxonomy-aware category membership. It avoids writing explicit variable-length path patterns and automatically traverses the full broader hierarchy.

Articles Covering Both Document DBs and RDBMSs

MATCH path = (a:Concept)<-[:broader*0..]-()<-[:refers_to]-(art:Article)-[:refers_to]->()-[:broader*0..]->(b:Concept)
WHERE a.prefLabel = "document-oriented database"
  AND b.prefLabel = "relational database management system"
RETURN art.title

”Read Next” Recommendations

Use this query to surface articles that share related concepts with a given article — a simple but effective personalisation pattern:
MATCH simpath = (a:Article)-[:refers_to]->(cat)-[:broader*0..1]->()<-[:broader*0..1]-()<-[:refers_to]-(other)
WHERE a.uri = "https://dev.to/qainsights/performance-testing-neo4j-database-using-bolt-protocol-in-apache-jmeter-1oa9"
RETURN other.title,
       [x IN nodes(simpath) WHERE x:Concept | coalesce(x.prefLabel, x.label)]

Resources

Watch the Recording

Full live-stream recording of Going Meta Session 2 on YouTube.

Session Code on GitHub

The build-kg.cypher script and all queries from this session.

Build docs developers (and LLMs) love