Search Engine and Indexer.Searching

Jan 27, 2012 at 6:36 AM

I see code related to searching in two projects now in the solution. Search Engine.proj and Indexer.Proj contains search related code. I added files related to searching in Search Engine. Proj and lordlohtar has checked in Search related files in Indexer.Proj.

Jan 27, 2012 at 6:37 AM

I am not sure whether both are intended for the same reason or not. Hence creating a discussion/

Coordinator
Jan 27, 2012 at 7:20 AM

Let me answer your question - in the discussion "Lucene.Net usage" we have decided that it would be better for the project if only Indexer depends on the Lucene libraries, so that if we find a better way to store data describing the code from the solution, we can easily switch to other library, with the changes to be made only in one project.

I proposed a solution, where I try to create an API for the SearchEngine module and see if we can get rid of the Lucene dependencies in your project. I've commited the first version of this API - these are the files you mentioned. The idea behind this is that you collect data from the UI, create the expression tree for complex queries (where AND OR, NOT operators will be used), using SimpleSearchCriteria.

Let me give you an example:

User wants to search for public classes with the name that contains word "Manager" or private methods with the name that contains word "Manage" - the search engine collects data from UI, creates tho SimpleSearchCriteria - one for the classes condition and the second for the methods, creates an OrSearchCriteria with these two SimpleSearchCriteria objects and calls Search on the Indexer, providing OrSearchCriteria object as an argument. Then, inside the Indexer module, Lucene specific query is created, run and the results are sent back to the SearchEngine.

SearchEngine probably parses the results, returns them to the UI, makes some caching stuff etc.

 

This way SearchEngine doesn't know anything about the indexer library and can be used with any other underlying module, without any changes.

 

Dave - please correct me if I said something wrong - lets finish this discussions so that we all have clear understanding of the functionality of every part of the project

Coordinator
Jan 27, 2012 at 1:18 PM

First, let me apologize to hanuman... I should have understood the implications of having only the Indexer depend on Lucene and updated him on the impact upon the Search Engine.  Sorry for the oversight!

So, as lordlothar mentions, we all agreed that only the Indexer should depend on Lucene.  Thus, the SearchEngine will need to eliminate its dependency on Lucene.  The SearchEngine should now use the API surfaced by the Indexer to construct its searches.  

The SearchEngine will now be responsible for things like:

  • Given a search string (e.g., "file buffer open") create a corresponding instance of SearchCriteria that searches the correct fields, etc.
  • Creating an API that the UI can use to easily search
    • For example, the UI would like to send the SearchEngine something as simple as a search string and expect the SearchEngine to set up all of the correct default settings to search and return the results
    • For example, the UI would like to have an easy-to-use API to tweak the search settings with convenience methods like "OnlySearchMethods" or similar.
  • Creating an API that provides suggestions given a search string (e.g., if given the search string "file open" then maybe the SearchEngine could do a quick search and realize that the term "buffer" is also used a lot in the results, and thus it could suggest adding "buffer").

Please let me know if I've forgotten anything.  Hanuman, again, sorry for the rescoping of this component and for wasting a few of your commits but I think in the long run this split will be better.  If it makes you feel better I threw away two days worth of coding last night ;) (and then committed only the best ideas from that session).  

Coordinator
Jan 27, 2012 at 1:23 PM
lordlothar wrote:

Let me give you an example:

User wants to search for public classes with the name that contains word "Manager" or private methods with the name that contains word "Manage" - the search engine collects data from UI, creates tho SimpleSearchCriteria - one for the classes condition and the second for the methods, creates an OrSearchCriteria with these two SimpleSearchCriteria objects and calls Search on the Indexer, providing OrSearchCriteria object as an argument. Then, inside the Indexer module, Lucene specific query is created, run and the results are sent back to the SearchEngine.

SearchEngine probably parses the results, returns them to the UI, makes some caching stuff etc.

I agree with this example.  Let me clarify the last sentence though...

SearchEngine probably parses the results, returns them to the UI, makes some caching stuff etc.

The SearchEngine might not actually parse the results (as the Indexer's API returns ProgramElements) but it can wrap those results in its own classes (i.e, CodeSearchResult) where it can add additional metadata doesn't make sense to add to ProgramElements, such as a relevance score against the search.  It could also potentially lookup and provide other information, such as actual code snippets surrounding the returned element if we decide that's important.  Hope this makes sense.... 

Coordinator
Jan 27, 2012 at 1:52 PM
davidcshepherd wrote:
I agree with this example.  Let me clarify the last sentence though...

SearchEngine probably parses the results, returns them to the UI, makes some caching stuff etc.

The SearchEngine might not actually parse the results (as the Indexer's API returns ProgramElements) but it can wrap those results in its own classes (i.e, CodeSearchResult) where it can add additional metadata doesn't make sense to add to ProgramElements, such as a relevance score against the search.  It could also potentially lookup and provide other information, such as actual code snippets surrounding the returned element if we decide that's important.  Hope this makes sense....

Yes - that's what I meant by parsing - I used a wrong word - sorry for that, but all I was thinking about was additional data that is not useful (needed) for the Indexer. Indexer will definitely return program elements.

Jan 28, 2012 at 4:39 AM

 I am happy that i got the clarity henceforth can think on the directions for Search Engine API :).

Jan 30, 2012 at 11:47 PM
lordlothar wrote:SearchEngine probably parses the results, returns them to the UI, makes some caching stuff etc.

 

This way SearchEngine doesn't know anything about the indexer library and can be used with any other underlying module, without any changes.

 

Dave - please correct me if I said something wrong - lets finish this discussions so that we all have clear understanding of the functionality of every part of the project

Dave/lordlothar, Can you provide me a use case scenario when you mean about caching. I am not very clear on this one.

Coordinator
Jan 31, 2012 at 7:54 AM

I believe by caching we meant storing results for some searches in the memory, so that when the search is made again (we need to check probably if anything has changed since last run) - you can return results from the cache, without calling the Indexer search method.

I will have to add some comparison methods for the search criteria classes, which you will use to check if you have the same query cached.

You need to think also how much memory you can use for that (plugin setting?), how will you update the results, which one will you delete as first when memory usage gets too high (LRU?) etc.

The whole process can look like that:

  1. User searches for public methods with one condition: name contains word "Provide".
  2. Search Engine creates the appropriate SearchCriteria object.
  3. Search Engine calls Indexer search method.
  4. Indexer runs the query and returns the results.
  5. Search Engine saves the results and the SearchCriteria object in the Dictionary<SearchCriteria, List<<Tuple<ProgramElement, double>>>.
  6. Search Engine returns the results to the UI.
  7. User runs search again.
  8. Search Engine checks if anything has changed in the code since last caching (parser can have public flag "ParserUsed", which will be set when parser has to parse new files and reset by the SearchEngine while adding elements to the cache)
  •  
    • If no changes have been made, Search Engine checks if the results are in the cache (dictionary.Contains(searchCriteriaObj)) - if they are - Search Engine returns results without calling the Indexer
    • If parser was called (so that we can have changes in the indexed Lucene documents) or if there are no results for the given Search Criteria in the dictionary, process continue from step 3

I think in the future we can improve the checking for changes (for example parser can remember which files have been changed, so that when there are results for other files, Search Engine can use them again), but probably the process I've described could be a good start point.

 

Dave - please, correct any wrong assumptions I made

Coordinator
Jan 31, 2012 at 1:21 PM

> Dave/lordlothar, Can you provide me a use case scenario when you mean about caching. I am not very clear on this one.

Just a note to keep us on track, I think that parsing the search string into SearchCriteria is a much higher priority than implementing caching.  Caching is a definite nice-to-have but parsing the search string into SearchCriteria is a must-have.

 

--

The process lordlothar described above is pretty much what I had in mind.  Just a few small comments:

> Search Engine saves the results and the SearchCriteria object in the Dictionary<SearchCriteria, List<<Tuple<ProgramElement, double>>>.

The Search Engine (as it is now) returns results to its clients as List<CodeSearchResult>.  It is responsible for changing the List<Tuple<ProgramElement, double>> into a List<CodeSearchResult>.  We can drop the CodeSearchResult class and just use the Tuple if you guys want to... I just wanted to let you know how it was implemented now.

You need to think also how much memory you can use for that (plugin setting?),

I agree this should be a plugin setting and I think it should be kinda low by default.  The last thing we want Sando to be known for is for being a memory hog ;)

 


Feb 6, 2012 at 5:57 AM

Hello, Everyone,

I have added a LRUCache (Least Recently Used) class into the SearchEngine project. Some concerns need to be fixed in order to perform it correctly.

(1). Need the comparison methods for the SearchCriteria class, which is used as the key in the dictionary.

(2). The default capacity of the Cache is 50 right now, which might be changed in future in order to reflect the memory requirement of our plugin.

(3). As lordlothar indicated in 8, "Search Engine checks if anything has changed in the code since last caching (parser can have public flag "ParserUsed", which will be set when parser has to parse new files and reset by the SearchEngine while adding elements to the cache)", is there such a variable existing now?

Please correct any of the above assumptions or let me know if there are already APIs to fix them. Thank you.

huangxin0921

Coordinator
Feb 7, 2012 at 1:05 AM

Hi Xin!

Thanks for contributing this!  It's great to have another committer.  I'll take a look at it sometime tomorrow to give you any feedback I might have...

Coordinator
Feb 7, 2012 at 7:16 AM
huangxin0921 wrote:

Hello, Everyone,

I have added a LRUCache (Least Recently Used) class into the SearchEngine project. Some concerns need to be fixed in order to perform it correctly.

(1). Need the comparison methods for the SearchCriteria class, which is used as the key in the dictionary.

(2). The default capacity of the Cache is 50 right now, which might be changed in future in order to reflect the memory requirement of our plugin.

(3). As lordlothar indicated in 8, "Search Engine checks if anything has changed in the code since last caching (parser can have public flag "ParserUsed", which will be set when parser has to parse new files and reset by the SearchEngine while adding elements to the cache)", is there such a variable existing now?

Please correct any of the above assumptions or let me know if there are already APIs to fix them. Thank you.

huangxin0921

Hi,

Sorry for so late reply - I've been having a busy time recently.

Ad 1) I'll try to add the comparison method today and I'll let you know when it's done

Ad 2) I agree and it will be probably better if we allow user to change this setting using number of cached elements rather than memory size

Ad 3) As I remember there is a proposition in other thread to make this flag part of the Indexer module, which is in fact probably a better solution, because it is the Indexer module, which updates the stored elements - I'll also try to add this one today, so that you can continue your tasks

Coordinator
Feb 7, 2012 at 7:43 PM
Edited Feb 7, 2012 at 7:45 PM
lordlothar wrote:

Hi,

Sorry for so late reply - I've been having a busy time recently.

Ad 1) I'll try to add the comparison method today and I'll let you know when it's done

Ad 2) I agree and it will be probably better if we allow user to change this setting using number of cached elements rather than memory size

Ad 3) As I remember there is a proposition in other thread to make this flag part of the Indexer module, which is in fact probably a better solution, because it is the Indexer module, which updates the stored elements - I'll also try to add this one today, so that you can continue your tasks

Ad. 1. - I've overridden Object.Equals and Object.GetHashCode methods for the comparison purposes

Ad. 3. - I've created the LastUpdatedAt (DateTime) property in the DocumentIndexer class, which is being set to current date-time whenever the Indexer.CommitChanges() method is called - you should probably store the copy of it in your class instance and check if the Indexer's value has changed before you use the cached data

Feb 7, 2012 at 9:06 PM
Thank you for the implementations. I will change the caching code accordingly.

Xin

On Tue, Feb 7, 2012 at 3:43 PM, [email removed] wrote:
> From: lordlothar
>
> lordlothar wrote:
>
> Hi,
>
> Sorry for so late reply - I've been having a busy time recently.
>
> Ad 1) I'll try to add the comparison method today and I'll let you know when
> it's done
>
> Ad 2) I agree and it will be probably better if we allow user to change this
> setting using number of cached elements rather than memory size
>
> Ad 3) As I remember there is a proposition in other thread to make this flag
> part of the Indexer module, which is in fact probably a better solution,
> because it is the Indexer module, which updates the stored elements - I'll
> also try to add this one today, so that you can continue your tasks
>
> Ad. 1. - I've implemented an Override of the Object.Equals and
> Object.GetHashCode methods for the comparison purposes
>
> Ad. 3. - I've created the LastUpdatedAt (DateTime) property in the
> DocumentIndexer class, which is being set to current date-time whenever the
> Indexer.CommitChanges() method is called - you should probably store the
> copy of it in your class instance and check if the Indexer's value has
> changed before you use the cached data
>
> Read the full discussion online.
>
> To add a post to this discussion, reply to this email
> ([email removed])
>
> To start a new discussion for this project, email
> [email removed]
>
> You are receiving this email because you subscribed to this discussion on
> CodePlex. You can unsubscribe or change your settings on codePlex.com.
>
> Please note: Images and attachments will be removed from emails. Any posts
> to this discussion will also be available online at codeplex.com
Coordinator
Feb 8, 2012 at 1:10 AM

Hi Xin, I took a quick look and it looked great!  I made some superficial changes (i.e., put -> Put) and checked them in.  Keep up the great contributions!

Feb 11, 2012 at 7:32 PM
lordlothar wrote:
lordlothar wrote:

Hi,

Sorry for so late reply - I've been having a busy time recently.

Ad 1) I'll try to add the comparison method today and I'll let you know when it's done

Ad 2) I agree and it will be probably better if we allow user to change this setting using number of cached elements rather than memory size

Ad 3) As I remember there is a proposition in other thread to make this flag part of the Indexer module, which is in fact probably a better solution, because it is the Indexer module, which updates the stored elements - I'll also try to add this one today, so that you can continue your tasks

Ad. 1. - I've overridden Object.Equals and Object.GetHashCode methods for the comparison purposes

Ad. 3. - I've created the LastUpdatedAt (DateTime) property in the DocumentIndexer class, which is being set to current date-time whenever the Indexer.CommitChanges() method is called - you should probably store the copy of it in your class instance and check if the Indexer's value has changed before you use the cached data

I am trying to update the caching code with the provided LastUpdatedAt variable of the DocumentIndexer class. It seems I can not get this variable within the CodeSearcher class. I notice there is _currentIndexer member in SolutionMonitor and I might get LastUpdatedAt from there. I also find that a new CodeSearcher is constructed every time when SearchButtonClick() is called in SearchViewControl.xaml.cs. Is it possible we make the CodeSearcher a member of a certain class (e.g., in SolutionMonitor) so that it might be more efficient and we can access _currentIndexer?

David, please correct me if I misunderstood or missed some methods I can use. Thank you.

Coordinator
Feb 11, 2012 at 8:26 PM
Edited Feb 11, 2012 at 8:27 PM
huangxin0921 wrote:
I am trying to update the caching code with the provided LastUpdatedAt variable of the DocumentIndexer class. It seems I can not get this variable within the CodeSearcher class. I notice there is _currentIndexer member in SolutionMonitor and I might get LastUpdatedAt from there. I also find that a new CodeSearcher is constructed every time when SearchButtonClick() is called in SearchViewControl.xaml.cs. Is it possible we make the CodeSearcher a member of a certain class (e.g., in SolutionMonitor) so that it might be more efficient and we can access _currentIndexer?

David, please correct me if I misunderstood or missed some methods I can use. Thank you.

Good question - the fact that CodeSearcher is created per search also means that the IndexerSearcher is created per search.

What about creating a new class (something like SearchCache), creating an instance of this class in the IndexerSearcher class, changing IndexerSearcherFactory so that it doesn't create a new IndexerSearcher every click, but uses static dictionary (see DocumentIndexerFactory) to reuse objects?

CodeSearcher would be then responsible for data conversion between UI search objects and Indexer SearchCriteria and could be recreated every click so that it doesn't consume memory when user is not using search functionality (IndexerSearcherFactory will remember the IndexerSearcher object). Additionally, the IndexerSearcher class uses DocumentIndexer for getting Lucene searcher, so that the problem to check for cache validation would be automatically solved.

If we leave the cache where it is now or if we make CodeSearcher persistent across searches, we will have to store it in the memory and also store the IndexerSearcher object.

Waiting for your comments

Coordinator
Feb 13, 2012 at 5:21 PM

> Good question - the fact that CodeSearcher is created per search also means that the IndexerSearcher is created per search.

The fact that I'm creating a CodeSearcher per search seems like it may be the root of this issue.  I'm now reusing the CodeSearcher until the project changes.  This allows per-project searches to be cached.  I've just committed this code, check out the updated SearchViewControl.xaml.cs (at the bottom) and see if this update is sufficient.  As lordlothar says, we may want to move this caching elsewhere in the future, but this feels like it is sufficient for now.  Let me know if you disagree and we could look at moving it elsewhere.

Coordinator
Feb 13, 2012 at 6:57 PM
davidcshepherd wrote:

> Good question - the fact that CodeSearcher is created per search also means that the IndexerSearcher is created per search.

The fact that I'm creating a CodeSearcher per search seems like it may be the root of this issue.  I'm now reusing the CodeSearcher until the project changes.  This allows per-project searches to be cached.  I've just committed this code, check out the updated SearchViewControl.xaml.cs (at the bottom) and see if this update is sufficient.  As lordlothar says, we may want to move this caching elsewhere in the future, but this feels like it is sufficient for now.  Let me know if you disagree and we could look at moving it elsewhere.

Did you mean per project or per solution, because I'm a little confused right now?

Coordinator
Feb 13, 2012 at 7:37 PM

I meant per solution, ohhps!

Coordinator
Feb 13, 2012 at 7:44 PM
davidcshepherd wrote:

I meant per solution, ohhps!

Ok :)

But - creating a CodeSearcher per solution doesn't solve the problem with the access to DocumentIndexer LastUpdatedAt property - Dave - can huangxin somehow access the instance of DocumentIndexer? I think it's rather a bad idea if IndexerSearcher provide such an access, because it is not its responsibility to make this value part of its interface - or maybe I'm wrong? What do you think?

Coordinator
Feb 13, 2012 at 8:00 PM
lordlothar wrote:
davidcshepherd wrote:

I meant per solution, ohhps!

Ok :)

But - creating a CodeSearcher per solution doesn't solve the problem with the access to DocumentIndexer LastUpdatedAt property - Dave - can huangxin somehow access the instance of DocumentIndexer? I think it's rather a bad idea if IndexerSearcher provide such an access, because it is not its responsibility to make this value part of its interface - or maybe I'm wrong? What do you think?

I agree with you that the IndexerSearcher should NOT provide access to the DocumentIndexer or even the LastUpdateAt property... these should be private to the Indexer project.  However, I don't want to move any of the CodeSearcher stuff to the Indexer project because I really think it belongs in the Search Engine project.  

How about this.... should the DocumentIndexer implement an INotifer (or something similar) so that we could do something like:

DocumentIndexer.AddUpdateListener(CodeSearcher);

Then when the DocumentIndexer is updated it can call Notify on all its listeners (i.e., the CodeSearcher).  The CodeSearcher can then handle this Notify by clearing its cache.  

Does this make sense?  If not, any ideas for alternatives that keep the searching and caching functionality in the Search Engine? Let me know what you think....   

Coordinator
Feb 13, 2012 at 8:27 PM
davidcshepherd wrote:
However, I don't want to move any of the CodeSearcher stuff to the Indexer project because I really think it belongs in the Search Engine project. 

How about this.... should the DocumentIndexer implement an INotifer (or something similar) so that we could do something like:

DocumentIndexer.AddUpdateListener(CodeSearcher);

Then when the DocumentIndexer is updated it can call Notify on all its listeners (i.e., the CodeSearcher).  The CodeSearcher can then handle this Notify by clearing its cache.

I think it's a very good idea, with one additional note - if it's a static method it should be:
DocumentIndexer.AddUpdateListener(CodeSearcher, string luceneDirectoryPath);

There is only one thing that is frightening me - we are using lucene directory as a key in many places - in example for the DocumentIndexer creation - I've started to wonder recently if it's a smart solution - I would say that it would be much safer if we create some kind of a key class that will be created per solution and that will be used for factories etc. It can have Lucene directory path hidden inside, but if we find out that there should be an additional attribute describing the key, we will have to change one class implementation instead of changing many places in many classes.

This key class can be named something like SolutionKey and have one method string GetKeyRepresentation() which can return now Lucene directory, but in the future can easily be changed into more complex key. We can than use this class whenever we need to get solution-singleton class like DocumentIndexer, notifier etc.

Does it all seem like something we need to do or maybe they are just my exaggerated fears?

Coordinator
Feb 14, 2012 at 1:07 PM

> I think it's a very good idea, with one additional note - if it's a static method it should be: DocumentIndexer.AddUpdateListener(CodeSearcher, string luceneDirectoryPath);

I was thinking it should not be a static method.  It should be an instance method (i.e., add this codesearcher instance as a listener to this documentIndexer instance).

There is only one thing that is frightening me - we are using lucene directory as a key in many places...

You bring up a good point.  I'd be happy if there was a SolutionKey class that contained this key information.  It could have the GetKeyRepresentation() as you mention and maybe also a GetSolutionPath() method.  The GetKeyRepresentation() can (for now) simply return the path, but as you mention that may change in the future.  

This seems like a good idea to me, thanks for bringing it up.

Coordinator
Feb 14, 2012 at 1:18 PM

>> I think it's a very good idea, with one additional note - if it's a static method it should be: DocumentIndexer.AddUpdateListener(CodeSearcher, string luceneDirectoryPath);

> I was thinking it should not be a static method.  It should be an instance method (i.e., add this codesearcher instance as a listener to this documentIndexer instance).

 

Is there any place in application when CodeSearcher and DocumentIndexer exists together? In SearchViewControl.xaml.cs? I mean so that the AddUpdateListener method is called only once.

Anyway - I will provide the interface for listeners inside the DocumentIndexer .

 

>> There is only one thing that is frightening me - we are using lucene directory as a key in many places...

>You bring up a good point.  I'd be happy if there was a SolutionKey class that contained this key information.  It could have the GetKeyRepresentation() as you mention and maybe also a GetSolutionPath() method.  The GetKeyRepresentation() can (for now) simply return the path, but as you mention that may change in the future.  

This seems like a good idea to me, thanks for bringing it up.

 

I'll add this class and try to update all the related calls.

 

I will also add the issues for those two changes.

Coordinator
Feb 15, 2012 at 6:47 PM

I've added the IIndexUpdateListener interface and the corresponding methods to add and remove listeners from the DocumentIndexer class - huangxin, please extend CodeSearcher and use this functionality to enable index updates notifications - Dave, please choose the right place in the application, where CodeSearcher can be added as a listener to the DocumentIndexer instance

Coordinator
Feb 15, 2012 at 9:02 PM

I've added the SolutionKey class - please - look at the implementation and let me know if this is what you wanted and what you described in your last post.

If it's ok I will update the places in the code where the instance of this class should be used

Coordinator
Feb 16, 2012 at 2:37 PM

I just took a look at the SolutionKey class and it looks good to me.  Feel free to update the places in the code where this should be used.

Coordinator
Feb 16, 2012 at 6:02 PM
Edited Feb 16, 2012 at 6:33 PM
davidcshepherd wrote:

I just took a look at the SolutionKey class and it looks good to me.  Feel free to update the places in the code where this should be used.

I have one problem - SolutionKey class is a key for factories and all solution-singleton objects. However, for the objects which will call this factories I need a possibility to get an instance of this class for the current solution.

We were talking about the possibility to configure the path for the Lucene index directory, which means that we need to store this setting in a configuration file. It also means that we can store the solution path (and maybe other informations like the solution id - set at the first time solution is opened, index related informations etc) in this file, so that when our plugin is initialized, it can read the basic information about all the solutions and create SolutionKey objects for them.

Dave - here's a question for you - is it possible that the part of our plugin which is related to the solution events (like solution opened etc) can provide a static way to get current SolutionKey object? I believe this part can access the solution metadata like path, which can be used to get the Lucene directory and the rest informations from the config file.

I mean - in my opinion the whole process can look like this:

  1. VS is opened
  2. Plugin is initialized - informations about all the solutions are read from the plugin configuration file 
  3. Dictionary<Guid,SolutionKey> is created - solution id is used as a key   
  4. Solution is opened (the first 3 points can be called here if the plugin is not initialized when VS is opened)
    If this is the first time the solution is opened (no info in config file) - new SolutionKey object with new Guid is created (and saved to the config file)
  5. Plugin remembers solution id, based on which it can return SolutionKey object for the current solution 
  6. Object that want to call factories or create solution related objects can call the plugin method to get the SolutionKey instance
  7. Factories can use the solution id (GUID) if they have their own dictionaries of created elements 

Is this scenario ok? Did I miss something? Can I use GUID (solution id) as a key for factories which have their own dictionaries of created elements, like DocumentIndexerFactory?

  in this file
Coordinator
Feb 17, 2012 at 1:12 PM

> Dave - here's a question for you - is it possible that the part of our plugin which is related to the solution events (like solution opened etc) can provide a static way to get current SolutionKey object? I believe this part can access the solution metadata like path, which can be used to get the Lucene directory and the rest informations from the config file.

Yes, it is no problem for the UI plugin to provide a "GetCurrentSolutionKey" method.  I have implemented a skeleton for this and checked it in.  See if it matches your needs.  Feel free to make comments here and/or add TODO tags in the code.  The way to access the current solution key is:

UIPackage.GetInstance().GetCurrentSolutionKey();

It currently only will return the CURRENTLY OPEN Solution's key.  It does not cache keys, or remember keys across VS sessions.  We can add this later if you feel it is necessary.  

Please note that I realize the UIPackage.GetInstance() and GetCurrentSoltuionKey() are both not totally threadsafe yet, but I'll fix that soon.  

FYI, the plugin is initialized on VS startup, but no solutions are known until they are actually opened.   

Coordinator
Feb 17, 2012 at 1:27 PM

Perfect!

I think at some point in the future we will have to save some informations about the solution in the configuration file and read it while plugin initialization - i.e. the state of the index can be important so that we know, if it's up to date or if we should parse the files and recreate the Lucene documents (bwt - do we have an access to the file modification date? If we store this date in Lucene index, having information about the file for every program element, we can easily check if the file changed and update only those documents for which this date is different than the stored one - it can be quite a big performance improvement in comparison to delete all/recreate all method we are using now).

I've a got a family weekend so I'll be probably back on Monday - I'll let you know if I have any comments on your change then.

 

Coordinator
Feb 19, 2012 at 5:51 PM

I've committed SolutionKey class related changes - guys please - look at them and tell me what do you think.

Dave - two informations for you - I had to move the SolutionKey object creation to the factory, just before the line where the DocumentIndexer is created, because I need an instance of this class for the DocumentIndexerFactory. I've also added the Guid solutionId field to the SolutionKey class, because I think the lucene index path should not be an official key, even if it's unique among the SolutionKey objects - please - feel free to add any comment on that