|
From: Jeremy F. <jfi...@ma...> - 2002-10-31 22:26:18
|
Nathan -
As for the XML descriptor format I totally agree. In fact, after we =
discussed this last week I put together an example that is almost =
identical to what you've constructed. This seems to be a no-brainer and =
the parser just needs to be built.
In terms of the array issue I keep harping on, your suggestion of a =
generic 'array' field type got me thinking. What we're really talking =
about here are collections (to use a java term). The "join" terminology =
seems to come from the relational database world, and, please correct me =
if I'm wrong, doesn't really fit into an OO context.=20
What if we extend basefield with a basecollection component. A few =
accessor methods to basecollection are added, or maybe override =
basefield, to enable access to multiple items, be they simple or complex =
values. Then any type of collection can be constructed: the =
contentObject array, an array of string values, maybe even structures. =
Most of the basefield methods work 'out of the box' and problematic =
methods, such as renderSimpleForm(), are overriden or nullified (made to =
throw an exception perhaps). With the new XML descriptor format a 'key' =
property to the collection type allows for simple identification:
<contentType label=3D"Thread">
<fields>
<field name=3D"thread"
label=3D"Thread"
type=3D"org.bacfug.modus.fields.text">
<rules>
<rule type=3D"org.bacfug.modus.validation.full"/>
<rule type=3D"org.bacfug.modus.validation.maxlength" value=3D"100"/>
</rules>=20
</field>
<field name=3D"postid"
type=3D"org.bacfug.modus.fields.hidden"/>
<field name=3D"posts"
label=3D"Posts"
type=3D"org.bacfug.modus.fields.objectcollection"
key=3D"postid"/>=20
</fields>
</contentType>
It seems that this should work without having to create a baseFieldValue =
component, as you've suggested - although if you're still thinking about =
that, and it seems to offer more flexibility, let us know.
I think a few more concrete examples might help flesh this thing out. =
We've got the simple press release and I've offered a few examples of =
'parent is composed of child' type relationships. Maybe someone has a =
scenario in which the relationship is not one of composition, such as =
the thread containing posts. I'd be curious to see what a many-to-many =
relationship would look like in object terms. Are there situations in =
which this collection field type won't work?
-Jeremy
----- Original Message -----=20
From: Nathan Dintenfass=20
To: mod...@li...=20
Sent: Tuesday, October 29, 2002 8:45 PM
Subject: RE: [Modus-devs] Collections of content objects and =
performance
So, the question before us is whether "child" objects or "joined" =
objects
should be a separate kind of attribute of a contentObject from a =
field.
Hmm.
One question I've been thinking about is whether this is about a
parent-child relationship, in which case it makes more sense to use =
the way
you've been working on (and I'd consider calling it =
getChildren("threads")
or something like that. Or, is it more that a contentObject can have =
some
"joins" -- and really we need a way to define a join (as being either
one-to-many or many-to-many, for instance).
Seems the first issue is whether a field should be able to be of a =
type that
is actually just another contentObject. An interesting question. I =
think
the answer is yes, but I can also see how it causes a lot of problems =
for
the API and the cleanliness of the code base. One idea I floated was =
that
there should be a way for a field to be an array of values, =
independent of
whether it's contentObjects or not. Then, have a way for the value of =
the
field to be another contentObject. A field would then, perhaps, have
methods like isArray() and isContentObject() -- which would help a =
user who
might want to build some abstracted interfaces. Both arrays of values =
and
contentObjects as values creates some nastiness in the interface, =
though,
potentially. For instance, renderSimpleForm() on a contentObject -- =
could
get ugly.
Right now I'm thinking that moving towards the XML descriptor is the =
first
step to any of this. I am increasingly convinced that the CFPROPERTY =
route
is too limiting from an programming standpoint and apparently is also
potentially a big problem when it comes to performance (per an =
off-line
discussion with Sean Corfield).
Once we're using XML I think a lot of the other issues will be easier =
to
tackle. So, for instance, the pressRelease.cfc in the modustest app =
might
look like:
<contentObjectType label=3D"Press Release">
<fields>
<field
name=3D"title"
label=3D"Title"
type=3D"org.bacfug.modus.fields.text">
<rules>
<rule
name=3D"org.bacfug.modus.validation.full" />
</rules>
</field>
<field
name=3D"body"
label=3D"Main Body"
type=3D"org.bacfug.modus.fields.longText"/>
<field
name=3D"image"
label=3D"Photo"
type=3D"org.bacfug.modus.fields.webImage"/>
<field
name=3D"featured"
label=3D"Featured?"
type=3D"org.bacfug.modus.fields.yesno"
default=3D"no">
<rules>
<rule
name=3D"org.bacfug.modus.validation.boolean"/>
</rules>
</field>
<field
name=3D"category"
label=3D"Category"
type=3D"modustest.contentObjects.categoryPicker"/>
</fields>
<contentObjectType>
First question: does that make sense?
So, now the issue is if I were building an app with child components =
(or
joined components) it might look like:
<contentObject label=3D"Forum">
<field
name=3D"threads"
isArray=3D"yes"
type=3D"myApp.threadComponent">
</contentObject>
Or, it might look like:
<contentObject label=3D"Forum">
<childObjects>
<objectType type=3D"myApp.threadComponent">
</childObjects>
</contentObject>
Or, perhaps:
<contentObject label=3D"Forum">
<joins>
<join type=3D"one2many" objectType=3D"myApp.threadComponent">
</joins>
</contentObject>
Any thoughts from the rest of you about which would be best?
In that last one, would we think that the thread than also has a join =
in its
definition? Would we need a separate place where a join is defined =
outside
of this config file?
Tangent: if we have fields that have arrays of values, do we want to =
expose
the CF array, or would we want to create methods to traverse it (using =
some
kind of iterator) to maintain the object-based API?
Hope that's enough to continue the conversation . . .
- Nathan
-----Original Message-----
From: mod...@li...
[mailto:mod...@li...]On Behalf Of Jeremy
Firsenbaum
Sent: Tuesday, October 29, 2002 2:51 PM
To: modus devs
Subject: [Modus-devs] Collections of content objects and performance
Thought this thread might be of interest to others on the list. Nathan =
and I
have been talking about a couple of issues I've raised, namely,
contentObjects containing collections of other contentObjects and the
performance impact of instantiating so many objects.
Note that this reads from the bottom up:
Nathan - certainly not wed to the interface. But unless, as you =
suggest, the
definition of what a field is changes I don't see a way of using =
basefield
for content objects. Back to the forum example I've been using - =
retrieving
a collection of threads:
The way I suggested:
<cfset forum=3D createObject("component","forum").init(forumID)>
<cfset threads=3D category.getObjectArray('threads')>
<cfloop from=3D"1" to=3D"#arrayLen(thread)#" index=3D"i">
<!--- doing stuff with each thread, like: --->
#thread[i].getField('thread').getValue()#
</cfloop>
One possible way of doing what you suggest:
<cfset forum=3D createObject("component","forum").init(forumID)>
<cfset threads=3D category.getField('threads').getValue()>
<cfloop from=3D"1" to=3D"#arrayLen(thread)#" index=3D"i">
#thread[i].getField('thread').getValue()#
</cfloop>
As you can see the only difference is the method call for the threads =
array.
And of course this gets back to the descriptor for the content object. =
In
the first case thread would have a property type of 'objectArray' or
'subcomponent' or something like that, and in the latter it has the =
type
'field' with a particular fieldType.
If the latter seems cleaner to you, fine. I just did it this way to =
avoid
reworking basefield - sort of a proof of concept. The developer using =
the
API already needs to know the 'type' for various fields. Most are =
simple
strings, but anything that extends basefile will certainly be handled
differently, or at least make several new methods available. So making
'contentObject' be a possible fieldType should be doable - the
basefieldValue idea is probably the way to go. Let me think about that =
a
bit.
As for the performance issue, it comes in precisely when you want to
instantiate a whole array of contentObjects. If I call load() on a 40 =
page
book object, a new instance has to be created for each of these pages. =
The
only way to avoid that is to reference the cached object, which opens =
up a
whole other can of worms - locking the server scope, concurrent access =
to
writable data... This may be possible, but is this a route worth going =
down.
Maybe others have thoughts.
- Jeremy
----- Original Message -----
From: Nathan Dintenfass
To: Jeremy Firsenbaum
Sent: Tuesday, October 29, 2002 12:27 PM
Subject: RE:
Ah, I see what you mean. Yes, get() does that, but only if you wish =
to
populate from another instance -- which you pass in -- that is, if you =
have
an existing contentObject that you want to "load()", then you need to =
do
this. But, in this case you still never need to recreate a whole new
instance as you already have the one you want to load and the one you
retrieve from the cache (except on the first hit) -- but, what you are
saying is that is a performance bottleneck for you, right? Hmm.
On the "interface" I was talking about -- yes, the API exposed to the =
end
user. I figured you were not wed to it. My instinct says that it =
would be
better to do an overhaul of what a "field" is. First, I'd make it =
possible
to have an array of values, even if not other contentObject. Then, =
I'd work
on making it possible to have the value of a field be a contentObject.
Perhaps baseFieldValue.cfc would be one way to get towards that??
I totally agree that one way or another there needs to be a way to =
have a
"book" and its "pages" (not to mention many-to-many relationships) and =
have
"book" and "page" each be instances of a contentObject.
- Nathan
-----Original Message-----
From: Jeremy Firsenbaum [mailto:jfi...@ma...]
Sent: Monday, October 28, 2002 5:33 PM
To: na...@ch...
Subject: Re:
"By the way, the only place I am using the makeClone() is when I put =
objects
into the cache, not when I take them out of the cache"
I'm sorry if I was misleading. "Cloning" takes place going in as well =
as
coming out, although the makeClone method is only called one way. =
Going in,
makeclone calls contentObjectPopulateFromInstance, which does all of =
the
actually duplicating. Coming out, get() calls
contentObjectPopulateFromInstance as well:
line 41 from simplefilesystempersister.cfc:
if(instance.cache.isObjectCached(arguments.id)){
objectRetrieved =3D instance.cache.getObject(arguments.id);
if(structCount(arguments) GT 1)
return =
contentObjectPopulateFromInstance(arguments[2],objectRetrieved);
else
return objectRetrieved;
}
I found the duplicating from a cached instance in
contentObjectPopulateFromInstance to be the bottleneck.
"I'm not sure I'm personally satisfied with the interface you are =
using in
your forums app."
I'm all up for criticism - but not sure what you're referring to by
interface. Do you mean the contentObjectArray within =
basecontentobject, or
the way in which the API is used externally to construct the forum.
Obviously this was just an example I was using to work with the idea =
of
content objects within content objects.
"Are you waiting for a signal from me as to what my alternative might =
be?"
Well - kinda, yeah. I mean I would like to know if this approach to
"composition" even makes sense, or whether there's another way of =
doing
"parts of" relationships within modus. Many systems, content =
management or
otherwise, have this kind of structure. An artifact, say a book, has =
many
pages, each one of which has been scanned in. At one level, you want =
to deal
with the book as a whole (when presenting a library of books), on =
another
level, each one of the pages is a content object unto itself (so that =
the
book is browsable). I would like to be able to load the book, for a =
single
request or in session scope, and have the pages come along for the =
ride,
just as each thread is available within the forum content object. Does =
this
even make sense within the context of what modus was and is being =
designed
to do?
----- Original Message -----
From: Nathan Dintenfass
To: Jeremy Firsenbaum
Sent: Monday, October 28, 2002 7:56 PM
Subject: RE:
Ah, yes. Well, right now you are at the cutting edge of that =
question. I'm
not sure I'm personally satisfied with the interface you are using in =
your
forums app. Doesn't feel clean to me, but I have a hard time =
justifying
that feeling. Are you waiting for a signal from me as to what my
alternative might be?
By the way, the only place I am using the makeClone() is when I put =
objects
into the cache, not when I take them out of the cache -- are you
experiencing serious performance problems with that? My main issue =
with
performance before the caching was with the getAll(). Load() is still =
a bit
slower than I'd like, but I figure that will be rarely called in =
volume
since getAll would be generally be faster. That is an area to work =
on,
though.
- n
-----Original Message-----
From: Jeremy Firsenbaum [mailto:jfi...@ma...]
Sent: Monday, October 28, 2002 4:45 PM
To: na...@ch...
Subject: Re:
Sorry for being obtuse - I mean being able to declare other content =
objects
as being contained within another content object: forums are composed =
of
threads, which are composed of posts...
----- Original Message -----
From: Nathan Dintenfass
To: Jeremy Firsenbaum
Sent: Monday, October 28, 2002 7:37 PM
Subject: RE:
When you say "composition of content object" what do you mean?
- n
-----Original Message-----
From: Jeremy Firsenbaum [mailto:jfi...@ma...]
Sent: Monday, October 28, 2002 4:33 PM
To: na...@ch...
Subject: Re:
Fair enough. Maybe it's all of the nested subcomponents that accounts =
for
the slowness in cloning from the cache.
I'd love to do some more work on modus, maybe sink my teeth in the XML
descriptor thing, but I feel like I can't do much with it until I know
whether composition of content objects is feasible. This seems to be =
the
most important feature needed to model more complex systems. Looking =
forward
to seeing what you think.
-Jeremy
----- Original Message -----
From: Nathan Dintenfass
To: Jeremy Firsenbaum
Sent: Monday, October 28, 2002 7:13 PM
Subject: RE:
Jeremy:
I hadn't yet gotten to look in depth at your changes. I agree the =
clone()
method is far from optimal (as I mentioned in that posting).
My comment was based on the dramatic performance enhancement I =
witnessed on
the modustest app after implementing the caching. My calls to =
getAll() went
from over 400ms with a handful of "press releases" to 20ms.
- n
-----Original Message-----
From: Jeremy Firsenbaum [mailto:jfi...@ma...]
Sent: Monday, October 28, 2002 3:52 PM
To: Nathan Dintenfass
Subject:
Hey Nathan
I saw your post to CFCDev today:
"I have taken pages that were slow because they had to instantiate =
dozens of
components and made them very fast by just grabbing from the cache in =
the
server scope."
This got me wondering if you have looked into the performance issue =
with
clone() that I mentioned last week. It seems like it wouldn't make a
difference whether the subcomponents were instantiated within the
contentObject, as I had tried to do, or if this were left to the =
developer
to do externally: forums =3D createObject (forum.cfc), loop for =
threads =3D
createObject(thread.cfc). If you need "dozens of components" the clone
technique doesn't seem to be optimal.
Were my performance numbers similar to yours (if you've had time to =
look
into this) or am I totally off base here?
Jeremy
-------------------------------------------------------
This sf.net email is sponsored by:ThinkGeek
Welcome to geek heaven.
http://thinkgeek.com/sf
_______________________________________________
Modus-devs mailing list
Mod...@li...
https://lists.sourceforge.net/lists/listinfo/modus-devs |