2016-11-21 59 views
0

我正在使用Spring-Data和SpringBoot來填充我的Neo4j圖形數據庫。有沒有辦法通過Neo4j Cypher查詢追蹤端到端數據沿襲?

我已經定義了以下Neo4j的實體:

Source實體 - >

@NodeEntity 
public class Source implements Comparable<Source> { 

    @GraphId private Long id; 

    private String name; 
    private SourceType type; 
    private String dataStoreName; 
    private String dataStoreDesc; 

    private Source() { 
     // Empty constructor required as of Neo4j API 2.0.5 
    }; 

    public Source(String name, SourceType type, String dataStoreName, String dataStoreDesc) { 
     this.name = name; 
     this.type = type; 
     this.dataStoreName = dataStoreName; 
     this.dataStoreDesc = dataStoreDesc; 
    } 
    @Relationship(type = "CONTAINS", direction = Relationship.UNDIRECTED) 
    public Set<Field> fields; 

    public void contains(Field field) { 
     if (fields == null) { 
      fields = new HashSet<Field>(); 
     } 
     fields.add(field); 
    } 


    /* Getter and Setters */ 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public SourceType getType() { 
     return type; 
    } 

    public void setType(SourceType type) { 
     this.type = type; 
    } 

    public String getDataStoreName() { 
     return dataStoreName; 
    } 

    public void setDataStoreName(String dataStoreName) { 
     this.dataStoreName = dataStoreName; 
    } 

    public String getDataStoreDesc() { 
     return dataStoreDesc; 
    } 

    public void setDataStoreDesc(String dataStoreDesc) { 
     this.dataStoreDesc = dataStoreDesc; 
    } 

    public Set<Field> getFields() { 
     return fields; 
    } 

    public void setFields(Set<Field> fields) { 
     this.fields = fields; 
    } 

    @Override 
    public int compareTo(Source other) { 
     String name = other.getName(); 
     SourceType type = other.getType(); 
     if(this.name.equalsIgnoreCase(name) && this.type.equals(type)) 
      return 0; 

     return -1; 
    } 

    @Override 
    public int hashCode() { 
     final int prime = 31; 
     int result = 1; 
     result = prime * result + ((name == null) ? 0 : name.hashCode()); 
     result = prime * result + ((type == null) ? 0 : type.hashCode()); 
     return result; 
    } 

    @Override 
    public boolean equals(Object obj) { 
     if (this == obj) 
      return true; 
     if (obj == null) 
      return false; 
     if (getClass() != obj.getClass()) 
      return false; 
     Source other = (Source) obj; 
     if (name == null) { 
      if (other.name != null) 
       return false; 
     } else if (!name.equals(other.name)) 
      return false; 
     if (type != other.type) 
      return false; 
     return true; 
    } 

} 

Field實體 - >

@NodeEntity 
    public class Field implements Comparable<Field> { 

     @GraphId private Long id; 

     private String name; 
     private FieldType fieldType; 
     private SourceType sourceType; 

     private String logicalName; 
     private String dataType; 
     private String dataSize; 
     private String description; 

     private Field() { 
      // Empty constructor required as of Neo4j API 2.0.5 
     }; 

     public Field(String name, FieldType fieldType, SourceType sourceType, String logicalName, String dataType, String dataSize, String description) { 
      this.name = name; 
      this.fieldType = fieldType; 
      this.sourceType = sourceType; 
      this.logicalName = logicalName; 
      this.dataType = dataType; 
      this.dataSize = dataSize; 
      this.description = description; 
     } 
     @Relationship(type = "MAPS-TO", direction = Relationship.UNDIRECTED) 
     public Set<Field> fields; 

     public void mapsTo(Field field) { 
      if (fields == null) { 
       fields = new HashSet<Field>(); 
      } 
      fields.add(field); 
     } 

     /* Getter and Setters */ 

     public String getName() { 
      return name; 
     } 

     public void setName(String name) { 
      this.name = name; 
     } 

     public FieldType getFieldType() { 
      return fieldType; 
     } 

     public void setFieldType(FieldType fieldType) { 
      this.fieldType = fieldType; 
     } 

     public SourceType getSourceType() { 
      return sourceType; 
     } 

     public void setSourceType(SourceType sourceType) { 
      this.sourceType = sourceType; 
     } 

     public String getLogicalName() { 
      return logicalName; 
     } 

     public void setLogicalName(String logicalName) { 
      this.logicalName = logicalName; 
     } 

     public String getDataType() { 
      return dataType; 
     } 

     public void setDataType(String dataType) { 
      this.dataType = dataType; 
     } 

     public String getDataSize() { 
      return dataSize; 
     } 

     public void setDataSize(String dataSize) { 
      this.dataSize = dataSize; 
     } 

     public String getDescription() { 
      return description; 
     } 

     public void setDescription(String description) { 
      this.description = description; 
     } 

     public Set<Field> getFields() { 
      return fields; 
     } 

     public void setFields(Set<Field> fields) { 
      this.fields = fields; 
     } 

     @Override 
     public int compareTo(Field other) { 
      String name = other.getName(); 
      FieldType fieldType = other.getFieldType(); 
      SourceType sourceType = other.getSourceType(); 
      if(this.name.equalsIgnoreCase(name) && this.fieldType.equals(fieldType) && this.sourceType.equals(sourceType)) 
       return 0; 

      return -1; 
     } 

     @Override 
     public int hashCode() { 
      final int prime = 31; 
      int result = 1; 
      result = prime * result + ((fieldType == null) ? 0 : fieldType.hashCode()); 
      result = prime * result + ((name == null) ? 0 : name.hashCode()); 
      result = prime * result + ((sourceType == null) ? 0 : sourceType.hashCode()); 
      return result; 
     } 

     @Override 
     public boolean equals(Object obj) { 
      if (this == obj) 
       return true; 
      if (obj == null) 
       return false; 
      if (getClass() != obj.getClass()) 
       return false; 
      Field other = (Field) obj; 
      if (fieldType != other.fieldType) 
       return false; 
      if (name == null) { 
       if (other.name != null) 
        return false; 
      } else if (!name.equals(other.name)) 
       return false; 
      if (sourceType != other.sourceType) 
       return false; 
      return true; 
     } 


    } 

所以,一個SourceCONTAINSField秒。而一個FieldMAPS-TO一個或多個其他Field s。

每個SourceSourceType

我不同的SourceType s是:生產者,入場,舞臺,中級,出境,消費者。

public enum SourceType { 
     PRODUCER, INBOUND, STAGING, INTERMEDIATE, OUTBOUND, CONSUMER; 
    } 

每個FieldFieldType的。我的不同FieldType是:FILE_FIELD,DB_COLUMN。

public enum FieldType { 
    FILE_FIELD, DB_COLUMN; 
} 

我的數據血統如下: 生產者 - >入站 - > STAGING - >中間體 - >出站 - >消費

我現在正在尋找一種先進的Cypher查詢如果我在CONSUMER Source中提供Field,我可以跟蹤其歷史返回,直到PRODUCER Source

同樣,我也找了查詢通過,如果我的生產者Source提供Field,我能夠追蹤它的後裔向前直到消費者Source

我試圖建立使用shortestPathneighbors函數查詢,但它似乎並沒有拉我正在尋找的結果。

任何建議/指針將不勝感激。

在此先感謝!

UPDATE-1在我的數據沿襲

背景: 我的應用程序從外部應用程序(產生)獲取文件。 我知道外部應用程序的數據庫表/列填充了文件中的字段。 所以這裏,PRODUCER將是我的Source節點;外部應用程序(填充文件)的每個table.column是Field節點,PRODUCER Source節點將與所有Field節點(表示填充文件的外部應用程序數據庫表的table.column)有CONTAINS關係。

來自外部應用程序的文件被稱爲INBOUND。它是一個逗號分隔的文件。 我知道文件中的字段名稱以及順序是什麼。 所以在這裏,INBOUND將是我的Source節點;文件中的每個字段將是一個Field節點,並且INBOUND Source節點將與所有Field節點(表示入站文件中的文件字段)具有CONTAINS關係。 INBOUND Source的每個Field節點與PRODUCER Source(一對一映射)的Field節點將具有MAPS_TO關係。

繼續類似的工作流程,我的下一個階段被稱爲STAGING,其中我將入站文件字段加載到我的數據庫表/列中。 所以在這裏,STAGING將是我的Source節點,並且數據庫表的每一列(我加載文件字段時)都將表示一個Field節點。 STAGING源節點將與所有Field節點(表示我加載文件字段的db表的db table.column)具有CONTAINS關係。 同樣,STAGING Source的每個Field節點將與INBOUND Source(一對一映射)的Field節點具有MAPS_TO關係。

類似我的下一個階段是INTERMEDIATE。在這個階段,我正在查詢我加載輸入文件字段的表,然後將輸出刷新到另一個文件中(根據我的業務用例,我可能會選擇查詢全部或只有一部分表列從輸入文件填充)。 我知道什麼字段以什麼順序進入我的INTERMEDIATE文件。 所以在這裏,INTERMEDIATE是我的Source節點,每個進入INTERMEDIATE文件的字段代表我的Field節點。也中間Source將與表示中間文件中的字段的所有Field節點具有CONTAINS關係。 此外,這些Field節點中的每一個都將與STAGING Source(一對一映射)的字段具有MAPS_TO關係。

同樣,我已經OUTBOUND階段和最後消費階段。

...(我希望你現在能夠以可視化的血統)

我查詢的目標是,比如說,如果我給一個Field名(代表生產者的TABLE.COLUMN)作爲輸入,那麼我應該能夠追蹤它的血統,直到消費者(即我的血統的最後階段)。

+0

快速註釋:Neo4j標籤不能有連字符,所以您應該將'MAPS-TO'更改爲'MAPS_TO'。 –

+0

「不能有」或「不應該有」?因爲我已經用上面的關係填充了我的db,並且我可以在Neo4j上直觀地看到它們。 – lbvirgo

+0

好的,所以你可以在圖中插入帶連字符的標籤,但Cypher不支持這些標籤。如果你試圖運行查詢MATCH(n) - [:MAPS-TO] - >(m)RETURN n,m',你將得到:'Invalid input' - ':期望一個標識符字符,空格,' |',長度說明,屬性圖或']'(第1行,第17列(偏移量:16))'。 –

回答

0

我可以通過下面的查詢來獲取所需的數據沿襲:

MATCH (f5:Field)-[:MAPS_TO]-(f4:Field)-[:MAPS_TO]-(f3:Field)-[:MAP‌​S_TO]-(f2:Field)-[:M‌​APS_TO]-(f1:Field)-[‌​:MAPS_TO]-(f:Field)<‌​-[:CONTAINS]-(s:Sour‌​ce {type: "SOURCE"}) WHERE f.name="<my input source field>" RETURN f,s,f1,f2,f3,f4,f5 
0

同樣,我也找了查詢通過,如果我的生產者Source提供Field,我能夠追蹤它的後裔向前直到消費者Source

我不認爲我完全理解你的數據模型尚未&要求,但這裏是這個查詢一個想法:

MATCH 
    (:Field {name: { fieldName } })<-[:CONTAINS]- 
    (:Source {type: "PRODUCER" })-[:MAPS_TO]-> 
    (:Source {type: "INBOUND"  })-[:MAPS_TO]-> 
    (:Source {type: "STAGING"  })-[:MAPS_TO]-> 
    (:Source {type: "INTERMEDIATE"})-[:MAPS_TO]-> 
    (:Source {type: "OUTBOUND" })-[:MAPS_TO]-> 
    (consumer:Source {type: "CONSUMER" }) 
RETURN consumer 
+0

Tx,但這似乎沒有讓我的血統。我剛更新了我的初始文章以詳細描述我的數據模型。對不起,很長的描述,但只是想確保我的血統正確理解。 Tx提供任何進一步的建議/指針。 – lbvirgo

+0

我能夠通過以下查詢獲得所需的數據沿襲:'MATCH (f5:Field) - [:MAPS_TO] - (f4:Field) - [:MAPS_TO] - (f3:Field) - [:MAPS_TO] - (f2:場) - [:MAPS_TO] - (f1:場) - [:MAPS_TO] - (f:場)< - [:CONTAINS] - (s:Source {type:「SOURCE」}) WHERE f。 name =「<我的輸入源字段>」 RETURN f,s,f1,f2,f3,f4,f5' – lbvirgo

+0

太棒了!請隨時將您的解決方案作爲單獨的答案發布,並接受它。有一件事需要考慮:我會爲名稱使用一個參數,因此條件變爲'f.name = {name}',您可以在調用過程中設置'name'參數。 –

相關問題