An example of creating an interface using queries, rather than direct table references. This is how we built the Hollywood Web demo, which answers questions about Oscar-winners.
Sometimes the straight-forward approach is not the best approach. Here is an example which shows why there are many options built-in to Access ELF that let you customize the way an interface is set up.
The Hollywood database looks simple enough, at first glance.
All we want to do is create an interface that lets people ask questions about which movies,
which actors, and which actresses have won Academy Awards (Oscars) and which others have been
nominated for these awards. There are two main problems. One is that people will use the
word "who" referring to either movies or people, for instance:
Who won for Best Actor in 1954?" or
"Who won Best Picture in 1994?"
Another problem is that each entry in the Awards table has a Status field which is either
"Nominee" or "Winner". Now if we ask "Who won such and such..." it makes sense to return the
entries with "Winner". But if we ask "Who was nominated for such and such..." we want to see,
not only the Nominees, but also the Winner!
These aren't big problems, but they're typical of the small adjustments you'll need to make
when applying our software to your own databases. In fact, there are many ways to solve these
problems, since ELF is highly programmable. In this case, we'll start by substituting custom-built
queries for the tables in this database. First we define a ScreenArtists query:
This corresponds to the SQL
SELECT DISTINCTROW Movies.MovieName, Artists.Artist AS ArtistName, AWARDS.AwardYear, AWARDS.Category, AWARDS.Status, Movies.Director
FROM (Artists RIGHT JOIN AWARDS ON Artists.[Artist ID] = AWARDS.[Artist ID]) LEFT JOIN Movies ON AWARDS.[Movie ID] = Movies.[Movie ID];
Now we create a second query called OscarWinners, exactly the same as the first except that
it filters on the Status, showing only the Winners.
This corresponds to the SQL
SELECT DISTINCTROW Movies.MovieName, Artists.Artist AS ArtistName, AWARDS.AwardYear, AWARDS.Category, AWARDS.Status, Movies.Director
FROM (Artists RIGHT JOIN AWARDS ON Artists.[Artist ID] = AWARDS.[Artist ID]) LEFT JOIN Movies ON AWARDS.[Movie ID] = Movies.[Movie ID]
WHERE (((AWARDS.Status)="Winner"));
The interesting part about the query is the JOIN that connects Awards to Artists. The arrow indicates that it's defined as Include ALL from AWARDS and only those records from 'Artists' where the joined fields are equal. This is because the records in the AWARDS table that relate to "Best Picture" don't have an entry for ArtistID, only one for MovieID. (The other JOIN is defined the same way, but that appears to be done out of someone's love of symmetry; it makes no difference if it's defined as a normal INNER JOIN, since every AWARD record has a MovieID and so does every Movie record!)
One point is that now both recordsets show both a MovieName and an ArtistName; of course, the ArtistName will be blank when displaying nominees and winners for "Best Picture". We've pretty much made sure that ELF will consider the MovieName the "key" of each query by putting it in the first position. So every answer will show the movie involved. Showing the Artist will happen naturally whenever someone asks a "who"-type question, because ELF knows that an "Artist" is a type of Person. (To verify this, enter artist in the Lexicon Lookup window and click Reference. The definition of artist is coded with an "m" for "mankind".)
Now we run the Access ELF analysis on this database, choosing Analyze Queries and selecting a Custom Analysis. In the Access ELF Custom Analysis window we make sure to select ONLY the two queries ScreenArtists and OscarWinners. A neat feature of Access ELF is that you can select a query without selecting the underlying table that it gets data from. In that case, ELF actually runs the query and enters all the data returned by the query into its lexicon.
When the Analysis is done, let's try a test question,
who won for Best Picture in 1954?
The SQL generated is
SELECT DISTINCTROW ScreenArtists.ArtistName , ScreenArtists.AwardYear , ScreenArtists.MovieName FROM ScreenArtists WHERE ( ScreenArtists.Category = "Best Picture" and ( ( ( ScreenArtists.AwardYear >= #01/01/54# and ScreenArtists.AwardYear < DateAdd ( "YYYY" , 1 , #01/01/54# ) ) ) ) ) ;
and the answer is
ArtistName AwardYear MovieName
1954 On the Waterfront
1954 Three Coins in the Fountain
1954 Seven Brides for Seven Brothers
1954 Country Girl
1954 Caine Mutiny
Well, this is wrong!
We solved the problem about showing both Movie and Artist, but forgot the part about deciding
when to show Winners and when to show all Nominees. But now this is an easy fix. All we have to
do is to convince ELF to generate a reference to the OscarWinners query at some times, and the
ScreenArtists query otherwise. We'll use the word "won" as a trigger.
Open the Phrase definition editor (click "ABC" on the Toolbar) and add an entry:
When I Type: "won"
I Really Mean: was OscarWinners_ . ArtistName
Notice that in order to make reference to a data set, you need to append an underscore to it; this applies to both tables and queries. This definition will substitute a reference to the ArtistName field of the OscarWinners query every time a user submits a question with the word "won"
Once we enter this definition, we can immediately test it. Phrase definitions are fully
interactive; you don't need to re-run an Analysis each time you add one! Now the answer to
who won for Best Picture in 1954? is returned correctly as:
ArtistName AwardYear MovieName
1954 On the Waterfront
with the generated SQL being
SELECT DISTINCTROW OscarWinners.ArtistName , OscarWinners.AwardYear , OscarWinners.MovieName FROM OscarWinners WHERE ( ( OscarWinners.Category = "Best Picture" and ( ( OscarWinners.AwardYear >= #01/01/54# and OscarWinners.AwardYear < DateAdd ( "YYYY" , 1 , #01/01/54# ) ) ) ) ) ;
Pretty amazing, this is all that's needed to handle a variety of the most common
(and even some uncommon) questions on this database. For example,
Which pictures had more than one winner for Best Director?
Count_Of ArtistName MovieName
2
West Side Story
SELECT DISTINCTROW OscarWinners.MovieName , OscarWinners.ArtistName FROM OscarWinners WHERE ( ( OscarWinners.status = "Winner" and OscarWinners.Category = "Best Director" ) ) ; SELECT DISTINCT count ( [elfQ1].ArtistName ) AS [Count_Of ArtistName] , [elfQ1].MovieName FROM [elfQ1] group by [elfQ1].MovieName HAVING Not IsNull([elfQ1].MovieName) ; SELECT DISTINCT [elfQ2].* FROM [elfQ2] WHERE [elfQ2].[Count_Of ArtistName] > 1 ; SELECT DISTINCT [elfQ3].* FROM [elfQ3] , OscarWinners , [elfQ3] INNER JOIN OscarWinners ON [elfQ3].MovieName = OscarWinners.MovieName WHERE ( ( OscarWinners.status = "Winner" and OscarWinners.Category = "Best Director" ) ) order by [elfQ3].MovieName ;
Turns out this is the only co-directed film to ever win Best Picture. It's amazing what you can learn!
Now how did we pick that exact subsitution phrase "was OscarWinners_ . ArtistName"? Access ELF is highly syntax-sensitive, meaning you can't just type in keywords as if it were an Internet search engine. The word "won" is an inflected verb, and plays an important part in the question. If we substitute just "OscarWinners_ . ArtistName" for "won", we're really asking Access ELF to answer the question: who OscarWinners_ . ArtistName for Best Picture in 1954? and it won't be able to.
This brings us to the last little wrinkle in this implementation, and gives us a chance to show
off another neat feature of the Phrases facility. What if we type in a very similar question:
who won Best Picture in 1954? (exactly the same, but without "for"). Now
the substitution defined above leads to:
who was OscarWinners_ . ArtistName Best Picture in 1954? This is pretty
close, but it turns out that ELF doesn't like this either, it wants something like "for Best Picture"
or "where Best Picture" etc. And if we add the "for" right into the substitution, then we've
got a problem with the original question, which then comes out
who was OscarWinners_ . ArtistName for for Best Picture in 1954? -- the
"for for" stops ELF cold. There's nothing to do but add another rule to handle this case:
When I Type: "won {Category}"
I Really Mean: was OscarWinners_ . ArtistName for {1}
This Phrase definition uses the new "data-aware" feature of the Phrase facility. (For complete details on this, see the Phrase Usage topic.) This allows us to specify a field name as part of the input pattern -- here, the Category field. On the substitution side, whatever fills this slot in the actual question will be substituted where the {1} marker appears. So in this case, the rule changes who won Best Picture in 1954? into who was OscarWinners_ . ArtistName for Best Picture in 1954?
This refinement is a special feature of the context-sensitive phrase facility, which is triggered by using quotes (or square brackets) in the When I Type: part of the definition. It wasn't strictly necessary to use the quotes in the first case (since that one didn't use context-sensitivity at all). But if we make this second definition, we do need to use quotes in the first one as well. The reason is that context-sensitive substitution has a lower priority than general-purpose substitutions. If they both use the same word (eg. "won"), then the context-sensitive rule (which fires first) will defer to the general-purpose rule (which fires later). By default, the two types of rules fire in alphabetical order on the trigger part of the definition; if you need to override this firing order, you can enter arbitrary ordering values in the Sort Key field.
Last Updated: August, 2009