I am using Worksite API to query documents in iManage (version 8.5). I've listed my code below. If I only use one search parameter then the code works without any problem. However, if I add more than one parameter then it returns either null or no result (result.Count = 0)
Then I changed my code to use the ManOrQuery class (provided by my Worksite API, please see the commented lines) and that still doesn't work.
// Search for documents matching the specified date range.
iManageSearch rds = new iManageSearch(isession);
// Populate searchparameters
IManProfileSearchParameters searchparams = Utility.CreateUnpopulatedProfileParams(idms);
//searchparams.Add(imProfileAttributeID.imProfileCreateDate, dateRange.Value);
//searchparams.Add(imProfileAttributeID.imProfileAuthor, srchKey);
//searchparams.Add(imProfileAttributeID.imProfileFullText, srchKey);
searchparams.Add(imProfileAttributeID.imProfileDocNum, srchKey);
//searchparams.Add(imProfileAttributeID.imProfileDescription, srchKey);
// Search documents
IManDocuments results = rds.GetDocuments(Utility.BuildDatabaseList(isession.Databases), searchparams);
// tried the other way to search document
//QueryBuilder qb = new QueryBuilder();
//ManOrQuery orquery = qb.CreateORQuery;
//qb.AddORSearchFieldValue(orquery, imProfileAttributeID.imProfileDocNum, srchKey);
//qb.AddORSearchFieldValue(orquery, imProfileAttributeID.imProfileAuthor, srchKey);
//qb.AddORSearchFieldValue(orquery, imProfileAttributeID.imProfileFullText, srchKey);
//IManContents results = qb.GetContents(iworkarea, Utility.BuildDatabaseList(isession.Databases), (IManQuery)orquery);
int c = results.Count;
on my UI, I've a textbox for users to enter their search credential. And I would like to compare the search value with Author, DocNumber, DocTitle and also the content of documents. My goal is to build a query like (docAuthor=srchKey OR docNum=srchKey OR docDescription = srchKey ...). I've been banging my head, hope anyone can help me. Thank you.
PS: I also referred to a post here How to get information out of iManage / Desksite, but that doesn't work for me....
I know its been a little while since this question was posted, and I have done some searching around stackoverflow and not been able to find much to help me on this problem, however I have managed to write some code that works (for me at least) and I hope if its too late to help you, it might help someone else.
I cant see how you set up the database in the code above, so there may be a problem there - as the syntax for adding your search parameters appears to be correct.
Update:
I have spoken to our administrators, and it appears that to do searching, it depends on the indexer settings of the server. This is potentially why your code was not working. For me I had to disable the indexer from the database properties in the WorkSite Service manger, so that it would use SQL
IManDMS dms = (IManDMS)Activator.CreateInstance(Type.GetTypeFromProgID("iManage.ManDMS"));
IManSession session = dms.Sessions.Add(serverName);
session.TrustedLogin2(userToken);
IManDatabase database = session.Databases.ItemByName(libraryName);
IManProfileSearchParameters searchparameters = dms.CreateProfileSearchParameters();
// add search parameters
// this works (just to prove that we can search for a document)
searchparameters.Add(imProfileAttributeID.imProfileDocNum, "4882408");
searchparameters.Add(imProfileAttributeID.imProfileCreateDate, new DateTime(2015, 04, 8).ToShortDateString());
// run the search
IManContents searchResults = database.SearchDocuments(searchparameters, true);
// process the results
foreach (IManDocument item in ((IEnumerable)searchResults).OfType<IManDocument>())
{
// do something with the document
}
session.Logout();
dms.Sessions.RemoveByObject(session);
Related
I am using the following code to retrieve emails whose subject contains "MS" and "QW". I see more than 8 emails satisfying the search criteria, but the code returns only two emails. Can anyone help me take a look at what the problem is in here?
var filter1 = new SearchFilter.ContainsSubstring(ItemSchema.Subject, "MS", ContainmentMode.Substring, ComparisonMode.IgnoreCase);
var filter2 = new SearchFilter.ContainsSubstring(ItemSchema.Subject, "QW");
var sf = new SearchFilter.SearchFilterCollection(LogicalOperator.And, filter1, filter2);
var findResults = service.FindItems(WellKnownFolderName.Inbox, sf view);
A few things I can see if with the Second search filter you haven't specified the ContainmentMode or ComparsisonMode. You also don't seem to have added them to the SearchFilter Collection. eg you should have
sf.Add(filter1);
sf.Add(filter2);
That Search will yield pretty poor performance on a folder with a large number I would suggest you maybe look at AQS then you can do
service.FindItems(WellKnownFolderName.Inbox, "Subject:MS AND Subject:QW" ,view);
That will search against the Content Indexes and yield better performance.
How does one use Firebase to do basic auto-completion/text preview?
For example, imagine a blog backed by Firebase where the blogger can tag posts with tags. As the blogger is tagging a new post, it would be helpful if they could see all currently-existing tags that matched the first few keystrokes they've entered. So if "blog," "black," "blazing saddles," and "bulldogs" were tags, if the user types "bl" they get the first three but not "bulldogs."
My initial thought was that we could set the tag with the priority of the tag, and use startAt, such that our query would look something like:
fb.child('tags').startAt('bl').limitToFirst(5).once('value', function(snap) {
console.log(snap.val())
});
But this would also return "bulldog" as one of the results (not the end of the world, but not the best either). Using startAt('bl').endAt('bl') returns no results. Is there another way to accomplish this?
(I know that one option is that this is something we could use a search server, like ElasticSearch, for -- see https://www.firebase.com/blog/2014-01-02-queries-part-two.html -- but I'd love to keep as much in Firebase as possible.)
Edit
As Kato suggested, here's a concrete example. We have 20,000 users, with their names stored as such:
/users/$userId/name
Oftentimes, users will be looking up another user by name. As a user is looking up their buddy, we'd like a drop-down to populate a list of users whose names start with the letters that the searcher has inputted. So if I typed in "Ja" I would expect to see "Jake Heller," "jake gyllenhaal," "Jack Donaghy," etc. in the drop-down.
I know this is an old topic, but it's still relevant. Based on Neil's answer above, you more easily search doing the following:
fb.child('tags').startAt(queryString).endAt(queryString + '\uf8ff').limit(5)
See Firebase Retrieving Data.
The \uf8ff character used in the query above is a very high code point
in the Unicode range. Because it is after most regular characters in
Unicode, the query matches all values that start with queryString.
As inspired by Kato's comments -- one way to approach this problem is to set the priority to the field you want to search on for your autocomplete and use startAt(), limit(), and client-side filtering to return only the results that you want. You'll want to make sure that the priority and the search term is lower-cased, since Firebase is case-sensitive.
This is a crude example to demonstrate this using the Users example I laid out in the question:
For a search for "ja", assuming all users have their priority set to the lowercased version of the user's name:
fb.child('users').
startAt('ja'). // The user-inputted search
limitToFirst(20).
once('value', function(snap) {
for(key in snap.val()){
if(snap.val()[key].indexOf('ja') === 0) {
console.log(snap.val()[key];
}
}
});
This should only return the names that actually begin with "ja" (even if Firebase actually returns names alphabetically after "ja").
I choose to use limitToFirst(20) to keep the response size small and because, realistically, you'll never need more than 20 for the autocomplete drop-down. There are probably better ways to do the filtering, but this should at least demonstrate the concept.
Hope this helps someone! And it's quite possible the Firebase guys have a better answer.
(Note that this is very limited -- if someone searches for the last name, it won't return what they're looking for. Hence the "best" answer is probably to use a search backend with something like Kato's Flashlight.)
It strikes me that there's a much simpler and more elegant way of achieving this than client side filtering or hacking Elastic.
By converting the search key into its' Unicode value and storing that as the priority, you can search by startAt() and endAt() by incrementing the value by one.
var start = "ABA";
var pad = "AAAAAAAAAA";
start += pad.substring(0, pad.length - start.length);
var blob = new Blob([start]);
var reader = new FileReader();
reader.onload = function(e) {
var typedArray = new Uint8Array(e.target.result);
var array = Array.prototype.slice.call(typedArray);
var priority = parseInt(array.join(""));
console.log("Priority of", start, "is:", priority);
}
reader.readAsArrayBuffer(blob);
You can then limit your search priority to the key "ABB" by incrementing the last charCode by one and doing the same conversion:
var limit = String.fromCharCode(start.charCodeAt(start.length -1) +1);
limit = start.substring(0, start.length -1) +limit;
"ABA..." to "ABB..." ends up with priorities of:
Start: 65666565656565650000
End: 65666665656565650000
Simples!
Based on Jake and Matt's answer, updated version for sdk 3.1. '.limit' no longer works:
firebaseDb.ref('users')
.orderByChild('name')
.startAt(query)
.endAt(`${query}\uf8ff`)
.limitToFirst(5)
.on('child_added', (child) => {
console.log(
{
id: child.key,
name: child.val().name
}
)
})
In Lucene, we can use TermQuery to search a text with a field. I am wondering how to search a keyword across a bunch of fields or all the searchable fields?
Another approach, which doesn't require to index anything more than what you already have, nor to combine different queries, is using the MultiFieldQueryParser.
You can provide a list of fields where you want to search on and your query, that's all.
MultiFieldQueryParser queryParser = new MultiFieldQueryParser(
Version.LUCENE_41,
new String[]{"title", "content", "description"},
new StandardAnalyzer(Version.LUCENE_41));
Query query = queryParser.parse("here goes your query");
This is how I would do it with the original lucene library written in Java. I'm not sure whether the MultiFieldQueryParser is available in lucene.net too.
Two approaches
1) Index-time approach: Use a catch-all field. This is nothing but appending all the text from all the fields (total text from your input doc) and place that resulting huge text in a single field. You've to add an additional field while indexing to act as a catch-all field.
2) Search-time approach: Use a BooleanQuery to combine multiple queries, for example TermQuery instances. Those multiple queries can be formed to cover all the target fields.
Example check at the end of the article.
Use approach 2 if you know the target-field list at runtime. Otherwise, you've got to use the 1st approach.
Another easy approach to search across all fields using "MultifieldQueryParser" is use IndexReader.FieldOption.ALL in your query.
Here is example in c#.
Directory directory = FSDirectory.Open(new DirectoryInfo(HostingEnvironment.MapPath(VirtualIndexPath)));
//get analyzer
Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_29);
//get index reader and searcher
IndexReader indexReader__1 = IndexReader.Open(directory, true);
Searcher indexSearch = new IndexSearcher(indexReader__1);
//add all possible fileds in multifieldqueryparser using indexreader getFieldNames method
var queryParser = new MultiFieldQueryParser(Version.LUCENE_29, indexReader__1.GetFieldNames(IndexReader.FieldOption.ALL).ToArray(), analyzer);
var query = queryParser.Parse(Criteria);
TopDocs resultDocs = null;
//perform search
resultDocs = indexSearch.Search(query, indexReader__1.MaxDoc());
var hits = resultDocs.scoreDocs;
click here to check out my pervious answer to same quesiton in vb.net
According to the SSO documentation for IA these attributes should be available (I'm guessing a bit at the attributes URI):
First Name (http://axschema.org/namePerson/first)
Last Name (http://axschema.org/namePerson/last)
Realm Id (http://axschema.org/intuit/realmId)
Reviewing the query string passed during stage 3 of the open id request, here are the attributes present:
openid.alias3.type.alias1 => http://axschema.org/namePerson
openid.alias3.value.alias1 => Full Name
openid.alias3.type.alias2 => http://axschema.org/contact/email
openid.alias3.value.alias2 => email#test.com
Bug, error in the documentation, or loose nut behind the keyboard?
Two problems here, the first problem is in my haste of cut and paste coding I was only requesting the full name and email. I revised the code to request first name, last name, and realm id. Now first name and last name come through fine. However, it took a big of poking around to get to the bottom of the realm id issue. First, the documentation did not give a clear answer on the attribute uri; however, I was able to find a clear answer on this thread https://idnforums.intuit.com/textthread.aspx?catid=69&threadid=16954. Paul Jackson gives a clear idea what is going on here:
The attribute for realm id is http://axschema.org/intuit/realmId
Sometimes the attribute does not come through
I put together a technique based on his suggestion in this thread. Basically, if the realm id does not come through then I'll parse it from the referring url which has it in the query string as realmId. Clearly, this is brittle but provides a "working" solution for now.
Here is a code snippet you can use during stage 3 of the handshake.
_realmId = fetch.GetAttributeValue(OpenId.IntuitWellKnownRealmId);
if (_realmId == null && httpRequest.UrlReferrer != null)
{
var url = httpRequest.UrlReferrer.ToString();
var i = url.IndexOf('?');
if (i != -1)
{
var querystring = url.Substring(i);
_realmId = System.Web.HttpUtility.ParseQueryString(querystring)["realmId"];
}
}
I take zero credit for this solution, Paul already had it figured out. Just posting here to help anyone searching on this problem.
This query gives visits by traffic source;
https: //www.google.com/analytics/feeds/data?ids=ga%3A123456&dimensions=ga%3Asource%2Cga%3Amedium&metrics=ga%3Avisits&sort=-ga%3Avisits&start-date=2009-08-12&end-date=2009-08-26&max-results=50
But, when specifying a filter: ga:pagePath==/Default.aspx - I get zero results
https: //www.google.com/analytics/feeds/data?ids=ga%3A123456&dimensions=ga%3Asource%2Cga%3Amedium&metrics=ga%3Avisits&filters=ga%3ApagePath%3D%3D%2FDefault.aspx&sort=-ga%3Avisits&start-date=2009-08-12&end-date=2009-08-26&max-results=50
How can i filter on ga:pagePath?
query1.Dimensions = "ga:date,ga:pagepath"
query1.Metrics = "ga:pageviews"
query1.Sort = "ga:date,ga:pagepath"
query1.GAStartDate = "2010-09-01"
query1.GAEndDate = "2010-09-10"
query1.Filters = "ga:pagePath=~/abc*"
hope this works for you.
Here's a very late theory to your question...
Your data query looks valid assuming you have content indexed as /Default.aspx. I've reproduced the query on my asp.net site and get back results. I can see two potential scenarios where your query may produce empty results.
The filter mechanism in Analytics is case sensitive so if by chance links come into the page as default.aspx your filter for 'Default.aspx' won't hit. I wouldn't have guessed it works this way but lately I've had to go back and fix some of our queries as they produced empty results when case mismatches occurred.
The second possible issue relates to the use of 'Default Documents'. If links are coming into your site without a file name ('/' or '/somepath/') and 'default.aspx' is setup as a 'Default Document' in IIS, then you really want the filter to be ga:pagePath==/.