btbytes (owner)

Revisions

gist: 115659 Download_button fork
public
Public Clone URL: git://gist.github.com/115659.git
Embed All Files: show embed
pytyrant_search.py #
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
'''
Author: mjmalone
https://twitter.com/mjmalone/status/1874036416
'''
 
import struct
import pytyrant
 
 
class QueryException(Exception): pass
 
 
def validate_str(arg):
    return isinstance(arg, basestring)
 
def validate_str_list(lst):
    if not hasattr(lst, '__iter__'):
        return False
    for item in lst:
        if not validate_str(item):
            return False
    return True
 
def validate_btwn_list(lst):
    return validate_str_list and len(lst) == 2
 
 
class Query(object):
 
    # Enumeration of query conditions
    OPERATIONS = (
        ('streq', '0', validate_str), # string is equal to
        ('strinc', '1', validate_str), # string is included in
        ('strbw', '2', validate_str), # string begins with
        ('strew', '3', validate_str), # string ends with
        ('strand', '4', validate_str_list), # string includes all tokens in
        ('stror', '5', validate_str_list), # string includes at least one token in
        ('stroreq', '6', validate_str_list), # string is equal to at least one token in
        ('strrx', '7', validate_str), # string matches regular expression of
        ('numeq', '8', validate_str), # number is equal to
        ('numgt', '9', validate_str), # number is greater than
        ('numge', '10', validate_str), # number is greater than or equal to
        ('numlt', '11', validate_str), # number is less than
        ('numle', '12', validate_str), # number is less than or equal to
        ('numbt', '13', validate_btwn_list), # number is between two tokens of
        ('numoreq', '14', validate_str_list), # number is equal to at least one token in
    )
 
    def __init__(self):
        self.args = []
 
    def get_packed_args(self):
        return [struct.pack('>BBIII', pytyrant.MAGIC, pytyrant.C.misc, 6, 0, len(self.args)), "search"] + self.args
 
    def pack_condition(self, field, op, value):
        if not isinstance(value, basestring) and hasattr(value, '__iter__'):
            # Value is a list of strings. Make it a single comma separated string.
            value = ','.join(value)
        condition = '\x00'.join(["addcond", field, op, value])
        return '%s%s' % (struct.pack('>I', len(condition)), condition)
 
    def get_operator_info(self, op_name):
        for name, code, validator in Query.OPERATIONS:
            if op_name == name:
                return code, validator
        raise QueryException("Unknown query condition: '%s'" % operation)
 
    def add_condition(self, **kwargs):
        for key, value in kwargs.iteritems():
            if '__' not in key:
                raise QueryException("Condition arguments should be of the form `field__operation`")
            field, operation = key.split('__')
 
            op_code, validator = self.get_operator_info(operation)
            if not validator(value):
                raise QueryException("Invalid value for query type `%s`: `%s`", (operator, value))
            self.args.append(self.pack_condition(field, op_code, value))
        return self
 
    def setlimit(self, limit, offset):
        condition = '\x00'.join(('setlimit', str(limit), str(offset)))
        self.args.append('%s%s' % (struct.pack('>I', len(condition)), condition))
        return self
 
 
class Tyrant(pytyrant.Tyrant):
    def search(self, query):
        pytyrant.socksend(self.sock, query.get_packed_args())
        pytyrant.socksuccess(self.sock)
        return [pytyrant.sockstr(self.sock) for i in xrange(pytyrant.socklen(self.sock))]
 
 
if __name__ == '__main__':
    t = Tyrant.open('127.0.0.1', 1979)
 
    # Find places that have names equal to California or Maryland
    q = Query().add_condition(name__stroreq = ["California", "Maryland"])
    r = t.search(q)
    for id in r:
        print t.get(id)
    print len(r)
 
    # Find the first 10 places with a parent WOEID between 2500066 and 2500080
    q = Query().add_condition(parent_woeid__numbt = ["2500066", "2500080"]).setlimit(10,0)
    r = t.search(q)
    for id in r:
        print t.get(id)
    print len(r)