当前位置: 首页 > 科技观察

为什么要在你的下一个RubyAPP中使用Neo4j

时间:2023-03-14 21:32:04 科技观察

每天,我需要存储大量的数据,当我积累了很多的时候,我可以使用很多工具,比如PostgreSQL、MySQL、SQLite、Redis和MongoDB的经验,当你熟练使用这些工具时,我认为它们没有任何乐趣。我从心底里热爱Ruby,因为它很有趣,并且允许我按照自己的方式做一些很酷的事情。但是我没有意识到这样一个数据处理工具会影响我很长一段时间,虽然它让我找到了新的乐趣,所以请让我向您介绍一下Neo4j。什么是Neo4j?Neo4j是一个图形数据库!这意味着它针对管理和查询实体(节点)之间的关系(节点)进行了优化,而不是像使用表的关系数据库。为什么这很棒?想象一个没有外键的世界。数据库中的每个实体都密切相关,可以直接引用其他实体。如果您要引用的关系没有关系表或搜索扫描,您只需要几个链接。这完美地展示了典型的对象模型。这是非常令人兴奋的,因为Neo4j可以提供很多我们期望从数据库获得的功能,并且它为我们提供了查询复杂数据图的工具。介绍ActiceNote为了链接Neo4j,我们将使用neo4jgem。可以在gem文档中找到在Rails应用程序中连接到Neo4j的方法。此外,显示代码的应用程序也可作为GitHub存储库中的Rails应用程序使用(使用sitepoint的分支)。您可以在数据库上运行rakeload_sample_data来填充数据库。下面是RailsAPPclassAssetincludeNeo4j::ActiveNodeproperty:titlehas_many:out,:categories,type::HAS_CATEGORYend的一个基本案例,来自AssetManagementRailsAPPclassAssetincludeNeo4j::ActiveNodepropertyend简要说明:1.Neo4jGEM给我们的Neo4j::ActiveNode模块,其中包括我们制作的这个模型2.这个资产意味着这个模型将负责Neo4j标记资产中的所有节点(除了标签一个节点可以有许多起类似作用的标签)3.我们有一个title属性描述每个节点4。我们有一个分类关联与传出的has_many。这个关联帮助我们通过下面的数据库HAS_CATEGORY关系找到分类对象。有了这个模型,我们可以进行基本查询来查找资产并获得其分类2.2.0:001>asset=Asset.first=>#2.2.0:002>asset.categories.to_a=>[#]任何熟悉ActiveRecord或Mongoid的人都会看到它数百次。为了让它更有趣,让我们定义一个类别模型:classCategoryincludeNeo4j::ActiveNodeproperty:namehas_many:in,:assets,origin::categoriesend在这里,我们的关联有一个引用资产模型类别关联的来源选项。如果我们愿意,我们可以再次指定type::HAS_CATEGORY来创建推荐如果我们想要获得与我们的资产共享一个类别的所有资产怎么办?2.2.0:003>asset.categories.assets.to_a=>[#,...]那么会发生什么?ActiveNode生成一个数据库查询,指定从我们的资产到共享同一类别的所有其他资产的路径。然后数据库会将这些资产发回给我们。它使用的查询如下:MATCHAsset436,asset436-[rel1:`HAS_CATEGORY`]->(node3:`Category`),node3<-[rel2:`HAS_CATEGORY`]-(result_assets:`Asset`)WHERE(ID(asset436)={ID_asset436})RETURNresult_assetsParameters:{ID_asset436:436}这种查询语言叫做Cypher,相当于Neo4j的SQL。特别注意圆括号周围的节点和关节的ASCII样式表示的关系。这个Cypher查询有点多余,因为ActiveNode在算法之上生成这些查询。如果要编写此查询,它将看起来像这样:MATCHsource_asset-[:HAS_CATEGORY]->(:Category)<-[:HAS_CATEGORY]-(result_assets:Asset)WHEREID(source_asset)={source_asset_id}RETURNresult_assetsParameters:{source_asset_id:436}另外,我发现Cypher比SQL更简单、更强大,但我们在这里不必太担心Cypher。如果你以后想了解更多关于Cypher的信息,你可以查阅totorials和refcards。如您所见,我们可以使用Neo4j来跨越实体。太奇妙了!我们也可以使用带成对连接的SQL来完成。虽然Cypher看起来很酷,但我们还没有取得任何重大突破。假设我们想使用这些查询来创建一些基于共享类别的资产推荐?我们要对资产进行排序,并按最常见的类别对它们进行排序。让我们在模型中创建一个方法:classAsset...Recommendation=Struct.new(:asset,:categories,:score)defasset_recommendations_by_category(common_links_required=3)categories(:c).assets(:asset).order('count(c)DESC').pluck('asset,collect(c),count(c)').rejectdo|_,_,count|count[#]1#[#,#,…]4#[#,#]2…………有没有注意到没有GROUPBY子句?Neo4j足够聪明,可以意识到collect和count是聚合函数,并在我们的结果中按非聚合列排序(在这种情况下仅资产变量)。注意是SQL!作为最后一步,我们能够创建的不仅仅是同一类别的推荐。假设我们有以下Neo4j子图:除了共享类别,让我们解释一下创建者和查看者资产有多少共同点:classAsset...Recommendation=Struct.new(:asset,:score)defsecret_sauce_recommendationsquery_as(:source).match('source-[:HAS_CATEGORY]->(category:Category)<-[:HAS_CATEGORY]-(asset:Asset)').break.optional_match('source<-[:CREATED]-(creator:User)-[:CREATED]->asset').break.optional_match('source<-[:VIEWED]-(viewer:User)-[:VIEWED]->asset').limit(5).order('scoreDESC').pluck(:asset,'(count(category)*2)+(count(creator)*4)+(count(viewer)*0.1)ASscore').mapdo|other_asset,score|Recommendation.new(other_asset,score)endendend在这里,我们深入研究并开始构建我们自己的查询。结构是相同的,但我们不仅要找到共享同一类别的两个资产之间的路径,还要指定另外两个可选路径。我们可以指定三个可选路径,但Neo4j需要将我们的资产与数据库中的所有其他资产进行比较。对于类别节点使用match而不是optional_match,我们至少需要一个共享类别。这极大地限制了我们的搜索空间。图中有1个共享类别、0个共享创建者和2个共享查看者。这意味着“Ruby”和“RubyonRails”之间的分数将是:(1*2)+(0*4)+(2*0.1)=2.2另请注意,我们对聚合函数的计数(和排序).这对我来说很酷,让我想想就有点兴奋。..轻松授权让我们解决另一个常见问题。假设您的CEO来到您的办公桌前说:“我们已经构建了一个很棒的应用程序,但客户希望能够控制谁可以看到他们的东西。您可以构建一些隐私控制吗?”看起来很简单。让我们依靠一个标志来访问私有资产:classAsset...property:public,default:truedefself.visible_to(user)query_as(:asset).match_nodes(user:user).where("asset.publicORAsset<-[:CREATED]-user").pluck(:asset)endend使用这些设计,您可以显示用户可以看到的所有资产,因为资产是公开的或因为观察者拥有它。没问题,但又不是大问题。在另一个数据库中,您可以搜索两列/属性。让我们感受一下!产品经理来找你说,“嘿,谢谢,但现在人们想让其他用户直接访问他们的私人资料”。没问题!您可以创建一个UI,让用户通过VIEWALBE_BY关系添加和删除他们的资产,并像这样查询它们:classAsset...defself.visible_to(user)query_as(:asset).match_nodes(user:user)。where("asset.publicORasset<-[:CREATED]-userORasset-[:VIEWABLE_BY]->user").pluck(:asset)endend这也是一个连接表。你还是用户获取资产的另一条途径。花一些时间来欣赏Neo4j的无模式本质。满足于您的工作日,斜靠在椅子上,啜饮下午茶。当然,前提是社交媒体客户服务代表说,“用户喜欢新功能,但他们希望能够创建群组并分配群组访问权限。你能做到吗?而且,你能允许任意级别的用户访问吗?”分组?你要深入的看。他们的目光停留了几分钟,然后说:“当然可以!”。那么事情开始变得复杂起来,让我们看一个例子:如果这些资产都是私有代码,那么到目前为止给Matz和tenderlove访问Ruby,让DHH访问RubyonRails。添加分组支持,然后您就可以直接开始分配组了::VIEWABLE_BY]->userORAsset-[:VIEWABLE_BY]->(:Group)<-[:BELONGS_TO]-user").pluck('DISTINCTasset')endend这很简单,因为你只需要添加另一个路径。当然,到目前为止那是我们的旧帽子……Tenderlove和Yehuda将能够看到“RubyonRails”资产,因为他们被“Railsists”分组。另请注意:一些用户现在有多个资产路径(例如Matz通过Rubyists分组和CREATED关联转到Ruby),您需要返回DISTINCT资产。但是,通过分层组织指定任意路径将花费您更多时间。您可以查看Neo4j文档,直到找到称为“变量关系”的内容,然后尝试:classAsset...defself.visible_to(user)query_as(:asset).match_nodes(user:user).where("asset.publicORAsset<-[:CREATED]-userORasset-[:VIEWABLE_BY]->userORasset-[:VIEWABLE_BY]->(:Group)<-[:HAS_SUBGROUP*0..5]-(:Group)<-[:BELONGS_TO]-user").pluck('DISTINCTAsset')endend到此为止!此查询查找有权访问某个组的资产并遍历HAS_SUBGROUP关系0到5,最后以检查用户是否在最后一个组中结束。您是故事的主人公,您的公司会因您快速完成工作而付钱给您。结论您可以使用Neo4j做很多我做不到的很棒的事情(包括使用它令人惊叹的Web界面通过Cyper查找您的数据)。它不仅是一种以简单直观的方式存储数据的绝妙方式,而且还提供了高度相关数据的许多查询效率优势(相信我,即使您没有意识到,您的数据也是高度相关的)。我鼓励您查看Neo4j并在您的下一个项目中试用它。