5050import org .neo4j .values .virtual .MapValue ;
5151
5252import java .util .ArrayList ;
53+ import java .util .Collection ;
5354import java .util .HashSet ;
5455import java .util .List ;
5556import java .util .Map ;
5859import java .util .concurrent .atomic .LongAdder ;
5960import java .util .function .Function ;
6061import java .util .function .LongPredicate ;
62+ import java .util .function .Supplier ;
6163import java .util .stream .Collectors ;
6264
6365import static java .util .stream .Collectors .toMap ;
@@ -81,13 +83,11 @@ public final class NodesBuilder {
8183
8284 private final ConcurrentMap <String , NodePropertiesFromStoreBuilder > propertyBuildersByPropertyKey ;
8385
84- private final NodeLabelTokenToPropertyKeys nodeLabelTokenToPropertyKeys ;
85-
8686 NodesBuilder (
8787 long maxOriginalId ,
8888 int concurrency ,
8989 TokenToNodeLabel tokenToNodeLabel ,
90- NodeLabelTokenToPropertyKeys nodeLabelTokenToPropertyKeys ,
90+ Supplier < NodeLabelTokenToPropertyKeys > nodeLabelTokenToPropertyKeysSupplier ,
9191 ConcurrentMap <String , NodePropertiesFromStoreBuilder > propertyBuildersByPropertyKey ,
9292 IdMapBuilder idMapBuilder ,
9393 boolean hasLabelInformation ,
@@ -97,7 +97,6 @@ public final class NodesBuilder {
9797 ) {
9898 this .maxOriginalId = maxOriginalId ;
9999 this .concurrency = concurrency ;
100- this .nodeLabelTokenToPropertyKeys = nodeLabelTokenToPropertyKeys ;
101100 this .idMapBuilder = idMapBuilder ;
102101 this .propertyStates = propertyStates ;
103102 this .labelInformationBuilder = !hasLabelInformation
@@ -128,7 +127,7 @@ public final class NodesBuilder {
128127 hasLabelInformation ,
129128 hasProperties ,
130129 tokenToNodeLabel ,
131- nodeLabelTokenToPropertyKeys ,
130+ nodeLabelTokenToPropertyKeysSupplier . get () ,
132131 propertyBuilderFn
133132 )
134133 );
@@ -200,14 +199,11 @@ public Nodes build() {
200199 }
201200
202201 public Nodes build (long highestNeoId ) {
203- // Flush remaining buffer contents
204- this .threadLocalBuilder .forEach (ThreadLocalBuilder ::flush );
205- // Clean up resources held by local builders
206- this .threadLocalBuilder .close ();
202+ var localLabelTokenToPropertyKeys = closeThreadLocalBuilders ();
207203
208204 var idMap = this .idMapBuilder .build (labelInformationBuilder , highestNeoId , concurrency );
209205 var nodeProperties = buildProperties (idMap );
210- var nodeSchema = buildNodeSchema (idMap , nodeProperties );
206+ var nodeSchema = buildNodeSchema (idMap , localLabelTokenToPropertyKeys , nodeProperties );
211207 var nodePropertyStore = NodePropertyStore .builder ().properties (nodeProperties ).build ();
212208
213209 return ImmutableNodes .builder ()
@@ -217,26 +213,53 @@ public Nodes build(long highestNeoId) {
217213 .build ();
218214 }
219215
220- private NodeSchema buildNodeSchema (IdMap idMap , Map <String , NodeProperty > nodeProperties ) {
221- var nodeSchema = NodeSchema .empty ();
222- var importPropertySchemas = nodeProperties
216+ private List <NodeLabelTokenToPropertyKeys > closeThreadLocalBuilders () {
217+ // Flush remaining buffer contents
218+ this .threadLocalBuilder .forEach (ThreadLocalBuilder ::flush );
219+ // Collect token to property keys for final union
220+ var labelTokenToPropertyKeys = new ArrayList <NodeLabelTokenToPropertyKeys >();
221+ this .threadLocalBuilder .forEach (tlb -> labelTokenToPropertyKeys .add (tlb .nodeLabelTokenToPropertyKeys ));
222+ // Clean up resources held by local builders
223+ this .threadLocalBuilder .close ();
224+
225+ return labelTokenToPropertyKeys ;
226+ }
227+
228+ private NodeSchema buildNodeSchema (
229+ IdMap idMap ,
230+ Collection <NodeLabelTokenToPropertyKeys > localLabelTokenToPropertyKeys ,
231+ Map <String , NodeProperty > nodeProperties
232+ ) {
233+
234+ // Collect the property schemas from the imported property values.
235+ var propertyKeysToSchema = nodeProperties
223236 .entrySet ()
224237 .stream ()
225238 .collect (Collectors .toMap (Map .Entry ::getKey , entry -> entry .getValue ().propertySchema ()));
226-
227- // consider node labels without properties
239+ // Union the label to property key mappings from each import thread.
240+ var globalLabelTokenToPropertyKeys = localLabelTokenToPropertyKeys
241+ .stream ()
242+ .reduce (
243+ NodeLabelTokenToPropertyKeys .lazy (),
244+ (left , right ) -> NodeLabelTokenToPropertyKeys .union (left , right , propertyKeysToSchema )
245+ );
246+ // Collect node labels without properties from the id map
247+ // as they are not stored in the above union mapping.
228248 var nodeLabels = new HashSet <>(idMap .availableNodeLabels ());
229- // and also node labels with associated properties
230- nodeLabels .addAll (nodeLabelTokenToPropertyKeys .nodeLabels ());
231-
232- nodeLabels .forEach (nodeLabel -> {
233- nodeSchema .addLabel (
234- nodeLabel ,
235- this .nodeLabelTokenToPropertyKeys .propertySchemas (nodeLabel , importPropertySchemas )
249+ // Add labels that actually have node properties attached.
250+ localLabelTokenToPropertyKeys .forEach (localMapping -> nodeLabels .addAll (localMapping .nodeLabels ()));
251+
252+ // Use all labels and the global label to property
253+ // key mapping to construct the final node schema.
254+ return nodeLabels .stream ()
255+ .reduce (
256+ NodeSchema .empty (),
257+ (unionSchema , nodeLabel ) -> unionSchema .addLabel (
258+ nodeLabel ,
259+ globalLabelTokenToPropertyKeys .propertySchemas (nodeLabel , propertyKeysToSchema )
260+ ),
261+ (lhs , rhs ) -> lhs
236262 );
237- });
238-
239- return nodeSchema ;
240263 }
241264
242265 private Map <String , NodeProperty > buildProperties (IdMap idMap ) {
0 commit comments