I am trying to use pymongo to update an existing index:
#!/usr/bin/env python
import pymongo
from pymongo import MongoClient
client = MongoClient()
db = client.alfmonitor
tests = db.tests
post = {
'name' : 'Blah',
'active' : True,
'url' : 'http://www.google.com',
}
tests.insert(post)
tests_list = db.tests.find({'active':True, 'name':'Blah'})
for test in tests_list:
test['active'] = False
test.update(
test,
)
print '===================================================='
for test in db.tests.find():
print test #<- when I print out these, active=True is still showing up
I've been trying to follow documentation and examples I have seen on SO but none of them seem to be working for me. Anyone can explain what I'm doing wrong here?
Thanks!
Use this (don't forget to add multi=True if you want to update all matches):
db.tests.update({'active':True, 'name':'Blah'}, {'$set': {'active': False}}, multi=True)
Why your code isn't working:
for test in tests_list:
# test is of a dict type
test['active'] = False
# You are calling the method of dict type
# that adds all values from dictionary test to dictionary test,
# so nothing goes to database
test.update(
test,
)
When you want to commit changes made to a retrieved doc, use collection.save instead of update:
test['active'] = False
db.tests.save(test)
Both of these worked for me:
db.tests.update(
{'active':True, 'name':'Blah'},
{
'$set': {'active': False}
},
multi=True
)
tests_list = db.tests.find({'active':True, 'name':'Blah'})
for test in tests_list:
test['active'] = False
db.tests.save(test)
Thanks very much traceur and JonnyHK!
Related
Here is my sample code
import boto3
import os
ENV = "dev"
DB = "http://awsservice.com"
REGION = "us-east-1"
TABLE = "traffic-count"
def main():
os.environ["AWS_PROFILE"] = ENV
client = boto3.resource("dynamodb", endpoint_url=DB, region_name=REGION)
kwargs = {'Key': {'id': 'D-D0000012345-P-1'},
'UpdateExpression': 'ADD #count.#car :delta \n SET #parentKey = :parent_key, #objectKey = :object_key',
'ExpressionAttributeValues': {':delta': 1, ':parent_key': 'District-D0000012345', ':object_key': 'Street-1'},
'ExpressionAttributeNames': {'#car': 'car', '#count': 'count', '#parentKey': 'parentKey', '#objectKey': 'objectKey'}}
client.Table(TABLE).update_item(**kwargs)
if __name__ == "__main__":
main()
What I want to achieve is this:
With a single API call (in this update_item), I want to be able to
If the item does not exit. create an item with a map count and initialise it with {'car': 1} and set the fields parent_key and object_key.
or
If the item already exists, update the field to {'car': 2} (if the original count is 1)
Previously, if I did not use a map, I can successfully update with this expression,
SET #count = if_not_exist(#count, :zero) + :delta,
#parentKey = :parent_key, #objectKey = :object_key
However I am getting this error:
botocore.exceptions.ClientError: An error occurred
(ValidationException) when calling the UpdateItem operation: The
document path provided in the update expression is invalid for update
Which document path is causing the problem? How can I fix it?
For those who landed on this page with similar error:
The document path provided in the update expression is invalid for update
The reason may be:
for the item on which the operation is being performed,
this attribute (count, for example) is not yet set.
Considering the sample code from question,
The exception could be coming from all those items where count is empty or not set. So the update query doesn't know on which map the new value(s) (car, for example) needs to be set or updated.
In the question, it was possible for the OP in the beginning because, the attribute is not a map and the process is simply setting the value to count as is. It's not trying to access a key of an unknown map to set the value.
This can be handled by catching the exception. For example:
from botocore.exceptions import ClientError
...
try:
response = table.update_item(
Key={
"pk": pk
},
UpdateExpression="set count.car = :c,
ExpressionAttributeValues={
':c': "some car"
},
ReturnValues="UPDATED_NEW"
)
except ClientError as e:
if e.response['Error']['Code'] == 'ValidationException':
response = table.update_item(
Key={
"pk": pk
},
UpdateExpression="set count = :count",
ExpressionAttributeValues={
':count': {
':c': "some car"
}
},
ReturnValues="UPDATED_NEW"
)
I have the following python code in MongoDB:
input_1 = object_collection.find({"_id": ObjectId(key_1)})
for i in input_1:
print(i)
and it returns this:
{'_id': ObjectId('5d949843cc1e1fc0556983bc'), 'x_input': '11', 'y_input': '22'}
I am interested only in x_input and y_input where I would like to store them in order to calculate the same of them
So you can use 'project' in your query...
from pymongo import MongoClient
from bson import ObjectId
if __name__ == '__main__':
client = MongoClient("localhost:27017", username="barry", password="barry", authSource="admin", authMechanism="SCRAM-SHA-256")
with client.start_session(causal_consistency = True) as my_session:
with my_session.start_transaction():
db = client.mydatabase
collection = db.mycollection
for result in collection.find({"_id": ObjectId("5d97713e11261b4afebe517b")}, {"_id": 0, "x_input": 1}):
print (str(result))
See the bit ...
{"_id": 0, "x_input": 1}
... this instructs the query engine to turn off display for "_id", and turn on display for "x_input". If we specific any 'project' at all, then all fields we want to see must be specified. "_id" is the oddball to this strategy and will always show unless turned off.
Results:
{u'x_input': u'11'}
I am trying to insert data in a mongodb collection from python but the data is not being logged. This is how I am doing it:
from pymongo import MongoClient
import time
class data2db:
def __init__(self):
pass
def enter_data(self,data):
client = MongoClient('127.0.0.1', 27017)
db = client.db
coll=db.Temperature1
post = {"auth": data ,
"Time" : time.asctime(time.localtime(time.time()))}
post_= coll.insert(post)
c=data2db()
c.enter_data("24.3")
When I try to access the data from another method it returns null. This how I do it:
client = MongoClient('127.0.0.1', 27017)
db = client.db
coll=db.Temperature1
print coll.find_one({"_id" : 1})
print coll.find()
listed=str(coll.find({"_id" : 1})).split(' ')
listed=listed[len(listed)-1].split('>')[0]
listed={"_id" : "ObjectID(\""+listed+"\")"}
print coll.find_one(listed)'''
print db.command("collstats", "events")
As you can see, I already tried it in different ways but no matter what, it returns null. If I try to access the dbstats, like this:
print db.command("dbstats")
I get:
{u'extentFreeList': {u'totalSize': 0, u'num': 0}, u'storageSize': 49152, u'ok': 1.0, u'avgObjSize': 64.90566037735849, u'dataFileVersion': {u'major': 4, u'minor': 5}, u'db': u'db', u'indexes': 4, u'objects': 53, u'collections': 6, u'fileSize': 67108864, u'numExtents': 6, u'dataSize': 3440, u'indexSize': 32704, u'nsSizeMB': 16}
THe collection is not even showing on mongo commandline. Desperate for help.
Your code "works' but I think you may have copied-and-pasted something wrong. In particular, you're swapping the use of find() and find_one().
The enter_data() method calls insert() without specifying _id so the driver will invent one for you. That _id ends up being an ObjectId similar to this:
{ "_id" : ObjectId("558019749f43b8c19779c106"), "auth" : "24.3", "Time" : "Tue Jun 16 08:41:24 2015" }
Your code later calls print coll.find_one({"_id" : 1}) which will yield null because the invented _id will not be 1.
find() does not return a record; it returns a cursor. Calling print does not print the contents. Try this instead:
for r in coll.find():
print r
Lastly, because db is special convenience variable in the CLI, I'd avoid naming a database db.
If you try this code.. you can see the problem I have..
class Embedded(EmbeddedDocument):
boxfluxInt = IntField(default=0, db_field='i')
meta = {'allow_inheritance': False}
class Test(Document):
boxflux = MapField(field=EmbeddedDocumentField(Embedded), db_field='x')
meta = {'collection': 'test',
'allow_inheritance': False}
Test.drop_collection()
newTestDoc = Test()
newTestDoc.boxflux['DICTIONARY_KEY'] = Embedded(boxfluxInt=1)
newTestDoc.save()
Test.objects.update_one(inc__boxflux__DICTIONARY_KEY__boxfluxInt=1)
The result in Mongodb is like..
> db.test.findOne()
{
"_id" : ObjectId("4fbdbbc8c450190a50000001"),
"x" : {
"DICTIONARY_KEY" : {
"boxfluxInt" : 1,
"i" : 1
}
}
}
>
As you can see, I intended to increase 'x.DICTIONARY_KEY.i' by 1
but the result is that a new key (boxfluxInt) is created even though I set 'boxfluxInt' 's db_field as 'i'
Is it bug? or am I wrong?
I think the dictionary key ('DICTIONARY_KEY') makes conversion to mongo style db fields impossible.. if I'm correct..
OK this looks like a bug, best place to report them is in github: http://github.com/mongoengine/mongoengine
This wont get fixed until 0.7 as it will break existing users in production. So I'll have to write up migration notes as part of the fix.
For Example.. In Mongodb..
> db.test.findOne({}, {'mapField.FREE':1})
{
"_id" : ObjectId("4fb7b248c450190a2000006a"),
"mapField" : {
"BOXFLUX" : {
"a" : "f",
}
}
}
The 'mapField' field is made of MapField of Mongoengine.
and 'mapField' field has a log of key and data.. but I just retrieved only 'BOXFLUX'..
this query is not working in MongoEngine....
for example..
BoxfluxDocument.objects( ~~ querying ~~ ).only('mapField.BOXFLUX')
AS you can see..
only('mapField.BOXFLUX') or only only('mapField__BOXFLUX') does not work.
it retrieves all 'mapField' data, including 'BOXFLUX' one..
How can I retrieve only a field of MapField???
I see there is a ticket for this: https://github.com/hmarr/mongoengine/issues/508
Works for me heres an example test case:
def test_only_with_mapfields(self):
class BlogPost(Document):
content = StringField()
author = MapField(field=StringField())
BlogPost.drop_collection()
post = BlogPost(content='Had a good coffee today...',
author={'name': "Ross", "age": "20"}).save()
obj = BlogPost.objects.only('author__name',).get()
self.assertEquals(obj.author['name'], "Ross")
self.assertEquals(obj.author.get("age", None), None)
Try this:
query = BlogPost.objects({your: query})
if name:
query = query.only('author__'+name)
else:
query = query.only('author')
I found my fault! I used only twice.
For example:
BlogPost.objects.only('author').only('author__name')
I spent a whole day finding out what is wrong with Mongoengine.
So my wrong conclusion was:
BlogPost.objects()._collection.find_one(~~ filtering query ~~, {'author.'+ name:1})
But as you know it's a just raw data not a mongoengine query.
After this code, I cannot run any mongoengine methods.
In my case, I should have to query depending on some conditions.
so it will be great that 'only' method overwrites 'only' methods written before.. In my humble opinion.
I hope this feature would be integrated with next version. Right now, I have to code duplicate code:
not this code:
query = BlogPost.objects()
query( query~~).only('author')
if name:
query = query.only('author__'+name)
This code:
query = BlogPost.objects()
query( query~~).only('author')
if name:
query = BlogPost.objects().only('author__'+name)
So I think the second one looks dirtier than first one.
of course, the first code shows you all the data
using only('author') not only('author__name')