How to allocate an ID - google-app-engine

I want to get user ID 5005 for myself. I read that with GAE you can allocate an ID and set it but I don't fully understand the documentation.
The code is
handmade_key = db.Key.from_path('MyModel', 1)
first_batch = db.allocate_ids(handmade_key, 10)
first_range = range(first_batch[0], first_batch[1] + 1)
my_id = first_range.pop(0)
new_key = db.Key.from_path('MyModel', my_id)
new_instance = MyModel(key=new_key)
new_instance.put()
assert new_instance.key().id() == my_id
But where do I put that I want 5005 as the user ID? The model is the user model from webapp2.

You can allocate specific ids range using allocate_id_range, and then you should be able to construct the Key manually using Key.from_path by supplying your own id, and assign it to your new Model instance using the key argument.

Related

append timestamp string for index python

I am using Google App Engine, Standard Environment, NDB Datastore, Python 2.7. There is a limit of 200 indexes per project.
To reduce the number of indexes I am planning to do this:
I have three fields, the report_type, current_center_urlsafe_key and timestamp_entered in a model. I need to find all the entries in the Datastore that has a specific values for current_center_urlsafe_key and report_type. I need to sort these values based on the timestamp_entered (ascending and descending).
This would consume a separate composite-index and I would like to avoid it. To achieve this query, I plan to add a separate entity for every write by combining all three values like this:
center_urlsafe_key_report_type_timestamp = report_type + "***" + current_center_urlsafe_key + str(current_timestamp_ms)
Then I plan to do a query like this:
current_timestamp_ms = int(round(time.time() * 1000))
current_date = date.today()
date_six_months_back = common_increment_dateobj_by_months(self,current_date, -6)
six_month_back_timestamp = (date_six_months_back - date(1970, 1, 1)).total_seconds() * 1000
center_urlsafe_key_report_type_timestamp = report_type_selected + "***" + current_center_urlsafe_key + str(six_month_back_timestamp)
download_reports_forward = download_report_request_model.query(ndb.GenericProperty('center_urlsafe_key_report_type_timestamp') >= center_urlsafe_key_report_type_timestamp).order(ndb.GenericProperty('center_urlsafe_key_report_type_timestamp'))
download_reports_backward = download_report_request_model.query(ndb.GenericProperty('center_urlsafe_key_report_type_timestamp') >= center_urlsafe_key_report_type_timestamp).order(ndb.GenericProperty('-center_urlsafe_key_report_type_timestamp'))
My question is, if I add a timestamp as a string and add a prefix of report_type+"****"+current_center_urlsafe_key, will the NDB Datastore inequality filter provide the desired results?
There is a problem with the strategy. You need to have both ">=" and "<=" filters applied to ensure you are not fetching records from other prefix values. As an example, say your data is as follows
a-123-20191001
a-123-20190901
a-123-20190801
b-123-20191001
b-123-20190901
b-123-20190801
Now, if you do "key >= a-123-20190801", you would get all the data since 2019/08/01 for the prefix "a-123" but you will also end up with all data starting with "b-" since "b-*" >= "a-123-20190801". But if you do "key >= a-123-20190801 and key <= a-123-20191001" then your data will belong only to that prefix.

Google App Engine - query vs. filter clarification

My model:
class User(ndb.Model):
name = ndb.StringProperty()
Is there any difference in terms of efficiency/cost/speed between the following two queries?
u = User.query(User.name==name).get()
u = User.query().filter(User.name==name).get()
Should I use one of them over the other? I assume the 2nd one is worse because it firsts get the entire User class queryset and then applies the filter?
There is no difference in functionality between the two so you can choose whatever you like best. On the google documentation, they show these two examples:
query = Account.query(Account.userid >= 40, Account.userid < 50)
and
query1 = Account.query() # Retrieve all Account entitites
query2 = query1.filter(Account.userid >= 40) # Filter on userid >= 40
query3 = query2.filter(Account.userid < 50) # Filter on userid < 50 too
and state:
query3 is equivalent to the query variable from the previous example.

How do I select column value based on other column

Hi Right now I am using this code in my MVC project :
AppHistory history = new AppHistory();
history = (from AppHistory app in Market.AppHistories
where app.HistoryID == 11
select app).ToList().FirstOrDefault();
Here I am passing value for app.historyID as 11. In my SQL, this is primary key identity specification. I have userID which I am storing for current users.
So what i should do is instead of passing hardocoded value to app.HistoryID, i need to pass userID parameter here and have to select app.HistoryID based on that userID.
How can i do this?
Update :
My table design is shown below. I was not able to upload snap so I am writing my design below:
ColumnName DataType AllowNulls
HistoryID(Primary key) int No
userUID int Yes
HistoryUserActive int yes
HistoryInactiveFrom int yes
HistoryStart datetime yes
HistoryMonthCost int yes
Update 2:
Can i do like this to check column name based on userID :
int userId = userID
history = (from AppHistory app in termsAccepted.AppHistories
where app.UserID == userId
select app).FirstOrDefault();
Now I need to check whether user has historyID, that is hitory.historyID. For this i need to write if condition like :
if (history.historyID exists)
{
//
}
Here what code i should write to check historyID exists or not?

Algorithm to Organize Array

Here's the problem:
I have an array that has information about users. Each entry is separated by a blank field in the array.
For this example let's say the data fields are ID, first, last, phone, and email. However, if a user doesn't have a value for a particular field, then it is omitted entirely.
So here is what the array looks like
users[0] = 45049345
users[1] = Bob
users[2] = Smith
users[3] = 789-456-1230
users[4] = bob#gmail.com
users[5] =
users[6] = 63515987
users[7] = Joe
users[6] = Schmoe
users[8] = joe#gmail.com
users[9] =
I want to loop this array and store the data for each user in a database, however I have no clue how to verify that I am storing the right information in the place because there is no pattern in the array. Since Joe doesn't have a phone number, his email is would be stored as his phone number. This would result in ever subsequent entry to be off by 1 index.
Any ideas on how to go about this?
P.S. I am using node.js
EDIT: here is an example of the actual data
021870143-14
lastName
firstName
U
5/16/1988
11/6/2008
A
11/6/2008 6:0:2
NF
245
MAIN ST.
101
NEW YORK
NY
10002
11/4/2008
34
SD1
MUNC1J-036
MAG1-1
LEG77
SENT34
CONG5
CNTY
34-1
10/27/2008 19:59:53
NF
Here's pseudo code because I don't know javascript. I'm basing this off the fact that I think Javascript has dictionaries/associative arrays/hash tables etc.
new_user = {}
for i from 0 to arr.length
if arr[i] = null/Omitted field/nil/None/whatever
database.add_entry(new_user) // Add finished user table to database
new_user = {} // Start new dictionary
else
field = arr[i]
if field contains '#'
new_user.email = field
else if field contains '-'
new_user.phone_number = field
else if is_a_number(field)
new_user.id = field
else if new_user has first_name
new_user.last_name = field
else
new_user.first_name = field
Use tokenization ..
example:
users[0] = "45049345,Bob,Smith,789-456-1230,bob#gmail.com";
users[1] = "63515987,Joe,Schmoe,-,joe#gmail.com";
Process:
for (user in users)
{
for (detail in user)
{
document.write (detail + "<br>");
}
}

Between query equivalent on App Engine datastore?

I have a model containing ranges of IP addresses, similar to this:
class Country(db.Model):
begin_ipnum = db.IntegerProperty()
end_ipnum = db.IntegerProperty()
On a SQL database, I would be able to find rows which contained an IP in a certain range like this:
SELECT * FROM Country WHERE ipnum BETWEEN begin_ipnum AND end_ipnum
or this:
SELECT * FROM Country WHERE begin_ipnum < ipnum AND end_ipnum > ipnum
Sadly, GQL only allows inequality filters on one property, and doesn't support the BETWEEN syntax. How can I work around this and construct a query equivalent to these on App Engine?
Also, can a ListProperty be 'live' or does it have to be computed when the record is created?
question updated with a first stab at a solution:
So based on David's answer below and articles such as these:
http://appengine-cookbook.appspot.com/recipe/custom-model-properties-are-cute/
I'm trying to add a custom field to my model like so:
class IpRangeProperty(db.Property):
def __init__(self, begin=None, end=None, **kwargs):
if not isinstance(begin, db.IntegerProperty) or not isinstance(end, db.IntegerProperty):
raise TypeError('Begin and End must be Integers.')
self.begin = begin
self.end = end
super(IpRangeProperty, self).__init__(self.begin, self.end, **kwargs)
def get_value_for_datastore(self, model_instance):
begin = self.begin.get_value_for_datastore(model_instance)
end = self.end.get_value_for_datastore(model_instance)
if begin is not None and end is not None:
return range(begin, end)
class Country(db.Model):
begin_ipnum = db.IntegerProperty()
end_ipnum = db.IntegerProperty()
ip_range = IpRangeProperty(begin=begin_ipnum, end=end_ipnum)
The thinking is that after i add the custom property i can just import my dataset as is and then run queries on based on the ListProperty like so:
q = Country.gql('WHERE ip_range = :1', my_num_ipaddress)
When i try to insert new Country objects this fails though, complaning about not being able to create the name:
...
File "/Applications/GoogleAppEngineLauncher.app/Contents/Resources/GoogleAppEngine-default.bundle/Contents/Resources/google_appengine/google/appengine/ext/db/__init__.py", line 619, in _attr_name
return '_' + self.name
TypeError: cannot concatenate 'str' and 'IntegerProperty' objects
I tried defining an attr_name method for the new property or just setting self.name but that does not seem to help. Hopelessly stuck or heading in the right direction?
Short answer: Between queries aren't really supported at the moment. However, if you know a priori that your range is going to be relatively small, then you can fake it: just store a list on the entity with every number in the range. Then you can use a simple equality filter to get entities whose ranges contain a particular value. Obviously this won't work if your range is large. But here's how it would work:
class M(db.Model):
r = db.ListProperty(int)
# create an instance of M which has a range from `begin` to `end` (inclusive)
M(r=range(begin, end+1)).put()
# query to find instances of M which contain a value `v`
q = M.gql('WHERE r = :1', v)
The better solution (eventually - for now the following only works on the development server due to a bug (see issue 798). In theory, you can work around the limitations you mentioned and perform a range query by taking advantage of how db.ListProperty is queried. The idea is to store both the start and end of your range in a list (in your case, integers representing IP addresses). Then to get entities whose ranges contain some value v (i.e., between the two values in your list), you simply perform a query with two inequality filters on the list - one to ensure that v is at least as big as the smallest element in the list, and one to ensure that v is at least as small as the biggest element in the list.
Here's a simple example of how to implement this technique:
class M(db.Model):
r = db.ListProperty(int)
# create an instance of M which has a rnage from `begin` to `end` (inclusive)
M(r=[begin, end]).put()
# query to find instances of M which contain a value `v`
q = M.gql('WHERE r >= :1 AND r <= :1', v)
My solution doesn't follow the pattern you have requested, but I think it would work well on app engine. I'm using a list of strings of CIDR ranges to define the IP blocks instead of specific begin and end numbers.
from google.appengine.ext import db
class Country(db.Model):
subnets = db.StringListProperty()
country_code = db.StringProperty()
c = Country()
c.subnets = ['1.2.3.0/24', '1.2.0.0/16', '1.3.4.0/24']
c.country_code = 'US'
c.put()
c = Country()
c.subnets = ['2.2.3.0/24', '2.2.0.0/16', '2.3.4.0/24']
c.country_code = 'CA'
c.put()
# Search for 1.2.4.5 starting with most specific block and then expanding until found
result = Country.all().filter('subnets =', '1.2.4.5/32').fetch(1)
result = Country.all().filter('subnets =', '1.2.4.4/31').fetch(1)
result = Country.all().filter('subnets =', '1.2.4.4/30').fetch(1)
result = Country.all().filter('subnets =', '1.2.4.0/29').fetch(1)
# ... repeat until found
# optimize by starting with the largest routing prefix actually found in your data (probably not 32)

Resources