https://dgraph.io/docs/mutations/conditional-upsert/ describes the syntax of an upsert statement:
upsert {
query <query block>
[fragment <fragment block>]
mutation [@if(<condition>)] <mutation block 1>
[mutation [@if(<condition>)] <mutation block 2>]
...
}
This is very abstract and i had trouble finding a working example for the usecase to delete all nodes with a certain type so I placed the stackoverflow question: dgraph - How to delete all nodes with a given type? - Stack Overflow
and didn’t get an answer so i tried things out my self based on the example in: How to bulk delete nodes or cascade delete nodes? and came up with the solution:
https://stackoverflow.com/a/63358827/1497139
upsert {
query {
# get the uids of all Country nodes
countries as var (func: has(<dgraph.type>)) @filter(eq(<dgraph.type>, "Country")) {
uid
}
}
mutation {
delete {
uid(countries) * * .
}
}
}
The upsert works in the Ratel Web GUI using the “mutation” tab. It also is supposed to work via curl.
I then wanted to use this in my code https://github.com/WolfgangFahl/DgraphAndWeaviateTest/blob/master/dg/dgraph.py which is a wrapper for Dgraph that makes available the functions:
- addSchema
- addData
- query
- drop_all
- close
in a straight forward way. As outlined in the python unit test
def testCountries(self):
'''
test handling countries
'''
countryJsonUrl="https://gist.githubusercontent.com/erdem/8c7d26765831d0f9a8c62f02782ae00d/raw/248037cd701af0a4957cce340dabb0fd04e38f4c/countries.json"
with urllib.request.urlopen(countryJsonUrl) as url:
countryList=json.loads(url.read().decode())
#print(countryList)
cg=Dgraph(debug=True)
cg.drop_all()
schema='''
name: string @index(exact) .
code: string @index(exact) .
capital: string .
location: geo .
type Country {
code
name
location
capital
}'''
cg.addSchema(schema)
startTime=time.time()
for country in countryList:
# rename dictionary keys
#country['name']=country.pop('Name')
country['code']=country.pop('country_code')
country['dgraph.type']='Country'
lat,lng=country.pop('latlng')
country['location']={'type': 'Point', 'coordinates': [lng,lat] }
print(country)
cg.addData(countryList)
elapsed=time.time() - startTime
print("adding %d countries took %5.1f s" % (len(countryList),elapsed))
query='''{
# list of countries
countries(func: has(code)) {
uid
name
code
capital
location
}
}'''
queryResult=cg.query(query)
self.assertTrue("countries" in queryResult)
countries=queryResult["countries"]
self.assertEqual(247,len(countries))
schemaResult=cg.query("schema{}")
print(schemaResult)
self.assertTrue("schema" in schemaResult)
schema=schemaResult["schema"]
self.assertEqual(8,len(schema))
# see http://discuss.dgraph.io/t/running-upsert-in-python/9364
"""mutation='''
upsert {
query {
# get the uids of all Country nodes
countries as var (func: has(<dgraph.type>)) @filter(eq(<dgraph.type>, "Country")) {
uid
}
}
mutation {
delete {
uid(countries) * * .
}
}
}'''
cg.mutate(mutation)"""
cg.close
Please note how the upsert is commented out - this is the issue. The python library should IMHO support the upsert syntax directly and allow for calling upsert with an upsert command as per the documentation of it’s syntax. For me it does not make sense that the parts:
- upsert syntax documentation
- upsert example works via RATEL GUI
- upsert example works via curl
are consistent and then the way things are handled is completly different in the python library.
This inconsistency is very frustrating and will make adopting Dgraph hard since there is an extra step that needs some trial and error and fiddling. For the time being i don’t even have work-around how to get things working with the current python library. I’d appreciate a workaround example but the workaround is not the solution !!! It is very important to fix the library! Again: if you do not fix the library there will be a very bad inconsistency making live hard for developers because your documentation, your GUI your curl and your library will not behave in the same way.