1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 r"""fieldmappings.py: Mappings from field names to term prefixes, etc.
19
20 """
21 __docformat__ = "restructuredtext en"
22
23 import cPickle as _cPickle
24
26 """Mappings from field names to term prefixes, slot values, etc.
27
28 The following mappings are maintained:
29
30 - a mapping from field name to the string prefix to insert at the start of
31 terms.
32 - a mapping from field name to the slot numbers to store the field contents
33 in.
34
35 """
36 __slots__ = '_prefixes', '_prefixcount', '_slots', '_slotcount',
37
39 """Create a new field mapping object, or unserialise a saved one.
40
41 """
42 if serialised is not None:
43 (self._prefixes, self._prefixcount,
44 self._slots, self._slotcount) = _cPickle.loads(serialised)
45 else:
46 self._prefixes = {}
47 self._prefixcount = 0
48 self._slots = {}
49 self._slotcount = 0
50
52 """Generate a previously unused prefix.
53
54 Prefixes are uppercase letters, and start with 'X' (this is a Xapian
55 convention, for compatibility with other Xapian tools: other starting
56 letters are reserved for special meanings):
57
58 >>> maps = FieldMappings()
59 >>> maps._genPrefix()
60 'XA'
61 >>> maps._genPrefix()
62 'XB'
63 >>> [maps._genPrefix() for i in xrange(60)]
64 ['XC', 'XD', 'XE', 'XF', 'XG', 'XH', 'XI', 'XJ', 'XK', 'XL', 'XM', 'XN', 'XO', 'XP', 'XQ', 'XR', 'XS', 'XT', 'XU', 'XV', 'XW', 'XX', 'XY', 'XZ', 'XAA', 'XBA', 'XCA', 'XDA', 'XEA', 'XFA', 'XGA', 'XHA', 'XIA', 'XJA', 'XKA', 'XLA', 'XMA', 'XNA', 'XOA', 'XPA', 'XQA', 'XRA', 'XSA', 'XTA', 'XUA', 'XVA', 'XWA', 'XXA', 'XYA', 'XZA', 'XAB', 'XBB', 'XCB', 'XDB', 'XEB', 'XFB', 'XGB', 'XHB', 'XIB', 'XJB']
65 >>> maps = FieldMappings()
66 >>> [maps._genPrefix() for i in xrange(27*26 + 5)][-10:]
67 ['XVZ', 'XWZ', 'XXZ', 'XYZ', 'XZZ', 'XAAA', 'XBAA', 'XCAA', 'XDAA', 'XEAA']
68 """
69 res = []
70 self._prefixcount += 1
71 num = self._prefixcount
72 while num != 0:
73 ch = (num - 1) % 26
74 res.append(chr(ch + ord('A')))
75 num -= ch
76 num = num // 26
77 return 'X' + ''.join(res)
78
80 """Get a fieldname from a prefix.
81
82 If the prefix is not found, return None.
83
84 """
85 for key, val in self._prefixes.iteritems():
86 if val == prefix:
87 return key
88 return None
89
91 """Get the prefix used for a given field name.
92
93 """
94 return self._prefixes[fieldname]
95
97 """Get the slot number used for a given field name and purpose.
98
99 """
100 return self._slots[(fieldname, purpose)]
101
103 """Allocate a prefix for the given field.
104
105 If a prefix is already allocated for this field, this has no effect.
106
107 """
108 if fieldname in self._prefixes:
109 return
110 self._prefixes[fieldname] = self._genPrefix()
111
112 - def add_slot(self, fieldname, purpose, slotnum=None):
113 """Allocate a slot number for the given field and purpose.
114
115 If a slot number is already allocated for this field and purpose, this
116 has no effect.
117
118 Returns the slot number allocated for the field and purpose (whether
119 newly allocated, or previously allocated).
120
121 If `slotnum` is supplied, the number contained in it is used to
122 allocate the new slot, instead of allocating a new number. No checks
123 will be made to ensure that the slot number doesn't collide with
124 existing (or later allocated) numbers: the main purpose of this
125 parameter is to share allocations - ie, to collide deliberately.
126
127 """
128 try:
129 return self._slots[(fieldname, purpose)]
130 except KeyError:
131 pass
132
133 if slotnum is None:
134 self._slots[(fieldname, purpose)] = self._slotcount
135 self._slotcount += 1
136 return self._slotcount - 1
137 else:
138 self._slots[(fieldname, purpose)] = slotnum
139 return slotnum
140
142 """Serialise the field mappings to a string.
143
144 This can be unserialised by passing the result of this method to the
145 constructor of a new FieldMappings object.
146
147 """
148 return _cPickle.dumps((self._prefixes,
149 self._prefixcount,
150 self._slots,
151 self._slotcount,
152 ), 2)
153