neomodel是neo4j数据库的OGM(Object Graph Mapper)的python库,基于neo4j_driver开发。当在开发过程中需要查询节点的属性时,用这个库就会非常方便,也使代码简洁流畅。
-
安装neonodel的Python包
-
pypi源安装
pip install neomodel
-
从github安装
pip install git+git://github.com/neo4j-contrib/neomodel.git@HEAD#egg=neomodel-dev
-
连接neo4j数据库
-
config配置
from neomodel import config config.DATABASE_URL = 'bolt://neo4j:neo4j@localhost:7687' # default
-
db配置
from neomodel import db db.set_connection('bolt://neo4j:neo4j@localhost:7687')
-
Nodes
-
通过StructuredNode构建映射节点类
from neomodel import (config, StructuredNode, StringProperty, IntegerProperty, UniqueIdProperty, RelationshipTo, RelationshipFrom) config.DATABASE_URL = 'bolt://neo4j:password@localhost:7687'
class Country(StructuredNode): code = StringProperty(unique_index=True, required=True)# traverse incoming IS_FROM relation, inflate to Person objects inhabitant = RelationshipFrom('Person', 'IS_FROM')
class Person(StructuredNode): uid = UniqueIdProperty() name = StringProperty(unique_index=True) age = IntegerProperty(index=True, default=0)
# traverse outgoing IS_FROM relations, inflate to Country objects
country = RelationshipTo(Country, 'IS_FROM')Country和Person节点类都是继承StructuredNode构建的,节点类的属性通过实例化不同的数据类构建,这些属性也只是我们自己创建的节点属性,与neo4j自己生成的节点属性无关,比如id,labels等。
-
Create, Save, Delete操作
实例化节点、属性赋值、保存、删除、重载、等jim = Person(name='Jim', age=3).save() jim.age = 4jim.save()
# validation happens here
jim.delete() jim.refresh()
# reload properties from neo
jim.id
# neo4j internal id -
查找节点
使用“.nodes”类属性查找节点,根据条件筛选节点# raises Person.DoesNotExist if no match
jim = Person.nodes.get(name='Jim')
# Will return None unless bob exists
someone = Person.nodes.get_or_none(name='bob')
# Will return the first Person node with the name bob. This raises Person.DoesNotExist if there's no match.
someone = Person.nodes.first(name='bob')
# Will return the first Person node with the name bob or None if there's no match
someone = Person.nodes.first_or_none(name='bob')
# Return set of nodes
people = Person.nodes.filter(age__gt=3) -
Relationships
-
使用StructuredRel构建映射边的类
class FriendRel(StructuredRel): since = DateTimeProperty( default=lambda: datetime.now(pytz.utc) ) met = StringProperty()
-
class Person(StructuredNode): name = StringProperty() friends = RelationshipTo('Person', 'FRIEND', model=FriendRel) rel = jim.friends.connect(bob) rel.since # datetime object
通过Relationship构建两个类之间无向的关系,通过RelationshipTo或RelationshipFrom构建类之间有向关系,通过model指定关系实例继承的类,继承相关的属性。
-
基数限制
如果对边加上基数限制,需要在边的两个方向上分别去做定义,对cardinality作设置。class Person(StructuredNode):
car = RelationshipTo('Car', 'OWNS', cardinality=One)class Car(StructuredNode):
owner = RelationshipFrom('Person', 'OWNS', cardinality=One) -
实例化及常用方法
rel = jim.friends.connect(bob, {'since': yesterday, 'met': 'Paris'}) print(rel.start_node().name)
# jim
print(rel.end_node().name)
# bob
rel.met = "Amsterdam"rel.save()通过connect方法实例化一条边,还可以指定属性筛选;
通过start_node、end_node可以查看边的属性;
还可以对边进行赋值,并保存。
此外,当两个节点存在一种关系时,则可以使用relationship这种方法rel = jim.friends.relationship(bob)
-
Property types
属性默认值设置,可以是具体值,也可以是函数
from uuid import uuid4
my_id = StringProperty(unique_index=True, default=uuid4)
my_datetime = DateTimeProperty(default=lambda: datetime.now(pytz.utc))
StringProperty 的Choices
class Person(StructuredNode):
SEXES = {'F': 'Female', 'M': 'Male', 'O': 'Other'}
sex = StringProperty(required=True, choices=SEXES)
tim = Person(sex='M').save()
tim.sex # M
tim.get_sex_display() # 'Male'
-
Array Properties
class Person(StructuredNode):
names = ArrayProperty(StringProperty(), required=True) bob = Person(names=['bob', 'rob', 'robert']).save() -
UniqueIdProperty
class Person(StructuredNode):
uid = UniqueIdProperty() Person.nodes.get(uid='a12df...') -
Dates and times
DateTimeProperty 对应 UTC epoch value
DateProperty 对应‘YYYY-MM-DD’created = DateTimeProperty(default_now=True)
-Aliasing properties
依赖属性支持节点属性设置class Person(StructuredNode):
full_name = StringProperty(index=True) name = AliasProperty(to='full_name') Person.nodes.filter(name='Jim') # just works -
独特属性名
使用db_property去创建属性,类似于创建类属性class Person(StructuredNode):
name_ = StringProperty(db_property='name')
@property
def name(self): return self.name_.lower() if self.name_ else None
@name.setter
def name(self, value): self.name_ = value -
Other properties
EmailProperty、RegexProperty、NormalProperty -
Advanced queries
-
节点集合与过滤
通过nodes属性获得节点集合,通过filter过滤节点class SupplierRel(StructuredRel):
since = DateTimeProperty(default=datetime.now)
class Supplier(StructuredNode):
name = StringProperty() delivery_cost = IntegerProperty() coffees = RelationshipTo('Coffee', 'SUPPLIES')
class Coffee(StructuredNode):
name = StringProperty(unique_index=True) price = IntegerProperty() suppliers = RelationshipFrom(Supplier, 'SUPPLIES', model=SupplierRel)
# nodes with label Coffee whose price is greater than 2Coffee.nodes.filter(price__gt=2)
try: java = Coffee.nodes.get(name='Java')
except Coffee.DoesNotExist:
print "Couldn't find coffee 'Java'"过滤的方法有以下:
lt - less than
gt - greater than
lte - less than or equal to
gte - greater than or equal to
ne - not equal
in - item in list
isnull - True IS NULL, False IS NOT NULL
exact - string equals
iexact - string equals, case insensitive
contains - contains string value
icontains - contains string value, case insensitive
startswith - starts with string value
istartswith - starts with string value, case insensitive
endswith - ends with string value
iendswith - ends with string value, case insensitive
regex - matches a regex expression
iregex - matches a regex expression, case insensitive -
查看具有关系
利用has方法查看节点是否有一种或多种方法,True 为有,False为无Coffee.nodes.has(suppliers=True)
-
迭代性、切片、计数等
# Iterable
for coffee in Coffee.nodes:
print coffee.name
# Sliceable using python slice syntax
coffee = Coffee.nodes.filter(price__gt=2)[2:]
# Count with __len__
print len(Coffee.nodes.filter(price__gt=2))
if Coffee.nodes:
print "We have coffee nodes!"
-
过滤关系
使用match方法过滤关系nescafe = Coffee.nodes.get(name="Nescafe")
for supplier in nescafe.suppliers.match(since_lt=january):
print supplier.name -
通过属性排序
使用order_by方法排序,加’-‘倒排# Ascending sort
for coffee in Coffee.nodes.order_by('price'):
print coffee, coffee.price
# Descending sort
for supplier in Supplier.nodes.order_by('-delivery_cost'):
print supplier, supplier.delivery_cost -
指定None则不排序,使用’?’随机排序
# Sort in descending order
suppliers = Supplier.nodes.order_by('-delivery_cost')
# Don't order; yield nodes in the order neo4j returns them
suppliers = suppliers.order_by(None)Coffee.nodes.order_by('?')
7. Cypher queries
-
通过StructuredNode的inflate类方法实现cypher查询
class Person(StructuredNode): def friends(self): results, columns = self.cypher("MATCH (a) WHERE id(a)={self} MATCH (a)-[:FRIEND]->(b) RETURN b")
return [self.inflate(row[0]) for row in results] -
StructuredNode外构建cypher
# for standalone queries
from neomodel import db results, meta = db.cypher_query(query, params) people = [Person.inflate(row[0]) for row in results]参考链接:https://neomodel.readthedocs.io/en/latest/index.html
注意:本文归作者所有,未经作者允许,不得转载