Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Z
Zope
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
Kirill Smelkov
Zope
Commits
b3048e12
Commit
b3048e12
authored
Jun 06, 2002
by
Tres Seaver
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
- Land new pluggable index types, specialized for date values and
date ranges, from branch.
parent
ab261465
Changes
13
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
1099 additions
and
2 deletions
+1099
-2
lib/python/Products/PluginIndexes/DateIndex/DateIndex.py
lib/python/Products/PluginIndexes/DateIndex/DateIndex.py
+193
-0
lib/python/Products/PluginIndexes/DateIndex/README.txt
lib/python/Products/PluginIndexes/DateIndex/README.txt
+22
-0
lib/python/Products/PluginIndexes/DateIndex/__init__.py
lib/python/Products/PluginIndexes/DateIndex/__init__.py
+1
-0
lib/python/Products/PluginIndexes/DateIndex/dtml/addDateIndex.dtml
...n/Products/PluginIndexes/DateIndex/dtml/addDateIndex.dtml
+47
-0
lib/python/Products/PluginIndexes/DateIndex/dtml/manageDateIndex.dtml
...roducts/PluginIndexes/DateIndex/dtml/manageDateIndex.dtml
+10
-0
lib/python/Products/PluginIndexes/DateIndex/tests/test_DateIndex.py
.../Products/PluginIndexes/DateIndex/tests/test_DateIndex.py
+135
-0
lib/python/Products/PluginIndexes/DateRangeIndex/DateRangeIndex.py
...n/Products/PluginIndexes/DateRangeIndex/DateRangeIndex.py
+413
-0
lib/python/Products/PluginIndexes/DateRangeIndex/README.txt
lib/python/Products/PluginIndexes/DateRangeIndex/README.txt
+41
-0
lib/python/Products/PluginIndexes/DateRangeIndex/__init__.py
lib/python/Products/PluginIndexes/DateRangeIndex/__init__.py
+1
-0
lib/python/Products/PluginIndexes/DateRangeIndex/dtml/addDateRangeIndex.dtml
.../PluginIndexes/DateRangeIndex/dtml/addDateRangeIndex.dtml
+59
-0
lib/python/Products/PluginIndexes/DateRangeIndex/dtml/manageDateRangeIndex.dtml
...uginIndexes/DateRangeIndex/dtml/manageDateRangeIndex.dtml
+42
-0
lib/python/Products/PluginIndexes/DateRangeIndex/tests/test_DateRangeIndex.py
...PluginIndexes/DateRangeIndex/tests/test_DateRangeIndex.py
+124
-0
lib/python/Products/PluginIndexes/__init__.py
lib/python/Products/PluginIndexes/__init__.py
+11
-2
No files found.
lib/python/Products/PluginIndexes/DateIndex/DateIndex.py
0 → 100644
View file @
b3048e12
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from
DateTime.DateTime
import
DateTime
from
Products.PluginIndexes
import
PluggableIndex
from
Products.PluginIndexes.common.UnIndex
import
UnIndex
from
Products.PluginIndexes.common.util
import
parseIndexRequest
from
types
import
StringType
,
FloatType
,
IntType
from
Globals
import
DTMLFile
from
BTrees.IOBTree
import
IOBTree
from
BTrees.OIBTree
import
OIBTree
from
BTrees.IIBTree
import
IISet
,
union
,
intersection
_marker
=
[]
class
DateIndex
(
UnIndex
):
""" Index for Dates """
__implements__
=
(
PluggableIndex
.
PluggableIndexInterface
,)
meta_type
=
'DateIndex'
query_options
=
[
'query'
,
'range'
]
manage
=
manage_main
=
DTMLFile
(
'dtml/manageDateIndex'
,
globals
()
)
manage_main
.
_setName
(
'manage_main'
)
manage_options
=
(
{
'label'
:
'Settings'
,
'action'
:
'manage_main'
},
)
def
clear
(
self
):
""" Complete reset """
self
.
_index
=
IOBTree
()
self
.
_unindex
=
OIBTree
()
def
index_object
(
self
,
documentId
,
obj
,
threshold
=
None
):
"""index an object, normalizing the indexed value to an integer
o Normalized value has granularity of one minute.
o Objects which have 'None' as indexed value are *omitted*,
by design.
"""
returnStatus
=
0
try
:
date_attr
=
getattr
(
obj
,
self
.
id
)
if
callable
(
date_attr
):
date_attr
=
date_attr
()
ConvertedDate
=
self
.
_convert
(
value
=
date_attr
,
default
=
_marker
)
except
AttributeError
:
ConvertedDate
=
_marker
oldConvertedDate
=
self
.
_unindex
.
get
(
documentId
,
_marker
)
if
ConvertedDate
!=
oldConvertedDate
:
if
oldConvertedDate
is
not
_marker
:
self
.
removeForwardIndexEntry
(
oldConvertedDate
,
documentId
)
if
ConvertedDate
is
not
_marker
:
self
.
insertForwardIndexEntry
(
ConvertedDate
,
documentId
)
self
.
_unindex
[
documentId
]
=
ConvertedDate
returnStatus
=
1
return
returnStatus
def
_apply_index
(
self
,
request
,
cid
=
''
,
type
=
type
,
None
=
None
):
"""Apply the index to query parameters given in the argument
Normalize the 'query' arguments into integer values at minute
precision before querying.
"""
record
=
parseIndexRequest
(
request
,
self
.
id
,
self
.
query_options
)
if
record
.
keys
==
None
:
return
None
keys
=
map
(
self
.
_convert
,
record
.
keys
)
index
=
self
.
_index
r
=
None
opr
=
None
#experimental code for specifing the operator
operator
=
record
.
get
(
'operator'
,
self
.
useOperator
)
if
not
operator
in
self
.
operators
:
raise
RuntimeError
,
"operator not valid: %s"
%
operator
# depending on the operator we use intersection or union
if
operator
==
"or"
:
set_func
=
union
else
:
set_func
=
intersection
# range parameter
range_arg
=
record
.
get
(
'range'
,
None
)
if
range_arg
:
opr
=
"range"
opr_args
=
[]
if
range_arg
.
find
(
"min"
)
>
-
1
:
opr_args
.
append
(
"min"
)
if
range_arg
.
find
(
"max"
)
>
-
1
:
opr_args
.
append
(
"max"
)
if
record
.
get
(
'usage'
,
None
):
# see if any usage params are sent to field
opr
=
record
.
usage
.
lower
().
split
(
':'
)
opr
,
opr_args
=
opr
[
0
],
opr
[
1
:]
if
opr
==
"range"
:
# range search
if
'min'
in
opr_args
:
lo
=
min
(
keys
)
else
:
lo
=
None
if
'max'
in
opr_args
:
hi
=
max
(
keys
)
else
:
hi
=
None
if
hi
:
setlist
=
index
.
items
(
lo
,
hi
)
else
:
setlist
=
index
.
items
(
lo
)
# XXX: Use multiunion!
for
k
,
set
in
setlist
:
if
type
(
set
)
is
IntType
:
set
=
IISet
((
set
,))
r
=
set_func
(
r
,
set
)
else
:
# not a range search
for
key
in
keys
:
set
=
index
.
get
(
key
,
None
)
if
set
is
not
None
:
if
type
(
set
)
is
IntType
:
set
=
IISet
((
set
,))
r
=
set_func
(
r
,
set
)
if
type
(
r
)
is
IntType
:
r
=
IISet
((
r
,))
if
r
is
None
:
return
IISet
(),
(
self
.
id
,)
else
:
return
r
,
(
self
.
id
,)
def
_convert
(
self
,
value
,
default
=
None
):
"""Convert Date/Time value to our internal representation"""
if
isinstance
(
value
,
DateTime
):
t_tup
=
value
.
parts
()
elif
type
(
value
)
is
FloatType
:
t_tup
=
time
.
gmtime
(
value
)
elif
type
(
value
)
is
StringType
:
t_obj
=
DateTime
(
value
)
t_tup
=
t_obj
.
parts
()
else
:
return
default
yr
=
t_tup
[
0
]
mo
=
t_tup
[
1
]
dy
=
t_tup
[
2
]
hr
=
t_tup
[
3
]
mn
=
t_tup
[
4
]
t_val
=
(
(
(
(
yr
*
12
+
mo
)
*
31
+
dy
)
*
24
+
hr
)
*
60
+
mn
)
return
t_val
manage_addDateIndexForm
=
DTMLFile
(
'dtml/addDateIndex'
,
globals
()
)
def
manage_addDateIndex
(
self
,
id
,
REQUEST
=
None
,
RESPONSE
=
None
,
URL3
=
None
):
"""Add a Date index"""
return
self
.
manage_addIndex
(
id
,
'DateIndex'
,
extra
=
None
,
\
REQUEST
=
REQUEST
,
RESPONSE
=
RESPONSE
,
URL1
=
URL3
)
lib/python/Products/PluginIndexes/DateIndex/README.txt
0 → 100644
View file @
b3048e12
DateIndex README
Overview
Normal FieldIndexes *can* be used to index values which are DateTime
instances, but they are hideously expensive:
o DateTime instances are *huge*, both in RAM and on disk.
o DateTime instances maintain an absurd amount of precision, far
beyond any reasonable search criteria for "normal" cases.
DateIndex is a pluggable index which addresses these two issues
as follows:
o It normalizes the indexed value to an integer representation
with a granularity of one minute.
o It normalizes the 'query' value into the same form.
o Objects which return 'None' for the index query are omitted from
the index.
lib/python/Products/PluginIndexes/DateIndex/__init__.py
0 → 100644
View file @
b3048e12
# Empty on purpose
lib/python/Products/PluginIndexes/DateIndex/dtml/addDateIndex.dtml
0 → 100644
View file @
b3048e12
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add DateIndex',
)">
<p class="form-help">
A <em>DateIndex</em> indexes DateTime attributes.
</p>
<form action="manage_addDateIndex" method="post" enctype="multipart/form-data">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-optional">
Type
</div>
</td>
<td align="left" valign="top">
DateIndex
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value=" Add " />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
lib/python/Products/PluginIndexes/DateIndex/dtml/manageDateIndex.dtml
0 → 100644
View file @
b3048e12
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<p class="form-help">
Nothing to manage at this time.
</p>
<dtml-var manage_page_footer>
lib/python/Products/PluginIndexes/DateIndex/tests/test_DateIndex.py
0 → 100644
View file @
b3048e12
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import
Zope
import
unittest
from
DateTime
import
DateTime
from
Products.PluginIndexes.DateIndex.DateIndex
import
DateIndex
class
Dummy
:
def
__init__
(
self
,
name
,
date
):
self
.
_name
=
name
self
.
_date
=
date
def
name
(
self
):
return
self
.
_name
def
date
(
self
):
return
self
.
_date
def
__str__
(
self
):
return
"<Dummy %s, date %s>"
%
(
self
.
_name
,
str
(
self
.
_date
))
class
DI_Tests
(
unittest
.
TestCase
):
def
setUp
(
self
):
self
.
_values
=
(
(
0
,
Dummy
(
'a'
,
None
)),
(
1
,
Dummy
(
'b'
,
DateTime
(
0
))),
(
2
,
Dummy
(
'c'
,
DateTime
(
'2002-05-08 15:16:17'
))),
(
3
,
Dummy
(
'd'
,
DateTime
(
'2032-05-08 15:16:17'
))),
(
4
,
Dummy
(
'e'
,
DateTime
(
'2062-05-08 15:16:17'
))),
(
5
,
Dummy
(
'e'
,
DateTime
(
'2062-05-08 15:16:17'
)))
)
self
.
_index
=
DateIndex
(
'date'
)
self
.
_noop_req
=
{
'bar'
:
123
}
self
.
_request
=
{
'date'
:
DateTime
(
0
)}
self
.
_min_req
=
{
'date'
:
DateTime
(
'2032-05-08 15:16:17'
),
'date_usage'
:
'range:min'
}
self
.
_max_req
=
{
'date'
:
DateTime
(
'2032-05-08 15:16:17'
),
'date_usage'
:
'range:max'
}
self
.
_range_req
=
{
'date'
:
(
DateTime
(
'2002-05-08 15:16:17'
),
DateTime
(
'2062-05-08 15:16:17'
)),
'date_usage'
:
'range:min:max'
}
self
.
_zero_req
=
{
'date'
:
0
}
self
.
_none_req
=
{
'date'
:
None
}
def
_populateIndex
(
self
):
for
k
,
v
in
self
.
_values
:
self
.
_index
.
index_object
(
k
,
v
)
def
_checkApply
(
self
,
req
,
expectedValues
):
result
,
used
=
self
.
_index
.
_apply_index
(
req
)
if
hasattr
(
result
,
'keys'
):
result
=
result
.
keys
()
self
.
failUnlessEqual
(
used
,
(
'date'
,))
self
.
failUnlessEqual
(
len
(
result
),
len
(
expectedValues
),
'%s | %s'
%
(
map
(
None
,
result
),
expectedValues
))
for
k
,
v
in
expectedValues
:
self
.
failUnless
(
k
in
result
)
def
_convert
(
self
,
date
):
yr
,
mo
,
dy
,
hr
,
mn
=
date
.
parts
()[:
5
]
return
(((
yr
*
12
+
mo
)
*
31
+
dy
)
*
24
+
hr
)
*
60
+
mn
def
test_empty
(
self
):
empty
=
self
.
_index
self
.
failUnlessEqual
(
len
(
empty
),
0
)
self
.
failUnlessEqual
(
len
(
empty
.
referencedObjects
()),
0
)
self
.
failUnless
(
empty
.
getEntryForObject
(
1234
)
is
None
)
marker
=
[]
self
.
failUnless
(
empty
.
getEntryForObject
(
1234
,
marker
)
is
marker
)
empty
.
unindex_object
(
1234
)
# shouldn't throw
self
.
failUnless
(
empty
.
hasUniqueValuesFor
(
'date'
))
self
.
failIf
(
empty
.
hasUniqueValuesFor
(
'foo'
))
self
.
failUnlessEqual
(
len
(
empty
.
uniqueValues
(
'date'
)),
0
)
self
.
failUnless
(
empty
.
_apply_index
({
'zed'
:
12345
})
is
None
)
self
.
_checkApply
(
self
.
_request
,
[])
self
.
_checkApply
(
self
.
_min_req
,
[])
self
.
_checkApply
(
self
.
_max_req
,
[])
self
.
_checkApply
(
self
.
_range_req
,
[])
def
test_retrieval
(
self
):
self
.
_populateIndex
()
values
=
self
.
_values
index
=
self
.
_index
self
.
failUnlessEqual
(
len
(
index
),
len
(
values
)
-
2
)
# One dupe, one empty
self
.
failUnlessEqual
(
len
(
index
.
referencedObjects
()),
len
(
values
)
-
1
)
# One empty
self
.
failUnless
(
index
.
getEntryForObject
(
1234
)
is
None
)
marker
=
[]
self
.
failUnless
(
index
.
getEntryForObject
(
1234
,
marker
)
is
marker
)
index
.
unindex_object
(
1234
)
# shouldn't throw
for
k
,
v
in
values
:
if
v
.
date
():
self
.
failUnlessEqual
(
self
.
_index
.
getEntryForObject
(
k
),
self
.
_convert
(
v
.
date
()))
self
.
failUnlessEqual
(
len
(
index
.
uniqueValues
(
'date'
)),
len
(
values
)
-
2
)
self
.
failUnless
(
index
.
_apply_index
(
self
.
_noop_req
)
is
None
)
self
.
_checkApply
(
self
.
_request
,
values
[
1
:
2
])
self
.
_checkApply
(
self
.
_min_req
,
values
[
3
:])
self
.
_checkApply
(
self
.
_max_req
,
values
[
1
:
4
])
self
.
_checkApply
(
self
.
_range_req
,
values
[
2
:]
)
def
test_suite
():
suite
=
unittest
.
TestSuite
()
suite
.
addTest
(
unittest
.
makeSuite
(
DI_Tests
)
)
return
suite
def
run
():
unittest
.
TextTestRunner
().
run
(
test_suite
())
if
__name__
==
'__main__'
:
run
()
lib/python/Products/PluginIndexes/DateRangeIndex/DateRangeIndex.py
0 → 100644
View file @
b3048e12
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
from
Products.PluginIndexes
import
PluggableIndex
from
Products.PluginIndexes.common.UnIndex
import
UnIndex
from
Products.PluginIndexes.common.util
import
parseIndexRequest
from
OFS.SimpleItem
import
SimpleItem
from
BTrees.IOBTree
import
IOBTree
from
BTrees.IIBTree
import
IISet
,
IITreeSet
,
union
,
intersection
,
multiunion
from
Globals
import
package_home
,
DTMLFile
,
InitializeClass
from
AccessControl
import
ClassSecurityInfo
from
DateTime.DateTime
import
DateTime
import
os
_dtmldir
=
os
.
path
.
join
(
package_home
(
globals
()
),
'dtml'
)
VIEW_PERMISSION
=
'View'
INDEX_MGMT_PERMISSION
=
'Manage ZCatalogIndex Entries'
class
DateRangeIndex
(
UnIndex
):
"""
Index a date range, such as the canonical "effective-expiration"
range in the CMF. Any object may return None for either the
start or the end date: for the start date, this should be
the logical equivalent of "since the beginning of time"; for the
end date, "until the end of time".
Therefore, divide the space of indexed objects into four containers:
- Objects which always match ( i.e., they returned None for both );
- Objects which match after a given time ( i.e., they returned None
for the end date );
- Objects which match until a given time ( i.e., they returned None
for the start date );
- Objects which match only during a specific interval.
"""
__implements__
=
(
PluggableIndex
.
PluggableIndexInterface
,
)
security
=
ClassSecurityInfo
()
meta_type
=
"DateRangeIndex"
manage_options
=
(
{
'label'
:
'Properties'
,
'action'
:
'manage_indexProperties'
}
,
)
query_options
=
[
'query'
]
since_field
=
until_field
=
None
def
__init__
(
self
,
id
,
since_field
=
None
,
until_field
=
None
,
caller
=
None
,
extra
=
None
):
if
extra
:
since_field
=
extra
.
since_field
until_field
=
extra
.
until_field
self
.
_setId
(
id
)
self
.
_edit
(
since_field
,
until_field
)
self
.
clear
()
security
.
declareProtected
(
VIEW_PERMISSION
,
'getSinceField'
)
def
getSinceField
(
self
):
"""
"""
return
self
.
_since_field
security
.
declareProtected
(
VIEW_PERMISSION
,
'getUntilField'
)
def
getUntilField
(
self
):
"""
"""
return
self
.
_until_field
manage_indexProperties
=
DTMLFile
(
'manageDateRangeIndex'
,
_dtmldir
)
security
.
declareProtected
(
INDEX_MGMT_PERMISSION
,
'manage_edit'
)
def
manage_edit
(
self
,
since_field
,
until_field
,
REQUEST
):
"""
"""
self
.
_edit
(
since_field
,
until_field
)
REQUEST
[
'RESPONSE'
].
redirect
(
'%s/manage_main'
'?manage_tabs_message=Updated'
%
REQUEST
.
get
(
'URL2'
)
)
security
.
declarePrivate
(
'_edit'
)
def
_edit
(
self
,
since_field
,
until_field
):
"""
Update the fields used to compute the range.
"""
self
.
_since_field
=
since_field
self
.
_until_field
=
until_field
security
.
declareProtected
(
INDEX_MGMT_PERMISSION
,
'clear'
)
def
clear
(
self
):
"""
Start over fresh.
"""
self
.
_always
=
IITreeSet
()
self
.
_since_only
=
IOBTree
()
self
.
_until_only
=
IOBTree
()
self
.
_since
=
IOBTree
()
self
.
_until
=
IOBTree
()
self
.
_unindex
=
IOBTree
()
# 'datum' will be a tuple of date ints
#
# PluggableIndexInterface implementation (XXX inherit assertions?)
#
def
getEntryForObject
(
self
,
documentId
,
default
=
None
):
"""
Get all information contained for the specific object
identified by 'documentId'. Return 'default' if not found.
"""
return
self
.
_unindex
.
get
(
documentId
,
default
)
def
index_object
(
self
,
documentId
,
obj
,
threshold
=
None
):
"""
Index an object:
- 'documentId' is the integer ID of the document
- 'obj' is the object to be indexed
- ignore threshold
"""
if
self
.
_since_field
is
None
:
return
0
since
=
getattr
(
obj
,
self
.
_since_field
,
None
)
if
callable
(
since
):
since
=
since
()
since
=
self
.
_convertDateTime
(
since
)
until
=
getattr
(
obj
,
self
.
_until_field
,
None
)
if
callable
(
until
):
until
=
until
()
until
=
self
.
_convertDateTime
(
until
)
datum
=
(
since
,
until
)
old_datum
=
self
.
_unindex
.
get
(
documentId
,
None
)
if
datum
==
old_datum
:
# No change? bail out!
return
0
if
old_datum
is
not
None
:
old_since
,
old_until
=
old_datum
self
.
_removeForwardIndexEntry
(
old_since
,
old_until
,
documentId
)
self
.
_insertForwardIndexEntry
(
since
,
until
,
documentId
)
self
.
_unindex
[
documentId
]
=
datum
return
1
def
unindex_object
(
self
,
documentId
):
"""
Remove the object corresponding to 'documentId' from the index.
"""
datum
=
self
.
_unindex
.
get
(
documentId
,
None
)
if
datum
is
None
:
return
since
,
until
=
datum
self
.
_removeForwardIndexEntry
(
since
,
until
,
documentId
)
del
self
.
_unindex
[
documentId
]
def
uniqueValues
(
self
,
name
=
None
,
withLengths
=
0
):
"""
Return a list of unique values for 'name'.
If 'withLengths' is true, return a sequence of tuples, in
the form '( value, length )'.
"""
if
not
name
in
(
self
.
_since_field
,
self
.
_until_field
):
return
[]
if
name
==
self
.
_since_field
:
t1
=
self
.
_since
t2
=
self
.
_since_only
else
:
t1
=
self
.
_until
t2
=
self
.
_until_only
result
=
[]
IntType
=
type
(
0
)
if
not
withValues
:
result
.
extend
(
t1
.
keys
()
)
result
.
extend
(
t2
.
keys
()
)
else
:
for
key
in
t1
.
keys
():
set
=
t1
[
key
]
if
type
(
set
)
is
IntType
:
length
=
1
else
:
length
=
len
(
set
)
result
.
append
(
(
key
,
length
)
)
for
key
in
t2
.
keys
():
set
=
t2
[
key
]
if
type
(
set
)
is
IntType
:
length
=
1
else
:
length
=
len
(
set
)
result
.
append
(
(
key
,
length
)
)
return
tuple
(
result
)
def
_apply_index
(
self
,
request
,
cid
=
''
):
"""
Apply the index to query parameters given in 'request', which
should be a mapping object.
If the request does not contain the needed parametrs, then
return None.
If the request contains a parameter with the name of the
column + "_usage", snif for information on how to handle
applying the index.
Otherwise return two objects. The first object is a ResultSet
containing the record numbers of the matching records. The
second object is a tuple containing the names of all data fields
used.
"""
record
=
parseIndexRequest
(
request
,
self
.
getId
()
)
if
record
.
keys
is
None
:
return
None
term
=
self
.
_convertDateTime
(
record
.
keys
[
0
]
)
#
# Aggregate sets for each bucket separately, to avoid
# large-small union penalties.
#
#until_only = IISet()
#map( until_only.update, self._until_only.values( term ) )
# XXX use multi-union
until_only
=
multiunion
(
self
.
_until_only
.
values
(
term
)
)
#since_only = IISet()
#map( since_only.update, self._since_only.values( None, term ) )
# XXX use multi-union
since_only
=
multiunion
(
self
.
_since_only
.
values
(
None
,
term
)
)
#until = IISet()
#map( until.update, self._until.values( term ) )
# XXX use multi-union
until
=
multiunion
(
self
.
_until
.
values
(
term
)
)
#since = IISet()
#map( since.update, self._since.values( None, term ) )
# XXX use multi-union
since
=
multiunion
(
self
.
_since
.
values
(
None
,
term
)
)
bounded
=
intersection
(
until
,
since
)
# Merge from smallest to largest.
#result = union( self._always, until_only )
result
=
union
(
bounded
,
until_only
)
result
=
union
(
result
,
since_only
)
#result = union( result, bounded )
result
=
union
(
result
,
self
.
_always
)
return
result
,
(
self
.
_since_field
,
self
.
_until_field
)
#
# ZCatalog needs this, although it isn't (yet) part of the interface.
#
security
.
declareProtected
(
VIEW_PERMISSION
,
'numObjects'
)
def
numObjects
(
self
):
"""
"""
return
len
(
self
.
_unindex
)
#
# Helper functions.
#
def
_insertForwardIndexEntry
(
self
,
since
,
until
,
documentId
):
"""
Insert 'documentId' into the appropriate set based on
'datum'.
"""
if
since
is
None
and
until
is
None
:
self
.
_always
.
insert
(
documentId
)
elif
since
is
None
:
set
=
self
.
_until_only
.
get
(
until
,
None
)
if
set
is
None
:
set
=
self
.
_until_only
[
until
]
=
IISet
()
# XXX: Store an int?
set
.
insert
(
documentId
)
elif
until
is
None
:
set
=
self
.
_since_only
.
get
(
since
,
None
)
if
set
is
None
:
set
=
self
.
_since_only
[
since
]
=
IISet
()
# XXX: Store an int?
set
.
insert
(
documentId
)
else
:
set
=
self
.
_since
.
get
(
since
,
None
)
if
set
is
None
:
set
=
self
.
_since
[
since
]
=
IISet
()
# XXX: Store an int?
set
.
insert
(
documentId
)
set
=
self
.
_until
.
get
(
until
,
None
)
if
set
is
None
:
set
=
self
.
_until
[
until
]
=
IISet
()
# XXX: Store an int?
set
.
insert
(
documentId
)
def
_removeForwardIndexEntry
(
self
,
since
,
until
,
documentId
):
"""
Remove 'documentId' from the appropriate set based on
'datum'.
"""
if
since
is
None
and
until
is
None
:
self
.
_always
.
remove
(
documentId
)
elif
since
is
None
:
set
=
self
.
_until_only
.
get
(
until
,
None
)
if
set
is
not
None
:
set
.
remove
(
documentId
)
if
not
set
:
del
self
.
_until_only
[
until
]
elif
until
is
None
:
set
=
self
.
_since_only
.
get
(
since
,
None
)
if
set
is
not
None
:
set
.
remove
(
documentId
)
if
not
set
:
del
self
.
_since_only
[
since
]
else
:
set
=
self
.
_since
.
get
(
since
,
None
)
if
set
is
not
None
:
set
.
remove
(
documentId
)
if
not
set
:
del
self
.
_since
[
since
]
set
=
self
.
_until
.
get
(
until
,
None
)
if
set
is
not
None
:
set
.
remove
(
documentId
)
if
not
set
:
del
self
.
_until
[
until
]
def
_convertDateTime
(
self
,
value
):
if
value
is
None
:
return
value
if
type
(
value
)
==
type
(
''
):
dt_obj
=
DateTime
(
value
)
value
=
dt_obj
.
millis
()
/
1000
/
60
# flatten to minutes
if
isinstance
(
value
,
DateTime
):
value
=
value
.
millis
()
/
1000
/
60
# flatten to minutes
return
int
(
value
)
InitializeClass
(
DateRangeIndex
)
manage_addDateRangeIndexForm
=
DTMLFile
(
'addDateRangeIndex'
,
_dtmldir
)
def
manage_addDateRangeIndex
(
self
,
id
,
extra
=
None
,
REQUEST
=
None
,
RESPONSE
=
None
,
URL3
=
None
):
"""
Add a date range index to the catalog, using the incredibly icky
double-indirection-which-hides-NOTHING.
"""
return
self
.
manage_addIndex
(
id
,
'DateRangeIndex'
,
extra
,
REQUEST
,
RESPONSE
,
URL3
)
lib/python/Products/PluginIndexes/DateRangeIndex/README.txt
0 → 100644
View file @
b3048e12
DateRangeIndex README
Overview
Zope applications frequently wish to perform efficient queries
against a pair of date attributes/methods, representing a time
interval (e.g., effective / expiration dates). This query *can*
be done using a pair of indexes, but this implementation is
hideously expensive:
o DateTime instances are *huge*, both in RAM and on disk.
o DateTime instances maintain an absurd amount of precision, far
beyond any reasonable search criteria for "normal" cases.
o Results must be fetched and intersected between two indexes.
o Handling objects which do not specify both endpoints (i.e.,
where the interval is open or half-open) is iffy, as the
default value needs to be coerced into a different abnormal
value for each end to permit ordered comparison.
o The *very* common case of the open interval (neither endpoint
specified) should be optimized.
DateRangeIndex is a pluggable index which addresses these issues
as follows:
o It groups the "open" case into a special set, '_always'.
o It maintains separate ordered sets for each of the "half-open"
cases.
o It performs the expensive "intersect two range search" operation
only on the (usually small) set of objects which provide a
closed interval.
o It flattens the key values into integers with granularity of
one minute.
o It normalizes the 'query' value into the same form.
lib/python/Products/PluginIndexes/DateRangeIndex/__init__.py
0 → 100644
View file @
b3048e12
# Empty on purpose
lib/python/Products/PluginIndexes/DateRangeIndex/dtml/addDateRangeIndex.dtml
0 → 100644
View file @
b3048e12
<dtml-var manage_page_header>
<dtml-var "manage_form_title(this(), _,
form_title='Add DateRangeIndex')">
<p class="form-help">
A DateRangeIndex takes the name of two input attributes; one containing the
start date of the range, the second the end of the range. This index is filled
with range information based on those two markers. You can then search for
objects for those where a given date falls within the range.
</p>
<form action="addDateRangeIndex" method="POST">
<table cellspacing="0" cellpadding="2" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Id
</div>
</td>
<td align="left" valign="top">
<input type="text" name="id" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Since field
</div>
</td>
<td align="left" valign="top">
<input type="text" name="extra.since_field:record" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Until field
</div>
</td>
<td align="left" valign="top">
<input type="text" name="extra.until_field:record" size="40" />
</td>
</tr>
<tr>
<td align="left" valign="top">
</td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value=" Add " />
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
lib/python/Products/PluginIndexes/DateRangeIndex/dtml/manageDateRangeIndex.dtml
0 → 100644
View file @
b3048e12
<dtml-var manage_page_header>
<dtml-var manage_tabs>
<p class="form-help">
You can update this DateRangeIndex by editing the following field and clicking
<emUpdate</em>.
</p>
<form action="&dtml-URL1;/manage_edit" method="POST">
<table cellpadding="2" cellspacing="0" border="0">
<tr>
<td align="left" valign="top">
<div class="form-label">
Since field
</td>
<td align="left" valign="top">
<input name="since_field" value="&dtml-getSinceField;" size="40" />
</td>
</tr>
<td align="left" valign="top">
<div class="form-label">
Until field
</td>
<td align="left" valign="top">
<input name="until_field" value="&dtml-getUntilField;" />
</td>
</tr>
<tr>
<td></td>
<td align="left" valign="top">
<div class="form-element">
<input class="form-element" type="submit" name="submit"
value="Update">
</div>
</td>
</tr>
</table>
</form>
<dtml-var manage_page_footer>
lib/python/Products/PluginIndexes/DateRangeIndex/tests/test_DateRangeIndex.py
0 → 100644
View file @
b3048e12
##############################################################################
#
# Copyright (c) 2001 Zope Corporation and Contributors. All Rights Reserved.
#
# This software is subject to the provisions of the Zope Public License,
# Version 2.0 (ZPL). A copy of the ZPL should accompany this distribution.
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
# FOR A PARTICULAR PURPOSE
#
##############################################################################
import
Zope
import
unittest
from
Products.PluginIndexes.DateRangeIndex.DateRangeIndex
import
DateRangeIndex
class
Dummy
:
def
__init__
(
self
,
name
,
start
,
stop
):
self
.
_name
=
name
self
.
_start
=
start
self
.
_stop
=
stop
def
name
(
self
):
return
self
.
_name
def
start
(
self
):
return
self
.
_start
def
stop
(
self
):
return
self
.
_stop
def
datum
(
self
):
return
(
self
.
_start
,
self
.
_stop
)
dummies
=
[
Dummy
(
'a'
,
None
,
None
)
,
Dummy
(
'b'
,
None
,
None
)
,
Dummy
(
'c'
,
0
,
None
)
,
Dummy
(
'd'
,
10
,
None
)
,
Dummy
(
'e'
,
None
,
4
)
,
Dummy
(
'f'
,
None
,
11
)
,
Dummy
(
'g'
,
0
,
11
)
,
Dummy
(
'h'
,
2
,
9
)
]
def
matchingDummies
(
value
):
result
=
[]
for
dummy
in
dummies
:
if
(
(
dummy
.
start
()
is
None
or
dummy
.
start
()
<=
value
)
and
(
dummy
.
stop
()
is
None
or
dummy
.
stop
()
>=
value
)
):
result
.
append
(
dummy
)
return
result
class
DRI_Tests
(
unittest
.
TestCase
):
def
setUp
(
self
):
pass
def
tearDown
(
self
):
pass
def
test_empty
(
self
):
empty
=
DateRangeIndex
(
'empty'
)
assert
empty
.
getEntryForObject
(
1234
)
is
None
empty
.
unindex_object
(
1234
)
# shouldn't throw
assert
not
empty
.
uniqueValues
(
'foo'
)
assert
not
empty
.
uniqueValues
(
'foo'
,
1
)
assert
empty
.
_apply_index
(
{
'zed'
:
12345
}
)
is
None
result
,
used
=
empty
.
_apply_index
(
{
'empty'
:
12345
}
)
assert
not
result
assert
used
==
(
None
,
None
)
def
test_retrieval
(
self
):
work
=
DateRangeIndex
(
'work'
,
'start'
,
'stop'
)
for
i
in
range
(
len
(
dummies
)
):
work
.
index_object
(
i
,
dummies
[
i
]
)
for
i
in
range
(
len
(
dummies
)
):
assert
work
.
getEntryForObject
(
i
)
==
dummies
[
i
].
datum
()
for
value
in
range
(
-
1
,
15
):
matches
=
matchingDummies
(
value
)
results
,
used
=
work
.
_apply_index
(
{
'work'
:
value
}
)
assert
used
==
(
'start'
,
'stop'
)
assert
len
(
matches
)
==
len
(
results
),
(
'%s: %s == %s'
%
(
value
,
map
(
lambda
x
:
x
.
name
(),
matches
),
results
)
)
matches
.
sort
(
lambda
x
,
y
:
cmp
(
x
.
name
(),
y
.
name
()
)
)
for
result
,
match
in
map
(
None
,
results
,
matches
):
assert
work
.
getEntryForObject
(
result
)
==
match
.
datum
()
def
test_suite
():
suite
=
unittest
.
TestSuite
()
suite
.
addTest
(
unittest
.
makeSuite
(
DRI_Tests
)
)
return
suite
def
run
():
unittest
.
TextTestRunner
().
run
(
test_suite
())
if
__name__
==
'__main__'
:
run
()
lib/python/Products/PluginIndexes/__init__.py
View file @
b3048e12
...
...
@@ -20,8 +20,17 @@ import TextIndex.TextIndex
import
FieldIndex.FieldIndex
import
KeywordIndex.KeywordIndex
import
TopicIndex.TopicIndex
_indexes
=
(
'TextIndex'
,
'KeywordIndex'
,
'FieldIndex'
,
'PathIndex'
,
'TopicIndex'
)
import
DateIndex.DateIndex
import
DateRangeIndex.DateRangeIndex
_indexes
=
(
'TextIndex'
,
'KeywordIndex'
,
'FieldIndex'
,
'PathIndex'
,
'TopicIndex'
,
'DateIndex'
,
'DateRangeIndex'
,
)
def
initialize
(
context
):
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment