T12: GraphLoader Organization/Company loading + GraphSchema constraints [production]

This commit is contained in:
Artur Kruszewski
2026-07-08 23:30:28 +02:00
parent 6e87fd1b8d
commit 6d16828df4
5 changed files with 144 additions and 2 deletions
@@ -30,6 +30,11 @@ public class GraphLoader {
loadRoles(session, dataset.roles(), stats); loadRoles(session, dataset.roles(), stats);
loadAffiliations(session, dataset.affiliations(), stats); loadAffiliations(session, dataset.affiliations(), stats);
loadMandates(session, dataset.mandates(), stats); loadMandates(session, dataset.mandates(), stats);
loadOrganizations(session, dataset.organizations(), stats);
loadCompanies(session, dataset.companies(), stats);
loadPersonOrganizationLinks(session, dataset.personOrganizationLinks(), stats);
loadOrganizationLinks(session, dataset.organizationLinks(), stats);
loadPersonCompanyLinks(session, dataset.personCompanyLinks(), stats);
} }
} }
return stats; return stats;
@@ -176,6 +181,130 @@ public class GraphLoader {
} }
} }
private void loadOrganizations(Session session, List<Organization> organizations, LoadStats stats) {
if (organizations.isEmpty()) return;
List<Map<String, Object>> batch = new ArrayList<>();
for (Organization o : organizations) {
batch.add(Map.of(
"id", o.id(),
"name", o.name(),
"krs", o.krs(),
"nip", o.nip(),
"sourceUrls", o.sourceUrls()
));
}
for (int i = 0; i < batch.size(); i += BATCH_SIZE) {
List<Map<String, Object>> subList = batch.subList(i, Math.min(i + BATCH_SIZE, batch.size()));
String cypher = "UNWIND $batch AS o " +
"MERGE (org:Organization {id: o.id}) " +
"SET org += {name: o.name, krs: o.krs, nip: o.nip, sourceUrls: o.sourceUrls}";
session.run(cypher, Map.of("batch", subList));
stats.addOrganizations(subList.size());
}
}
private void loadCompanies(Session session, List<Company> companies, LoadStats stats) {
if (companies.isEmpty()) return;
List<Map<String, Object>> batch = new ArrayList<>();
for (Company c : companies) {
batch.add(Map.of(
"id", c.id(),
"name", c.name(),
"krs", c.krs(),
"nip", c.nip(),
"ceidgId", c.ceidgId(),
"sourceUrls", c.sourceUrls()
));
}
for (int i = 0; i < batch.size(); i += BATCH_SIZE) {
List<Map<String, Object>> subList = batch.subList(i, Math.min(i + BATCH_SIZE, batch.size()));
String cypher = "UNWIND $batch AS c " +
"MERGE (comp:Company {id: c.id}) " +
"SET comp += {name: c.name, krs: c.krs, nip: c.nip, ceidgId: c.ceidgId, sourceUrls: c.sourceUrls}";
session.run(cypher, Map.of("batch", subList));
stats.addCompanies(subList.size());
}
}
private void loadPersonOrganizationLinks(Session session, List<PersonOrganizationLink> links, LoadStats stats) {
if (links.isEmpty()) return;
List<Map<String, Object>> batch = new ArrayList<>();
for (PersonOrganizationLink l : links) {
batch.add(Map.of(
"personId", l.personId(),
"organizationId", l.organizationId(),
"roleLabel", l.roleLabel(),
"basis", l.basis().name(),
"sourceUrls", l.sourceUrls()
));
}
for (int i = 0; i < batch.size(); i += BATCH_SIZE) {
List<Map<String, Object>> subList = batch.subList(i, Math.min(i + BATCH_SIZE, batch.size()));
String cypher = "UNWIND $batch AS l " +
"MATCH (p:Person {id: l.personId}), (org:Organization {id: l.organizationId}) " +
"MERGE (p)-[m:CZLONEK_ORGANIZACJI]->(org) " +
"SET m += {roleLabel: l.roleLabel, basis: l.basis, sourceUrls: l.sourceUrls}";
session.run(cypher, Map.of("batch", subList));
stats.addPersonOrganizationLinks(subList.size());
}
}
private void loadOrganizationLinks(Session session, List<OrganizationLink> links, LoadStats stats) {
if (links.isEmpty()) return;
List<Map<String, Object>> batch = new ArrayList<>();
for (OrganizationLink l : links) {
batch.add(Map.of(
"fromId", l.fromOrganizationId(),
"toId", l.toOrganizationId(),
"basis", l.basis().name(),
"note", l.note(),
"sourceUrls", l.sourceUrls()
));
}
for (int i = 0; i < batch.size(); i += BATCH_SIZE) {
List<Map<String, Object>> subList = batch.subList(i, Math.min(i + BATCH_SIZE, batch.size()));
String cypher = "UNWIND $batch AS l " +
"MATCH (a:Organization {id: l.fromId}), (b:Organization {id: l.toId}) " +
"MERGE (a)-[m:POWIAZANA_Z]->(b) " +
"SET m += {basis: l.basis, note: l.note, sourceUrls: l.sourceUrls}";
session.run(cypher, Map.of("batch", subList));
stats.addOrganizationLinks(subList.size());
}
}
private void loadPersonCompanyLinks(Session session, List<PersonCompanyLink> links, LoadStats stats) {
if (links.isEmpty()) return;
List<Map<String, Object>> batch = new ArrayList<>();
for (PersonCompanyLink l : links) {
batch.add(Map.of(
"personId", l.personId(),
"companyId", l.companyId(),
"roleLabel", l.roleLabel(),
"basis", l.basis().name(),
"sourceUrls", l.sourceUrls()
));
}
for (int i = 0; i < batch.size(); i += BATCH_SIZE) {
List<Map<String, Object>> subList = batch.subList(i, Math.min(i + BATCH_SIZE, batch.size()));
String cypher = "UNWIND $batch AS l " +
"MATCH (p:Person {id: l.personId}), (comp:Company {id: l.companyId}) " +
"MERGE (p)-[m:POWIAZANY_Z_FIRMA]->(comp) " +
"SET m += {roleLabel: l.roleLabel, basis: l.basis, sourceUrls: l.sourceUrls}";
session.run(cypher, Map.of("batch", subList));
stats.addPersonCompanyLinks(subList.size());
}
}
private String roleTypeToRelName(Role r) { private String roleTypeToRelName(Role r) {
String organ = r.organ().name(); String organ = r.organ().name();
String role = r.roleType().name(); String role = r.roleType().name();
@@ -196,6 +325,11 @@ public class GraphLoader {
public int roleCount; public int roleCount;
public int affiliationCount; public int affiliationCount;
public int mandateCount; public int mandateCount;
public int organizationCount;
public int companyCount;
public int personOrganizationLinkCount;
public int organizationLinkCount;
public int personCompanyLinkCount;
public final List<String> voivodeships = new ArrayList<>(); public final List<String> voivodeships = new ArrayList<>();
public void addVoivodeship(String v) { voivodeships.add(v); } public void addVoivodeship(String v) { voivodeships.add(v); }
@@ -204,12 +338,18 @@ public class GraphLoader {
public void addRoles(int count) { roleCount += count; } public void addRoles(int count) { roleCount += count; }
public void addAffiliations(int count) { affiliationCount += count; } public void addAffiliations(int count) { affiliationCount += count; }
public void addMandates(int count) { mandateCount += count; } public void addMandates(int count) { mandateCount += count; }
public void addOrganizations(int count) { organizationCount += count; }
public void addCompanies(int count) { companyCount += count; }
public void addPersonOrganizationLinks(int count) { personOrganizationLinkCount += count; }
public void addOrganizationLinks(int count) { organizationLinkCount += count; }
public void addPersonCompanyLinks(int count) { personCompanyLinkCount += count; }
@Override @Override
public String toString() { public String toString() {
return String.format( return String.format(
"Load stats - voivodeships: %s, hospitals: %d, people: %d, roles: %d, affiliations: %d, mandates: %d", "Load stats - voivodeships: %s, hospitals: %d, people: %d, roles: %d, affiliations: %d, mandates: %d, organizations: %d, companies: %d, personOrgLinks: %d, orgLinks: %d, personCompanyLinks: %d",
String.join(", ", voivodeships), hospitalCount, personCount, roleCount, affiliationCount, mandateCount String.join(", ", voivodeships), hospitalCount, personCount, roleCount, affiliationCount, mandateCount,
organizationCount, companyCount, personOrganizationLinkCount, organizationLinkCount, personCompanyLinkCount
); );
} }
} }
@@ -21,6 +21,8 @@ public class GraphSchema {
session.run("CREATE CONSTRAINT person_id IF NOT EXISTS FOR (p:Person) REQUIRE p.id IS UNIQUE"); session.run("CREATE CONSTRAINT person_id IF NOT EXISTS FOR (p:Person) REQUIRE p.id IS UNIQUE");
session.run("CREATE CONSTRAINT party_name IF NOT EXISTS FOR (x:Party) REQUIRE x.name IS UNIQUE"); session.run("CREATE CONSTRAINT party_name IF NOT EXISTS FOR (x:Party) REQUIRE x.name IS UNIQUE");
session.run("CREATE CONSTRAINT voiv_slug IF NOT EXISTS FOR (v:Voivodeship) REQUIRE v.slug IS UNIQUE"); session.run("CREATE CONSTRAINT voiv_slug IF NOT EXISTS FOR (v:Voivodeship) REQUIRE v.slug IS UNIQUE");
session.run("CREATE CONSTRAINT org_id IF NOT EXISTS FOR (o:Organization) REQUIRE o.id IS UNIQUE");
session.run("CREATE CONSTRAINT company_id IF NOT EXISTS FOR (c:Company) REQUIRE c.id IS UNIQUE");
session.run("CREATE INDEX person_name IF NOT EXISTS FOR (p:Person) ON (p.fullName)"); session.run("CREATE INDEX person_name IF NOT EXISTS FOR (p:Person) ON (p.fullName)");
session.run("CREATE INDEX govbody_name IF NOT EXISTS FOR (g:GovBody) ON (g.name)"); session.run("CREATE INDEX govbody_name IF NOT EXISTS FOR (g:GovBody) ON (g.name)");
log.info("Graph constraints and indexes created"); log.info("Graph constraints and indexes created");