1.ProjectEntity
(project:ProjectEntity {
key:key//项目key
name:name//项目名称
})
id 1 项目id
key:key//项目key
name:name//项目名称
name realworld 项目名称
CREATE INDEX idx_project_key IF NOT EXISTS FOR (m:ProjectEntity) ON (m.key) ;
2.ScanRecordEntity
(scanRecord:ScanRecordEntity){
scanId:scanId//扫描id
}
CREATE INDEX idx_scan_record_scanId IF NOT EXISTS FOR (m:ScanRecordEntity) ON (m.scanId) ;
//扫描信息所属关系 1:n
(:ProjectEntity)-[:HAS_SCAN_RECORD]->(:ScanRecordEntity)
3.VCSEntity
(vcsEntity:VCSEntity{
name:name,//仓库名称
path:path,//仓库本地绝对路径
vcsType:vcsType//仓库类型 git
})
VCSEntity
id 7aaf76a6-385a-480e-9377-335d798bb516
name realworld-mdd
path D:\code\realworld-mdd
vcsType git
createdAt 1678551239850
updatedAt 1678551239850
//版本控制信息关系 1:1
(:ProjectEntity)-[:VCS_BY]->(:VCSEntity)
4.ModuleEntity
(module:ModuleEntity {
name:name//模块名称
})
ModuleEntity
id 8a078585-5c06-43be-93b0-dd551244c64c
name root 模块名称
createdAt 1678551238729
updatedAt 1678551239249
//模块包含关系 1:n
(:ScanRecordEntity)-[:CONTAINS]->(:ModuleEntity)
5.FileEntity
(file:FileEntity{
name:name,//文件名
type:type,//文件类型 file/folder
relativePath:relativePath//相对路径
})
FileEntity
id d9cbe0a4-3f60-4704-a419-7489ee530fc8 文件id
name user
type folder 文件类型 file/folder
relativePath src-gen\main\java\cn\hylstudio\mdse\demo\realworld\model\response\user 相对路径
createdAt 1678551233924
updatedAt 1678551239353
CREATE INDEX idx_file_id IF NOT EXISTS FOR (m:FileEntity) ON (m.id) ;
CREATE INDEX idx_file_relativePath IF NOT EXISTS FOR (m:FileEntity) ON (m.relativePath) ;
//符号连接关系 n:1
(:FileEntity)-[:SYMBOL_LINK]->(:FileEntity)
//项目根目录 1:1
(:ScanRecordEntity)-[:ROOT_AT]->(:FileEntity)
//模块根目录 1:n
(:ModuleEntity)-[:MODULE_ROOT{
type: type//src testSrc resources testResources
}]->(:FileEntity)
6.PsiElementEntity
(:PsiElementEntity{//语法树
className:className//psiElement实现类
startOffset //文本开始位置
endOffset//文本结束位置
originText//原始文本
})
PsiElementEntity
id f30db139-4a90-4c10-b740-ac85514ab787
className com.intellij.psi.impl.source.PsiImportListImpl
startOffset 59
endOffset 857
originText 'java code'
createdAt 1680274076903
updatedAt 1680274076903
6.1.类
com.intellij.psi.impl.source.PsiClassImpl
$.qualifiedName str //全限定类名 com.a.b.c.ClassA
6.2.注解
com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl
$.qualifiedName str //全限定类名 com.a.b.c.AnnotationA
$.attributes arr //属性数组
$.attributes[i] obj
$.attributes[i].name str //属性名
$.attributes[i].values arr //属性值
$.attributes[i].values[i] str //属性值
//文件包含语法树根节点 1:n
(:FileEntity)-[:HAS_PSI_ELEMENTS]->(:PsiElementEntity)
//语法树包含关系 1:n
(:PsiElementEntity)-[:CONTAINS]->(:PsiElementEntity)
7.AnnotationAttrEntity
(:AnnotationAttrEntity{//注解属性
name
values
})
CREATE INDEX idx_psi_element_id IF NOT EXISTS FOR (m:PsiElementEntity) ON (m.id) ;
CREATE INDEX idx_psi_className IF NOT EXISTS FOR (m:PsiElementEntity) ON (m.className) ;
CREATE INDEX idx_psi_qualifiedName IF NOT EXISTS FOR (m:PsiElementEntity) ON (m.qualifiedName) ;
//注解关系 1:n
(:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiClassImpl'})-[:HAS_ANNOTATION]->(:PsiElementEntity{className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl'})
(:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiMethodImpl'})-[:HAS_ANNOTATION]->(:PsiElementEntity{className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl'})
//注解和注解属性的关系1:n
(:PsiElementEntity{className:"com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl"})-[:HAS_ATTR]->(:AnnotationAttrEntity)
8.ApiEndpointEntity
(:ApiEndpointEntity{
scanId: scanId,
method: httpMethod//RequestMethod.GET POST ....
path: "/1/2/3" // /api/public/1/2/3
})
CREATE INDEX idx_api_endpoint_scanId IF NOT EXISTS FOR (m:ApiEndpointEntity) ON (m.scanId) ;
CREATE INDEX idx_api_endpoint_path IF NOT EXISTS FOR (m:ApiEndpointEntity) ON (m.path) ;
//api入口绑定关系 1:n
(method:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiMethodImpl'})
-[:HAS_ANNOTATION]->
(methodAnnotation:PsiElementEntity{
className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl',
qualifiedName: 'org.springframework.web.bind.annotation.RequestMapping'
})
(method:PsiElementEntity)-[:HAS_ENDPOINT]->(:ApiEndPointEntity)
//查看IDEA日志
Get-Content .\build\idea-sandbox\system\log\idea.log -Tail 100 -Wait
//列出所有项目
MATCH (project:ProjectEntity)
RETURN project.key
//列出指定项目下的扫描记录
MATCH (project:ProjectEntity)-[:HAS_SCAN_RECORD]->(scanRecord:ScanRecordEntity)
WHERE project.key = "realworld-mdd"
RETURN scanRecord.scanId, scanRecord.createdAt
ORDER BY scanRecord.createdAt DESC
//查看某次扫描记录
MATCH (project:ProjectEntity)
-[:HAS_SCAN_RECORD]->
(scanRecord:ScanRecordEntity)
-[*]-
(related)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db"
RETURN project, scanRecord, related
// 查找要连接ModuleRoot的FileEntity和FileEntity
MATCH (scanRecord:ScanRecordEntity)-[rr:CONTAINS]->(module:ModuleEntity)-[rrr:MODULE_ROOT]->(moduleRoot:FileEntity)
MATCH (scanRecord)-[:ROOT_AT]->(:FileEntity)-[:CONTAINS*1..]->(srcRoot:FileEntity)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db" AND
moduleRoot.relativePath = srcRoot.relativePath AND
moduleRoot <> srcRoot
RETURN scanRecord, rr, module, rrr, moduleRoot, srcRoot
// 连接ModuleRoot的FileEntity和FileEntity
MATCH (scanRecord:ScanRecordEntity)-[rr:CONTAINS]->(module:ModuleEntity)-[rrr:MODULE_ROOT]->(moduleRoot:FileEntity)
MATCH (scanRecord)-[:ROOT_AT]->(:FileEntity)-[:CONTAINS*1..]->(srcRoot:FileEntity)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db" AND
moduleRoot.relativePath = srcRoot.relativePath AND
moduleRoot <> srcRoot
MERGE (moduleRoot)-[rrrr:SYMBOL_LINK]->(srcRoot)
RETURN scanRecord, rr, module, rrr, moduleRoot, rrrr, srcRoot
// 断开连接ModuleRoot的FileEntity和FileEntity
MATCH (scanRecord:ScanRecordEntity)-[rr:CONTAINS]->(module:ModuleEntity)-[rrr:MODULE_ROOT]->(moduleRoot:FileEntity)
MATCH (scanRecord)-[:ROOT_AT]->(:FileEntity)-[:CONTAINS*1..]->(srcRoot:FileEntity)
MATCH (moduleRoot)-[link:SYMBOL_LINK]->(srcRoot:FileEntity)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db" AND
moduleRoot.relativePath = srcRoot.relativePath AND
moduleRoot <> srcRoot
DELETE link
RETURN scanRecord, rr, module, rrr, moduleRoot, srcRoot
// -[:TYPE*minHops..maxHops]->
//使用这个连接查询后面的文件树
MATCH (project:ProjectEntity)
-[:HAS_SCAN_RECORD]->(scanRecord:ScanRecordEntity)
-[:CONTAINS]->(module:ModuleEntity)
-[:MODULE_ROOT]->(moduleRoot:FileEntity)
-[:SYMBOL_LINK]->(srcRoot:FileEntity)
-[:CONTAINS*1..]->(files)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db"
RETURN project, scanRecord, module, moduleRoot, srcRoot, files
//查找指定扫描记录里的单个文件
MATCH
(scanRecord:ScanRecordEntity)-[:ROOT_AT|CONTAINS*1..]->(file:FileEntity)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db"
AND
file.relativePath = "src\\main\\java\\cn\\hylstudio\\mdse\\demo\\realworld\\controller\\user\\UserController.java"
RETURN file
//查看指定文件的语法树
MATCH
(scanRecord:ScanRecordEntity)-[:ROOT_AT|CONTAINS*1..]->(file:FileEntity)
-[:HAS_PSI_ELEMENTS|CONTAINS*1..]->(elements:PsiElementEntity)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db"
AND
file.relativePath = "src\\main\\java\\cn\\hylstudio\\mdse\\demo\\realworld\\controller\\user\\UserController.java"
RETURN file,elements
//获取当前文件所有的语法树节点类型
// RETURN distinct(psiElements.className)
"com.intellij.psi.impl.source.tree.java.PsiPackageStatementImpl"
"com.intellij.psi.impl.source.tree.java.PsiKeywordImpl"
"com.intellij.psi.impl.source.PsiJavaCodeReferenceElementImpl"
"com.intellij.psi.impl.source.tree.java.PsiJavaTokenImpl"
"com.intellij.psi.impl.source.tree.java.PsiReferenceParameterListImpl"
"com.intellij.psi.impl.source.tree.java.PsiIdentifierImpl"
"com.intellij.psi.impl.source.tree.PsiWhiteSpaceImpl"
"com.intellij.psi.impl.source.PsiImportListImpl"
"com.intellij.psi.impl.source.PsiImportStatementImpl"
"com.intellij.psi.impl.source.PsiClassImpl"
"com.intellij.psi.impl.source.PsiMethodImpl"
"com.intellij.psi.impl.source.PsiModifierListImpl"
"com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl"
"com.intellij.psi.impl.source.tree.java.PsiAnnotationParamListImpl"
"com.intellij.psi.impl.source.tree.java.PsiNameValuePairImpl"
"com.intellij.psi.impl.source.tree.java.PsiReferenceExpressionImpl"
"com.intellij.psi.impl.source.tree.java.PsiLiteralExpressionImpl"
"com.intellij.psi.impl.source.PsiTypeElementImpl"
"com.intellij.psi.impl.source.PsiParameterListImpl"
"com.intellij.psi.impl.source.PsiParameterImpl"
"com.intellij.psi.impl.source.tree.java.PsiCodeBlockImpl"
"com.intellij.psi.impl.source.tree.java.PsiReturnStatementImpl"
"com.intellij.psi.impl.source.tree.java.PsiMethodCallExpressionImpl"
"com.intellij.psi.impl.source.tree.java.PsiExpressionListImpl"
"com.intellij.psi.impl.source.tree.java.PsiExpressionStatementImpl"
"com.intellij.psi.impl.source.tree.java.PsiDeclarationStatementImpl"
"com.intellij.psi.impl.source.tree.java.PsiLocalVariableImpl"
"com.intellij.psi.impl.source.tree.java.PsiIfStatementImpl"
"com.intellij.psi.impl.source.tree.java.PsiBlockStatementImpl"
"com.intellij.psi.impl.source.tree.java.PsiBinaryExpressionImpl"
"com.intellij.psi.impl.source.tree.java.PsiTypeParameterListImpl"
"com.intellij.psi.impl.source.PsiReferenceListImpl"
"com.intellij.psi.impl.source.tree.PsiCommentImpl"
"com.intellij.psi.impl.source.tree.java.PsiPrefixExpressionImpl"
"com.intellij.psi.impl.source.tree.java.PsiSwitchStatementImpl"
"com.intellij.psi.impl.source.tree.java.PsiSwitchLabelStatementImpl"
"com.intellij.psi.impl.source.tree.java.PsiCaseLabelElementListImpl"
"com.intellij.psi.impl.source.tree.java.PsiNewExpressionImpl"
"com.intellij.psi.impl.source.PsiDiamondTypeElementImpl"
"com.intellij.psi.impl.source.tree.java.PsiForeachStatementImpl"
"com.intellij.psi.impl.source.tree.java.PsiClassObjectAccessExpressionImpl"
"com.intellij.psi.impl.source.PsiFieldImpl"
"com.intellij.psi.impl.source.tree.java.PsiArrayInitializerMemberValueImpl"
"com.intellij.psi.impl.source.javadoc.PsiDocCommentImpl"
"com.intellij.psi.impl.source.javadoc.PsiDocTokenImpl"
//获取一个文件中的类、类的注解、类中的方法、方法注解,不使用HAS_ANNOTATION
MATCH (scanRecord:ScanRecordEntity)-[:ROOT_AT|CONTAINS*1..]->(file:FileEntity)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db"
AND
file.relativePath = "src\\main\\java\\cn\\hylstudio\\mdse\\demo\\realworld\\controller\\user\\UserController.java"
OPTIONAL MATCH (file)-[:HAS_PSI_ELEMENTS|CONTAINS*1..]->
//match class info
(class:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiClassImpl'})
OPTIONAL MATCH (class)-[:CONTAINS]->
(classModifierList:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiModifierListImpl'})
OPTIONAL MATCH (classModifierList)-[:CONTAINS]->
(classAnnotation:PsiElementEntity{className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl'})
OPTIONAL MATCH (classAnnotation)-[:HAS_ATTR]->
(classAnnotationAttr:AnnotationAttrEntity)
//match method info
OPTIONAL MATCH (class)-[:CONTAINS]->
(method:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiMethodImpl'})
OPTIONAL MATCH (method)-[:CONTAINS]->
(mehotdModifierList:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiModifierListImpl'})
OPTIONAL MATCH (mehotdModifierList)-[:CONTAINS]->
(methodAnnotation:PsiElementEntity{className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl'})
OPTIONAL MATCH (methodAnnotation)-[:HAS_ATTR]->
(methodAnnotationAttr:AnnotationAttrEntity)
return
class,classModifierList,classAnnotation,classAnnotationAttr,
method,mehotdModifierList,methodAnnotation,methodAnnotationAttr
//连接所有class和method上的注解
MATCH (scanRecord:ScanRecordEntity)-[:MODULE_ROOT|SYMBOL_LINK|CONTAINS*1..]->(file:FileEntity)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db"
AND
file.type = "file"
MATCH (file)-[:HAS_PSI_ELEMENTS|CONTAINS*1..]->
//match class info
(class:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiClassImpl'})
MATCH (class)-[:CONTAINS]->
(classModifierList:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiModifierListImpl'})
MATCH (classModifierList)-[:CONTAINS]->
(classAnnotation:PsiElementEntity{className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl'})
MATCH (classAnnotation)-[:HAS_ATTR]->
(classAnnotationAttr:AnnotationAttrEntity)
//match method info
MATCH (class)-[:CONTAINS]->
(method:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiMethodImpl'})
MATCH (method)-[:CONTAINS]->
(methodModifierList:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiModifierListImpl'})
MATCH (methodModifierList)-[:CONTAINS]->
(methodAnnotation:PsiElementEntity{className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl'})
MATCH (methodAnnotation)-[:HAS_ATTR]->
(methodAnnotationAttr:AnnotationAttrEntity)
MERGE (class)-[:HAS_ANNOTATION]->(classAnnotation)
MERGE (method)-[:HAS_ANNOTATION]->(methodAnnotation)
//获取一个文件中的类、类的注解、类中的方法、方法注解,使用HAS_ANNOTATION
MATCH (scanRecord:ScanRecordEntity)-[:ROOT_AT|CONTAINS*1..]->(file:FileEntity)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db"
AND
file.relativePath = "src\\main\\java\\cn\\hylstudio\\mdse\\demo\\realworld\\controller\\user\\UserController.java"
OPTIONAL MATCH (file)-[:HAS_PSI_ELEMENTS|CONTAINS*1..]->
//match class info
(class:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiClassImpl'})
OPTIONAL MATCH (class)-[:HAS_ANNOTATION]->
(classAnnotation:PsiElementEntity{className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl'})
OPTIONAL MATCH (classAnnotation)-[:HAS_ATTR]->
(classAnnotationAttr:AnnotationAttrEntity)
//match method info
OPTIONAL MATCH (class)-[:CONTAINS]->
(method:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiMethodImpl'})
OPTIONAL MATCH (method)-[:HAS_ANNOTATION]->
(methodAnnotation:PsiElementEntity{className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl'})
OPTIONAL MATCH (methodAnnotation)-[:HAS_ATTR]->
(methodAnnotationAttr:AnnotationAttrEntity)
return
class,classAnnotation,classAnnotationAttr,
method,methodAnnotation,methodAnnotationAttr
//单个文件,根据注解查询Api入口,并关联到方法上
MATCH (scanRecord:ScanRecordEntity)-[:ROOT_AT|CONTAINS*1..]->(file:FileEntity)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db"
AND
file.relativePath = "src\\main\\java\\cn\\hylstudio\\mdse\\demo\\realworld\\controller\\user\\UserController.java"
MATCH (file)-[:HAS_PSI_ELEMENTS|CONTAINS*1..]->
//match class info
(class:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiClassImpl'})
OPTIONAL MATCH (class)-[:HAS_ANNOTATION]->
(classAnnotation:PsiElementEntity{
className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl',
qualifiedName: 'org.springframework.web.bind.annotation.RequestMapping'
})
MATCH (classAnnotation)-[:HAS_ATTR]->
(classAnnotationAttr:AnnotationAttrEntity{name:"value"})
//match method info
MATCH (class)-[:CONTAINS]->
(method:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiMethodImpl'})
MATCH (method)-[:HAS_ANNOTATION]->
(methodAnnotation:PsiElementEntity{
className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl',
qualifiedName: 'org.springframework.web.bind.annotation.RequestMapping'
})
MATCH (methodAnnotation)-[:HAS_ATTR]->
(apiPathsOnMethod:AnnotationAttrEntity{name:"value"})
MATCH (methodAnnotation)-[:HAS_ATTR]->
(apiMethods:AnnotationAttrEntity{name:"method"})
UNWIND classAnnotationAttr.values AS prefix
UNWIND apiMethods.values AS httpMethod
UNWIND apiPathsOnMethod.values AS path
MERGE (method)-[:HAS_ENDPOINT]->(apiEndpoint:ApiEndpointEntity{method: httpMethod, path: prefix + path})
RETURN method, apiEndpoint
//全量文件,根据注解生成Api入口,并关联到方法和扫描记录上
MATCH (scanRecord:ScanRecordEntity)-[:MODULE_ROOT|SYMBOL_LINK|CONTAINS*1..]->(file:FileEntity)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db"
AND
file.type = "file"
MATCH (file)-[:HAS_PSI_ELEMENTS|CONTAINS*1..]->
//match class info
(class:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiClassImpl'})
OPTIONAL MATCH (class)-[:HAS_ANNOTATION]->
(classAnnotation:PsiElementEntity{
className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl',
qualifiedName: 'org.springframework.web.bind.annotation.RequestMapping'
})
MATCH (classAnnotation)-[:HAS_ATTR]->
(classAnnotationAttr:AnnotationAttrEntity{name:"value"})
//match method info
MATCH (class)-[:CONTAINS]->
(method:PsiElementEntity{className:'com.intellij.psi.impl.source.PsiMethodImpl'})
MATCH (method)-[:HAS_ANNOTATION]->
(methodAnnotation:PsiElementEntity{
className:'com.intellij.psi.impl.source.tree.java.PsiAnnotationImpl',
qualifiedName: 'org.springframework.web.bind.annotation.RequestMapping'
})
MATCH (methodAnnotation)-[:HAS_ATTR]->
(apiPathsOnMethod:AnnotationAttrEntity{name:"value"})
MATCH (methodAnnotation)-[:HAS_ATTR]->
(apiMethods:AnnotationAttrEntity{name:"method"})
UNWIND classAnnotationAttr.values AS prefix
UNWIND apiMethods.values AS httpMethod
UNWIND apiPathsOnMethod.values AS path
MERGE (apiEndpoint:ApiEndpointEntity {scanId: scanRecord.scanId, method: httpMethod, path: prefix + path})
MERGE (method)-[:HAS_ENDPOINT]->(apiEndpoint)
MERGE (scanRecord)-[:HAS_ENDPOINT]->(apiEndpoint)
RETURN method, apiEndpoint
//查找所有ApiEndpoint
MATCH (scanRecord:ScanRecordEntity)-[:HAS_ENDPOINT]->(apiEndpoint:ApiEndpointEntity)
WHERE
scanRecord.scanId = "643e424c1b99436f82551420981669db"
RETURN apiEndpoint