Re: Re[4]: newbie question - writing derived view back to db

Brian Kelley <[email protected]>
Newsgroups gmane.comp.db.metakit
Message-ID <[email protected]>
On Wed, 19 Jan 2005 01:32:47 +0100, Marcin Krol <[email protected]> wrote:

> I'm actually somewhat disappointed by Python interface to MK. I thought I
> would be able to access MK view as if it were a list of lists or list
> of dictionaries, not via the 'RowRef' objects with dot access, 

Back in the day, I felt like you that this aspect of metakit was
cumbersome, but I finally came to realize the power of the rowref.  If
you had the dictionary approach, every element of the row would have
to be accessed and converted.

for row in view:
     print row.a

Only the 'a' attribute is ever used and retrieved.  Furthermore, since
metakit is column oriented, the column is sequential in memory/disk
making access tremendously fast.  Now, if you find the value of a that
you like, then you can access the other columns.

Now, if you are just 

> However, in the following statement: like
> ViewName.PropertyName and iterating over view or doing
> find(name='Joe'). I'd prefer smth more resembling typical operation of
> Python's list/dicts. An ideal would be IMHO smth like:
> 
> a={'joe':['doe', 44], 'john': ['lennon', 45], 'joe': ['smith', 42]}
> 
> ..but implemented in such a way that a user could access any column as
> a key, smth a la multi-way dictionary, so user could specify
> 
> a[(0, 'joe')]

You are, of course, free to implement this functionality, python is
very dynamic in this regard, but should this throw an exception if the
key doesn't exist like a dictionary?  What if you multiple values that
match (0,'joe')?  If there is one should it return an object and for
many should it return a list?

You can certainly convert a view into a set of dictionaries, but
remember that in the metakit world, the underlying view can change
*underneath* you.  Columns can be removed, rows can be deleted and the
row reference will *still* work!
> 
>  I'm sure implementing smth like this
> would be a lot of work, though.

Ask and ye shall receive.  The following is a metakit wrapper that
wraps metakit calls.  There may be bugs in it so beware.  It behaves
just like metakit except:

import mkwrap
st = mkwrap.storage()
vw = st.getas(...)

vw[0,'a'] -> retrieves column 'a' at row 0, equivalent to vw[0].a
vw.asDict() -> returns lists of internal dictionaries

row.asDict() -> converts the row to a dictionary.

Enjoy

_____________________________________________
Metakit mailing list  -  [email protected]
http://www.equi4.com/mailman/listinfo/metakit
mkwrap.py (application/octet-stream, 4.2 KB)
"""usage: import metawrap as metakit
use just like metakit with the following changes:
print view ->         outputs the metakit dump version of a view, useful 
for debugging
view.columns() -> list the attributes (columns) of the view
view.asDict() -> return the row dictionaries
print row ->          prints the contents of a row
row.asDict() ->     returns a dictionary of the row's values.
                           The downside is that you can't have an 
attribute "asDict" in the row.
"""
import metakit, sys, StringIO, types

# Implementation notes: The hardest thing to understand here is the
# _view class below.  The trick is that sometimes a metakit function
# returns a seperate view which we would also like to wrap up in our
# handy view class.
#
# This is done by "intercepting" these functions (see VIEW_RETURNS below)
# with a special class named _view
# This class keeps track of the view associated with the function and
# the name of the function called.
# When the function is actually called, _view.__call__ is called
# with the appropriate parameters.  The result of applying the
# function with these parameters is then wrapped up in a view class
# and returned.  *whew*!
#
# you can always add methods to replace these though, for
# example you can add a view.append() method to replace the default
# append.

class MetaError(Exception): pass
def dump(view):
    stdout = sys.stdout
    sys.stdout = StringIO.StringIO()
    metakit.dump(view)
    text = sys.stdout.getvalue()
    sys.stdout = stdout
    return text
   
class storage:
    def __init__(self, filename=None, mode=None):
        if filename and mode:
            self.__db = metakit.storage(filename, mode)
        elif filename and not mode:
            raise MetaError("Need to supply both a filename and a mode")
        else:
            self.__db = metakit.storage()
    def __getattr__(self, attribute): return getattr(self.__db, attribute)
    def getas(self, table): return view(self.__db.getas(table))
    def view(self, viewname): return view(self.__db.view(viewname))

# these functions return views as well
VIEW_RETURNS = ["indices", "rview", "select",
                "project", "flatten", "join", "unique", "intersect",
                "different", "minus", "remapwith", "pair", "rename",
                "product", "groupby", "counts", "blocked", "hashed",
                "sort", "sortrev"]

class _view:
    """This class wraps views returned by view method functions."""
    def __init__(self, vw, st, funcname):
        self.vw = vw
        self.st = st
        self.func = func
    def __call__(self, *a, **kw):
        return view(vw=apply(getattr(self.vw, self.funcname), a, kw),
                    st=self.st)
   
class view:
    def __init__(self, vw=None, st=None):
        if vw is None: vw = metakit.view()
        self.__view = vw
        self.__storage = storage
    def __getattr__(self, attribute):
        if attribute in VIEW_RETURNS: return _view(self.__view, self.__storage, attribute)
        else: return getattr(self.__view, attribute)
    def __len__(self): return len(self.__view)
    def __getslice__(self, lo, hi): return view(self.__view[lo:hi])
    def __delslice__(self, lo, hi):  del self.__view[lo:hi]
    def __getitem__(self, index): 
	if type(index) == types.TupleType:
	    _row, col = index
	    return getattr(self[_row], col)
	return row(self.__view[index], self)
    def __str__(self): return dump(self)
    def columns(self): return [x.name for x in self.__view.structure()]
    def asDict(self): return [row.asDict() for row in self]

class row:
    def __init__(self, rw, vw):
        self.__rw = rw
        self.__vw = vw
    def __repr__(self):
        res = ["Row with attributes:"]
        for prop in self.__vw.structure():
            res.append("\t%s:\t%s"%(prop.name, getattr(self.__rw, prop.name)))
        return "\n".join(res)
    def __getattr__(self, column):
        if column[0:6] == "_row__": return self.__dict__[column]
        else: return getattr(self.__rw, column)
    def __setattr__(self, column, value):
        if column[0:6] == "_row__": self.__dict__[column] = value
        else: setattr(self.__rw, column, value)
    def asDict(self):
        res = {}
        for prop in self.__vw.structure():
            res[prop.name] = getattr(self.__rw, prop.name)
        return res
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.