1
2
3
4
5
6
7
8
9
10
11
12
13 from types import ListType, TupleType
14 from BTrees.IIBTree import IISet
15 import logging
16 LOG = logging.getLogger('ZenUtils.MultiPathIndex')
17
18 from Globals import DTMLFile
19
20 from ExtendedPathIndex import ExtendedPathIndex
21 from Products.PluginIndexes.common import safe_callable
22 from BTrees.OOBTree import OOSet
23
25 if not seq:
26 return False
27 if not isinstance(seq, (TupleType, ListType)):
28 return False
29 for item in seq:
30 if not isinstance(item, (TupleType, ListType)):
31 return False
32 return True
33
35 if isinstance(seq, (TupleType, ListType)):
36 return map(_recursivePathSplit, seq)
37 if '/' in seq:
38 return seq.split('/')
39 else:
40 return seq
41
43 """
44 A path index that is capable of indexing multiple paths per object.
45 """
46 meta_type = "MultiPathIndex"
47
48 - def search(self, path, default_level=0, depth=-1, navtree=0,
49 navtree_start=0):
58
60 """Extended path index uses a series of set "buckets" to store which items belong at
61 which point on the search path, this can sometimes cause issues if two points on the path
62 have the same name and thus belong to the same bucket
63 For instance:
64 /zport/dmd/Devices/Server/Windows/WMI/devices/test-winxp-1.zenoss.loc/os/software/VMware Tools
65 has another path at
66 /zport/dmd/Manfacturers/VMware/...
67
68 and our search returns a false positive because we search for
69 /zport/dmd/Devices/VMware
70
71 because we have the id for that component returned at each point along the path.
72
73 So this function iterates through all the return paths and makes sure the path that we are searching
74 for is in the path that is returned
75 """
76
77 if not isinstance(searchPath, basestring) or not searchPath.startswith('/'):
78 return pathset
79 rids = IISet()
80 if not pathset:
81 return
82 for rid in pathset:
83 paths = self._unindex[rid]
84 for path in paths:
85 if path.startswith(searchPath):
86 rids.insert(rid)
87 break
88
89 return rids
90
92 """ return names of indexed attributes """
93 return (self.id, )
94
96 """ hook for (Z)Catalog """
97
98 f = getattr(obj, self.id, None)
99 if f is not None:
100 if safe_callable(f):
101 try:
102 paths = f()
103 except AttributeError:
104 return 0
105 else:
106 paths = f
107 else:
108 try:
109 paths = obj.getPhysicalPath()
110 except AttributeError:
111 return 0
112
113 if not paths: return 0
114 paths = _recursivePathSplit(paths)
115 if not _isSequenceOfSequences(paths):
116 paths = [paths]
117
118 if docid in self._unindex:
119 unin = self._unindex[docid]
120
121 if isinstance(unin, set):
122 unin = self._unindex[docid] = OOSet(unin)
123 for oldpath in list(unin):
124 if list(oldpath.split('/')) not in paths:
125 self.unindex_paths(docid, (oldpath,))
126 else:
127 self._unindex[docid] = OOSet()
128 self._length.change(1)
129
130 self.index_paths(docid, paths)
131
132 return 1
133
134
136 for path in paths:
137 if isinstance(path, (list, tuple)):
138 path = '/'+ '/'.join(path[1:])
139 comps = filter(None, path.split('/'))
140 parent_path = '/' + '/'.join(comps[:-1])
141
142 for i in range(len(comps)):
143 self.insertEntry(comps[i], docid, i)
144
145
146 self.insertEntry(None, docid, len(comps)-1, parent_path, path)
147
148 self._unindex.setdefault(docid, OOSet()).insert(path)
149
150
152
153 if not self._unindex.has_key(docid):
154 return
155
156 def unindex(comp, level, docid=docid, parent_path=None,
157 object_path=None):
158 try:
159 self._index[comp][level].remove(docid)
160
161 if not self._index[comp][level]:
162 del self._index[comp][level]
163
164 if not self._index[comp]:
165 del self._index[comp]
166
167 if parent_path is not None:
168 self._index_parents[parent_path].remove(docid)
169 if not self._index_parents[parent_path]:
170 del self._index_parents[parent_path]
171 if object_path is not None:
172 del self._index_items[object_path]
173 except KeyError:
174
175 pass
176
177 old = set(self._unindex.get(docid, ()))
178 mkstr = lambda path:'/'.join(path) if isinstance(path, tuple) else path
179 paths = map(mkstr, paths)
180 toremove = set(paths) & old
181 tokeep = old - toremove
182 for path in toremove:
183 if not path.startswith('/'):
184 path = '/'+path
185 comps = path.split('/')
186 parent_path = '/'.join(comps[:-1])
187
188 for level in range(len(comps[1:])):
189 comp = comps[level+1]
190 unindex(comp, level, docid, parent_path, path)
191
192 level = len(comps[1:])
193 comp = None
194 unindex(comp, level-1, parent_path=parent_path, object_path=path)
195
196 self._unindex[docid].remove(path)
197
198 if tokeep:
199 self.index_paths(docid, tokeep)
200 else:
201
202 self._length.change(-1)
203 del self._unindex[docid]
204
205
207 """ hook for (Z)Catalog """
208 if not self._unindex.has_key(docid):
209 return
210 self.unindex_paths(docid, self._unindex[docid])
211
212 manage = manage_main = DTMLFile('dtml/manageMultiPathIndex', globals())
213 manage_main._setName('manage_main')
214
215
216 manage_addMultiPathIndexForm = DTMLFile('dtml/addMultiPathIndex', globals())
217
220 """
221 Add a MultiPathIndex.
222 """
223 return self.manage_addIndex(id, 'MultiPathIndex', extra=None,
224 REQUEST=REQUEST, RESPONSE=RESPONSE,
225 URL1=URL3)
226