How to use '*' in Mongodb? - python

I have 2 variables, sent from ajax, like name and age, their default value is '*'.
when name and age are set, like name = 'a', age = 20, I can query in mongodb(pymongo):
info = list(db.test.find({'name': name, 'age': age}))
But there is time age or name is unset, I have to write:
if age == '*' and name == '*':
info = list(db.test.find())
elif name == '*':
info = list(db.test.find({'age': age}))
elif age == '*':
info = list(db.test.find({'name': name}))
Is there way that I can write a single query? like:
info = list(db.test.find({'name': name, 'age': age}))

One option would be to treat * as {'$exists': True}:
name = {'$exists': True} if name == '*' else name
age = {'$exists': True} if age == '*' else age
info = list(db.test.find({'name': name, 'age': age}))
Or, better solve it on the python side by keeping name and age in the dictionary:
params = {}
if name != '*':
params['name'] = name
if age != '*':
params['age'] = age
info = list(db.test.find(params))

It is unclear what you want the query to return, what the initial values of age and name are. Please create a reproducible example (give an example of some sample docs, the query you are running, what is being returned and what you would like to see returned).
Are you looking for all documents where name (or age) is unset?
db.col.find({'name':'*'})
Are you looking for all documents where name=name or age=age?
db.col.find({ '$or': [{'name': name } , {'age': age} ] })
Please specify what specifically you want the query to return.
I disagree with alecxe however, you shouldn't 'solve on the python side'. You should do more research on how to write queries in MongoDB and let the database cluster do all of the heavy lifting in terms of processing. Just my .02.

Related

Function : Updating & printing a dictionary with the most recent values

I've created a function : A dictionary which takes in city name and country name to which the city belongs to. Currently the code just returns a dictionary after each iteration with city name and country name after taking inputs using the input(). I'm trying to update the code (no success so far) so that a new dictionary is printed after each iteration with updated dictionary which returns an output like
{'city' : 'berlin', 'country' : 'germany',
'city' : 'paris', 'country' : 'france',}
The code is as follows :
def city_country(city_name, country_name):
pair = {'City': city_name, 'Country': country_name}
return pair
active = True
while active:
user_city = input("Enter city name : ")
if user_city == 'q':
break
user_country = input("Enter country name : ")
if user_country == 'q':
break
new_pair = city_country(user_city, user_country)
print(new_pair)
The keys of the dictionary type are unique, this way of writing you can always print only one city and one country, maybe you can try to do it in json format, but it means you have to write another function to deal with the duplication of data (directly add or replace)
def city_country(city_name, country_name):
return {'City': city_name, 'Country': country_name}
data = []
while True:
user_city = input("Enter city name : ")
if user_city == 'q':
break
user_country = input("Enter country name : ")
if user_country == 'q':
break
data.append(city_country(user_city, user_country))
print(data)
city_country_dict = {}
active = True
while active:
user_city = input("Enter city name : ")
if user_city == 'q':
break
user_country = input("Enter country name : ")
if user_country == 'q':
break
new_pair = city_country(user_city, user_country)
#=================================
city_country_dict.update(new_pair)
#=================================
print(city_country_dict)

Way to store user input data to be called later in Python?

New to Python and am working on a task my friend gave me. The objective for this portion is to find user information that was previously added to a dictionary. I am trying to find a way that if the user is searching for a particular user, only that user's info will be returned. So far this is the code I have for this portion of the project:
selection = input('Please select an option 1 - 4:\t')
if selection == '1':
print('Add user - Enter the information for the new user:\t')
first_name = input('First name:\t')
last_name = input('Last name:\t')
hair_color = input('Hair color:\t')
eye_color = input('Eye color:\t')
age = input('Age:\t')
user_info = {}
user_info['First name'] = first_name
user_info['Last name'] = last_name
user_info['Hair color'] = hair_color
user_info['Eye color'] = eye_color
user_info['Age'] = age
Skipping code for sake of space on post
if selection == '3':
print('\nChoose how to look up a user')
print('1 - First name')
print('2 - Last name')
print('3 - Hair color')
print('4 - Eye color')
print('5 - Age')
print('6 - Exit to main menu\n')
search_option = input('Enter option:\t')
if search_option == '1' or search_option == 'First name' or search_option == 'first name':
input('Enter the first name of the user you are looking for:\t')
Any and all help is much appreciated!!
Depending on your project, using a dictionary might be difficult in the future. Let's not go down a dark road. Take a moment and assess the situation.
We know that we want to collect some information from the user, such as:
first name
last name
hair color
...etc
We also want to store the User object to retrieve later based on a particular ID. In your code, you search for other users based on attributes, but what if two or more users share the same attribute, for example, first name?
What your asking for are attributes associated with a particular user. Why not create a class called User?
class User:
def __init__(self, id, first_name, last_name, hair_color):
# You can also check if any attributes are blank and throw an exception.
self._id = id
self._first_name = first_name
self._last_name = last_name
self._hair_color = hair_color
# add more attributes if you want
# a getter to access the self._id property
#property
def id(self):
return self._id
def __str__(self):
return f"ID: {self._id} Name: {self._first_name} {self._last_name}
Hair Color: {self._hair_color}"
In your main function, you can now ask for the user details and store them in a class which you can append to a List.
from User import User
def ask_for_input(question):
answer = input(question)
return answer.strip() # strip any user created white space.
def main():
# Store our users
users = []
# Collect the user info
id = ask_for_input(question = "ID ")
first_name = ask_for_input(question = "First Name ")
last_name = ask_for_input(question = "Last Name ")
hair_color= ask_for_input(question = "Hair Color ")
# Create our user object
user = User(id=id, first_name=first_name, last_name=last_name, hair_color=hair_color)
print(user)
# accessing the id property
print(user.id)
users.append(user)
if __name__ == '__main__':
main()
You may also want to improve on the above class, for example, error checking, and adding type hints to make the code more readable.
If you're just storing the user information, a data class might be more appropriate.
If your looking for a broad suggestion, you could use mongob, it makes a great way to store data to be retrieved later, here is an example i built for another question. The prerequisites is that you'd have to get the mongod server running before you can use the pip install:
Here is an example of how to get it going and how easy it easy to retrieve data like yours
pip3 install pymongo
from pymongo import MongoClient
client = MongoClient()
client = MongoClient('localhost', 27017)
db = client.pymongo_test
posts = db.posts
post_data = {
'title': 'The title of this post',
'content': 'pymongo is awesome',
'author': 'Bill'
}
result = posts.insert_one(post_data)
print('One post: {0}'.format(result.inserted_id))
bills_post = posts.find_one({'author': 'Bill'})
print(bills_post)
#One post: 5dc61c0cc2b75ebc458da31f
#{'_id': ObjectId('5dc61bf76071bde943ca262b'), 'title': 'The title of this post', 'content': 'pymongo is awesome', 'author': 'Bill'}

SQL Alchemy update row based on optional fields from rest api

How to elegantly update the DB rows for selected fields that are optional from rest endpoint using the sqlalchemy.
Assume there is a user moel :
class User(Base):
__tablename__ = 'user'
id = Column(u'id', Integer(), primary_key=True)
name = Column(u'name', String(50),nullable = False)
address = Column(u'adress', String(50))
notes = Column(u'notes', String(50))
Example: I have an API that accepts optional parameters to update user data:
Case 1:
{"id":1,
"name":"nameone",
"address":"one address"
}
Case 2: here address is optional
{ "id":1,
"name":"name-1",
"notes":"test notes"
}
I can update the row using SQLalchemy if the fields are known
For Case 1 :
User.update().where(User.id == id).values(name="nameone",address="one address")
For Case 2 :
User.update().where(User.id == id).values(name="name-1",notes="test notes")
Is there any elegant way to do this instead of writing cases for each case scenario using sqlalchemy ORM?
Use Python to do your logic
data = { "id":1,
"name":"name-1",
"notes":"test notes"
}
user = User.query.filter(User.id == data['id']).first()
for attr, val in data.items():
if not attr == 'id':
setattr(user, attr, val)
Just to be clear, it sound like you are asking how to write only one 'update' statement for different combinations of fields.
An elegant way is to use a dictionary variable in the values parameter.
For example:
if case1:
values_dict = {"id":1, "name":"nameone", "address":"one address"}
else:
values_dict = {"id":1, "name":"name-1", "notes":"test notes"}
User.update().where(User.id == id).values(values_dict)
Depending on how your API returns the data you may or may not need use the case logic in this example.
See also https://docs.sqlalchemy.org/en/14/core/dml.html#sqlalchemy.sql.expression.update
"values – Optional dictionary which specifies the SET conditions of
the UPDATE."

Am I using the best SQLAlchemy query?

I've got an Opportunity table and an Owner table. The Opportunity table has a many to one relationship with Owner. I'm using Python, SQLAlchemy, FLask-Alchemy and SQLite on this project. I need to do an inner join on the Opportunity table against the Owner table and return a single result set (with fields from both tables).
I need to build a dictionary out of the result set so that I can easily generate Json. The following code all works... but... It took a ton of debugging to figure out how to transform the result set returned (which, in this case is a generator object) into a dictionary.
Did I make this harder than I needed to? Is there a better way to do this using SQLAlchemy? (such as using the expression language instead of the ORM)
owner = db.session.query(Owner).filter(Owner.id == owner_id).first()
if owner is None:
return None
result = {'data': []}
report_date = datetime.datetime.strptime('2016-05-01', '%Y-%m-%d')
rows = db.session.query(Opportunity)\
.filter(Opportunity.status == 'Won',
Opportunity.owner == owner,
or_(Opportunity.actual_close_date_iso_year_week == '2016/16',\
Opportunity.actual_close_date_iso_year_week == '2016/17'),
Opportunity.report_date == report_date)\
.order_by(Opportunity.account)\
.join(Owner, Owner.id == Opportunity.owner_id)\
.values(Opportunity.account,
Opportunity.report_date_iso_year_week,
Opportunity.report_date,
Owner.first_name)
for row in rows:
result_row = {}
fields = getattr(row, '_fields')
for field in fields:
result_row[field] = getattr(row, field)
result['data'].append(result_row)
I think that basically this is the way to get what you need, however I would suggest few minor changes:
You don't really need to join, as after .filter(Opportunity.owner==owner) all the rows that you get from the Opportunity table have the same owner_id.
I think it's better to define the list of needed fields once, instead of trying to get it from each row tuple.
So, the code may be like this:
required_fields = ('account','report_date_iso_year_week','report_date')
owner = db.session.query(Owner).filter(Owner.id == owner_id).first()
if owner is None:
return None
result = {'data': []}
report_date = datetime.datetime.strptime('2016-05-01', '%Y-%m-%d')
rows = db.session.query(Opportunity)\
.filter(Opportunity.status == 'Won',
Opportunity.owner == owner,
or_(Opportunity.actual_close_date_iso_year_week == '2016/16',\
Opportunity.actual_close_date_iso_year_week == '2016/17'),
Opportunity.report_date == report_date)\
.order_by(Opportunity.account)\
.values(*[getattr(Opportunity,f) for f in required_fields])
for row in rows:
result_row = {'first_name':owner.first_name}
for field in required_fields:
result_row[field] = getattr(row, field)
result['data'].append(result_row)

How do I look get an associated value in a json variable using python?

How do I look up the 'id' associated with the a person's 'name' when the 2 are in a dictionary?
user = 'PersonA'
id = ? #How do I retrieve the 'id' from the user_stream json variable?
json, stored in a variable named "user_stream"
[
{
'name': 'PersonA',
'id': '135963'
},
{
'name': 'PersonB',
'id': '152265'
},
]
You'll have to decode the JSON structure and loop through all the dictionaries until you find a match:
for person in json.loads(user_stream):
if person['name'] == user:
id = person['id']
break
else:
# The else branch is only ever reached if no match was found
raise ValueError('No such person')
If you need to make multiple lookups, you probably want to transform this structure to a dict to ease lookups:
name_to_id = {p['name']: p['id'] for p in json.loads(user_stream)}
then look up the id directly:
id = name_to_id.get(name) # if name is not found, id will be None
The above example assumes that names are unique, if they are not, use:
from collections import defaultdict
name_to_id = defaultdict(list)
for person in json.loads(user_stream):
name_to_id[person['name']).append(person['id'])
# lookup
ids = name_to_id.get(name, []) # list of ids, defaults to empty
This is as always a trade-off, you trade memory for speed.
Martijn Pieters's solution is correct, but if you intend to make many such look-ups it's better to load the json and iterate over it just once, and not for every look-up.
name_id = {}
for person in json.loads(user_stream):
name = person['name']
id = person['id']
name_id[name] = id
user = 'PersonA'
print name_id[user]
persons = json.loads(...)
results = filter(lambda p:p['name'] == 'avi',persons)
if results:
id = results[0]["id"]
results can be more than 1 of course..

Categories

Resources