I have a xpages booking application, there is a view for search officer's booking record. Due to search values may contains "(" or ")" or other symbols so I would use wild card search in the code. Also I need to ensure the search result returns properly so I created extra fields to connect two fields together (e.g. Fieldname1+++Fieldname2). I put the search code in view (In Properties -> Data -> Search in view results)
Here is the code for the search
var qstring= "";
if (
(sessionScope.officerSearch != null && sessionScope.officerSearch != "")||
(sessionScope.startDateSearch != null && sessionScope.startDateSearch != "")||
(sessionScope.returnDateSearch != null && sessionScope.returnDateSearch !=""))
{
qstring = " ( FIELD location+++group contains " + sessionScope.locationSearch +"+++"+ sessionScope.groupSearch +
" | FIELD location+++officer contains " + sessionScope.locationSearch +"+++"+ sessionScope.officerSearch +")" +
" & FIELD dateYYYYMM >=" + sessionScope.startDateSearch +
"& FIELD dateYYYYMM <=" + sessionScope.returnDateSearch;
}
return qstring;
When I run the application, I have two problems. If I search only one officer, I get an exception (Notes error: Query is not understandable). If I search more than one officer, I don't get the exception but the result does not return anything. So I use a computed field, paste the search code in there and run the program again. The computed field shows what I searched.
Due to I put the code in the computed field, I can read the whole query for the search
Problem 1: If I search one officer, although it occurs exception, I can imagine the query is like this
( FIELD location+++group contains "WEST+++GP AM(OSO)" | FIELD location+++officer contains "WEST+++John Freeman") & FIELD CompYMNUM >=201001& FIELD dateYYYYMM <=201712
If I put this query in Lotus Notes Client, and cick Search button, it returns the result properly
Problem 2: If I search multiple officers, although the result does not return anything, the computed field shows the query like this
( FIELD location+++group contains "WEST+++GP AM(OSO)" | FIELD location+++officer contains "WEST+++John Freeman, Kevin Sims, Sally Johnson, Tom Chan, William Ross") & FIELD dateYYYYMM >=201001& FIELD dateYYYYMM <=201712
I put this query in Lotus Notes Client, and cick Search button, it returns the result properly too.
I feel so strange that the Lotus Notes Client can return the proper result but the xpages cannot. Also I don't understand why if I search one officer, it causes exception (Query not understandable) and if I search more than one officer, it does not exception and not result return.
I think the reason I get the exception or the review does not the the result is I have "(" and ")" in the search value? However, I used wild card search to bound the search criteria (Or I misunderstand the wild card search?)
Also another reason I think is the field location+++officer. If search multiple officers, the field only recognize one officer e.g. "WEST+++John Freeman, Kevin Sims, Sally Johnson, Tom Chan, William Ross"?
Last but not least, maybe there is coding mistake in the code? But there is mistake in the code, I guess Lotus Notes Client will not return the result.
I try to solve the problems, but every time when I run the application, those problems still occurs.
Is my query has mistake?
Grateful if someone let me know my mistakes please? Thank you.
Related
I am trying to create a formula to obtain a list of items that have no open transactions.
I cant just filter out by status as this filters out transactions that are open, as opposed to showing me only items with nothing open.
So basically if an item has anything open then i dont want it on the search. I do need it on the search if it has all closed or it has no transactions at all.
Hoping someone can help put me in the right direction.
I am a little bit stuck at where to start with the formulas and tried a case formula.
You can use item saved search adding under criteria as "Transaction Fields-status-anyOf-select all closed/rejected/declined statuses" not in filter reason of saved search.
Thanks.
To get the value of non transaction items as well, You need to check the check box use expression under criteria in standard subtab use parens() with OR expression.
And add one more condition as "Transaction Fields-Internal Id-anyOf-none with
"Transaction Fields-status-anyOf-select all closed/rejected/declined statuses".
Add both condition with OR logic.
It will work for both items condition if it has transaction status with closed or with none of transaction internal ids.
Thanks.
I think this is possible in a saved search, and requires a change in the way the filtering is done. Rather than filtering on the "Filters", using grouping and summary calculations to determine if an item qualifies, basically :
Create the item saved search as you would normally, but don't include a "Standard" filter for the openness of the transaction.
In the results, group by item name (or internalid), and another fields you want to include in the top-level results.
In the Criteria - Summary list, add a Formula (Number) condition :
Summary Type= Sum (Count won't work here)
Formula = case when {transaction.status} = 'Open' then 1 else 0 end
Equal to 0
Whether this is more or less elegant than bknight's answer is debatable.
I don't think this is the sort of thing you can do with a single saved search.
It would be fairly easy to do with SuiteQL though.
The script below runs in the console and finds items that are not on any Pending Billing Sales Orders. It's adapted from a script with a different purpose but illustrates the concept.
You can get a list of the status values to use by creating a saved search that finds all the transactions with open statuses you want to exclude , take note of that saved search's id and running the second script in the console
require(['N/query'], query => {
const sqlStr = `
select item.id, itemid, count(po.tranid) as po, count(bill.tranId) as bill, max(bill.tranDate) as lastBilled, count(sale.tranId) as sales, count(tran.tranId) as trans
from item
left outer join transactionLine as line
on line.item = item.id
left outer join transaction as tran on line.transaction = tran.id
left outer join transaction as po on line.transaction = po.id and po.type = 'PurchOrd'
left outer join transaction as bill on line.transaction = bill.id and bill.type = 'VendBill'
left outer join transaction as sale on line.transaction = sale.id and sale.type in ('CustInvc', 'CashSale')
where item.id not in (select otl.item from transactionLine otl, transaction ot where
otl.transaction = ot.id and ot.status in ('SalesOrd:F'))
group by item.id, item.itemid
`;
console.log(sqlStr);
console.log(query.runSuiteQL({
query: sqlStr
}).asMappedResults().map((r, idx)=>{
if(!idx) console.log(JSON.stringify(r));
return `${r.id}\t${r.itemid}\t${r.po}\t${r.bill}\t${r.lastBilled}\t${r.sales}\t${r.trans}`;
}).join('\n'));
});
require(['N/search'], search=>{
const filters = search.load({id:304}).filters;
console.log(JSON.stringify(filters.find(f=>f.name == 'status'), null, ' '));
});
In terms of doing something with this you could run this in a saved search and email someone the results, show the results in a workbook in SuiteAnalytics or build a portlet to display the results - for this last Tim Dietrich has a nice write up on portlets and SuiteQL
In the Xpage, it will display officer name, officer type and department (I use sessionScope variable to do that). There is a combobox to allow the user to select
the officer type that the user wants to compare. And then a button is used for export the data.
The export data will show the comparison between the document that the selected officer type needs to read and the document that the current officer has been read.
If the officer has read document that matches the selected officer type needs to read, it will show "Document has been read", otherwise, it will
show "Document has not read"
Due this post export data from panel let me know how to export data from panel, I begin to follow the code from the post and modify some code to fulfill the user requirement.
I have two views to do the comparison:
Each officer type needs to read various documents, so there is a view to store the value, the first column is the officer type and the second column is the document
that the officer type should read. The second column is multi value field and I am able to set the Multi-value separator is New Line.
Another view the includes officer name, the related officer type and document read by the officer.(no column for multi value)
I use #DbLookup and for loop to do the comparison and here is part of code:
var officer = sessionScope.Officer;
var officerType = sessionScope.OfficerType;
var showSelectedTypeDoc = #DbLookup(#DbName(),"view1", officerType, 2);
var showUserHasReadDoc= #DbLookup(#DbName(),"view2", officer, 3);
var result = "";
while (viewEnt != null)
{
if(viewEnt.getColumnValues()[1] == officer) //get the value related to the officer in the session
{
writer.write("<tr>");
for(var s = 0; s < showSelectedTypeDoc.length;s++) //loop in view1 first
{
for(var r = 0; r < showUserHasReadDoc.length;r++)//then loop the view2
{
if(showSelectedTypeDoc[s] == showUserHasReadDoc[r])//if value matches between 2 views
{
result = "Document has been read";
}
else
{
result = "Document has not read";
}
}
}
//display the list of the document category related to specific officer type
writer.write("<td>" + viewEnt.getColumnValues()[2] + "</td>"); //Problem occurs here, it only shows the first value, the appearance depends on the loop
writer.write("<td>" + result+ "</td>"); // only display the else part
writer.write("</tr>");
}
}
Actually I suppose
viewEnt.getColumnValues()[2]
will show all the relevant values, however when I export the value, it only display the first value e.g. [first value].
The code
writer.write("<td>" + result+ "</td>");
I review the if condition, it should be fine, but it only shows the else part
I tried
showSelectedTypeDoc[s]
instead of
viewEnt.getColumnValues()[2]
it shows undefined and the appearance depends on the looping.
But if I try
showSelectedTypeDoc[0] //or showSelectedTypeDoc[1], etc
it can show the correct value and the appearance depends on the looping.
However I will not know how many values that the variable stored, so I prefer
showSelectedTypeDoc[s]
to find all relevant values but not successful.
I start to think why it only shows the first value. I revise the code and the view, I guess probably is the multi value column cause the problem. It is because In Louts Client, in the view I only click once to particular value, if the value has multi value, it will show the "tick" symbol to relevant data.
So I would like to know to how to use
viewEnt.getColumnValues()[2]
to retrieve all the data related to a specific value. Grateful for your advice please. Thank you.
getColumnsValues() returns a Java Vector. So the syntax you need to get a specific element in the Vector is getColumnValues().get(2) (which will get the third column).
Getting a column values doesn't need it to be categorised. Categorisation is only required for retrieving the entry, not the column values.
I believe when using the index that static columns (e.g. with formula set to a static value like 1 or "<tr>") are skipped.
I have a requirement to sync some entities (account, lead, contact etc) to a database table outside of the crm database but on the same server. I am looking for a supported way for doing this. Here's what I have tried, that don't work:
I first created table in the outside database that matches the schema from dbo.account (view). Then I wrote post create, post update, post assign and post delete plugins to create, update or delete the record in the outside table (using ADO.Net). I have written the plugin in the most generic way so that it can be registered for any entity with minimum changes to the plugin (by not hardcoding the field names). Doing it this way, the problem I am running into is with the fields that are foreign key to other tables. Eg. in dbo.account, there are fields like PrimaryContactId and PrimaryContactIdName, PreferredSystemUserId and PreferredSystemUserIdName, ParentAccountId and ParentAccountIdName etc. In the input parameters for the plugin, the xxxxId fields are available when they are updated, but not the 'xxxxIdName' fields. Because of which I am not able to 'sync' the table as is.
Is there a solution to make my plugin solution work?
Is there a better supported way for having a sync table?
Thanks in advance,
PS: 1. The data sync has to be in real time
PS: 2. Here is my function to get the query that does the update
private static string PrepareUpdateQuery(ITracingService tracingService, IEnumerable<KeyValuePair<string, object>> attributeCollection, string entityName, string entityIdName)
{
var query = "Update MainDb.MSCRM." + entityName + " set ";
foreach (KeyValuePair<string, object> keyValuePair in attributeCollection)
{
tracingService.Trace("Key: {0}", keyValuePair.Key);
if (keyValuePair.Key != entityIdName && keyValuePair.Key != "modifiedonbehalfby")
{
query = query + keyValuePair.Key + " = ";
if (keyValuePair.Value == null)
query = query + "null, ";
else
{
var typeOfValue = keyValuePair.Value.GetType().Name;
tracingService.Trace("typeOfValue: {0}", typeOfValue);
switch (typeOfValue)
{
case "EntityReference":
query = query + "'" + ((EntityReference)keyValuePair.Value).Id + "', ";
break;
case "OptionSetValue":
query = query + ((OptionSetValue)keyValuePair.Value).Value + ", ";
break;
case "BooleanManagedProperty":
query = query + (((BooleanManagedProperty)keyValuePair.Value).Value ? "1" : "0") + ", ";
break;
default:
query = query + "'" + keyValuePair.Value + "', ";
break;
}
}
}
}
return query;
}
If all you're after is the name of the entity that is an attribute on your currently executing plugin, the EntityReference object has a Name property that should contain that name. If it doesn't you you can query CRM with the id and logical name to get any value that you're looking for on the referenced entity.
Edit 1
If you're just moving the data, why even bother setting the referenced name? I'd removed those names from your database table, and just create a view that looks up the corresponding entity's name. It's what CRM is doing. It also makes your other database more normalized. IE. If you update the name of an entity that is referenced by another entity, you will have to search for and update all of those names...
the xxxIdName fields are just a helper for the views really, you can easily figure out what they
should contain.
For example, say you have an account 'some company' with a primary contact called 'bob bobson'.
when processing the account entity the primarycontactId will be a guid and the primarycontactIdName will be 'bob bobson', the accountIdName will be 'some company'.
easiest way to do this in your plugin is to look up the related entity and get the value from there - 90% of the time it's just the name field.
you also need to consider however if you are doing the right thing in using the CRM schema, perhaps it would be better to copy only the fields you need and use your own schema for the sync table.
UPDATE: just saw your code, you are overwritting the value contained in query and not setting it back to the base query, so you will get odd results/errors on the second pass through the foreach
If you're dead set on putting the related entity name in the primary entity table you can always grab it like this:
var entityEntityRef = (EntityReference)keyValuePair.Value;
var relatedEntity = service.Retrieve(entityRef.LogicalName, entityRef.Id, new ColumnSet(true));
Now relatedEntity as all the attributes available. You'll mostly be looking for the Name field, but some entities are different, like contact which uses the full name field I believe.
You can, in fact, register a single plugin for all entities (checking, of course, that the one that's firing the message is in the list of treated ones).
IEnumerable<String> supportees = new String[]{ "account", "contact" };
if(!supportees.Any(element
=> element == targetLogicalName))
return;
As for the linked entities, you have three choices.
Just skip them. Not full data sync but easies to implement.
Store the guids only. Data sync is instance-wide - limited but moderately easy.
Get all the linked data. Full information but a recursive PIA to develop.
For my website, i need to do a search mechanism, in which some of the entry field would be: Country, City, Between Dates (with or without year field), Keywords, etc etc.
My problem is, the user must decide what they wanna search for. For example, if they want to introduce just date, or date and city, or city and keyword.. etc. I dont really know how to do that, i mean, i know how to search for one thing at a time, but i'm not sure how can do this all-in-one.
a) Would i need like something like this: (if-else, if-else) and than write the code for each combination, or there is an easier way to do that?
b )Bytheway, my search mechanism is done the folowing way (i'v never done a search mechanism before, so i dont know if it is the best aproach, would apreciate some comments here also and suggestions):
class book{
String a
String b
...
Date z
String allAttributesTogether() {
a + b + c + ... + z
}
}
then in my controller, i do a double for statment and cross-match the introduced words for the search and the result of allAttributesTogether().
Thanks in advanced, VA
Check out the filter pane plugin.
When you say "search", comes to my mind search engines. But I think you are asking about querying the database, right?
If you are talking about search mechanisms, search engines are a great tool. You can take a look at Lucene, Compass, and ElasticSearch (ES) to name a few. Compass and ES are based on lucene, but are much higher in the abstraction level (easier to use).
I have been using ElasticSearch with great satisfaction.
If you are talking about querying the database, then you can just build a HQL query dynamically. The method bellow should be in a Controller, as it uses the params attribute. It is not tested ok?
List allAttributesTogether() {
def query = " select book from Book book "
def queryParams = [:]
def needsAnd = false
if(params.a || params.b || params.z ){
query += " where "
}
if(params.a){
query += " book.a = :a "
queryParams['a'] = params.a
needsAnd = true
}
if(params.b){
if(needsAnd) query += " and "
query += " book.b = :b "
queryParams['b'] = params.b
needsAnd = true
}
if(params.a){
if(needsAnd) query += " and "
query += " book.z = :z "
queryParams['z'] = params.z
}
return Book.executeQuery(query, queryParams)
}
There is also the alternative of using Criteria builder. You can also use "if" to add clauses to your Criteria clauses.
Ok,
Today I am trying to learn Subsonic. Pretty cool stuff.
I am trying to build some search functionality into my website but am struggling about how I might achieve this in Subsonic.
I have one search field that could contain multiple keywords. I want to return results that match all of the keywords. The target on the search is a single text column.
So far I have this (it runs but never returns results):
return new SubSonic.Select().From(Visit.Schema)
.InnerJoin(InfopathArchive.VisitIdColumn, Visit.VisitIdColumn)
.Where(InfopathArchive.XmlDocColumn).Like(keywords)
.ExecuteTypedList<Visit>();
There is a one to one mapping between the Visit table and the InfoPathArchive table. I just want to return the collection of Visits that have the keywords in the related XMLDocColumn.
If I could get that working it would be great. Now the second problem is that if someone searches for 'australia processmodel' then obviously the above code should only return that exact phrase. How can I create a query that splits up my search term so that it must return documents that contain ALL of the individual search terms?
Any help appreciated.
Edit: Ok, so the basic search works, but the multiple keyword search doesnt. I did what Adam suggested but it seems Subsonic only uses one parameter for the query.
Here is the code:
List<string> wordsInQueryList = keywords.Split(' ').ToList();
SqlQuery q = Select.AllColumnsFrom<Visit>()
.InnerJoin(InfopathArchive.VisitIdColumn, Visit.VisitIdColumn)
.Where(Visit.IsDeletedColumn).IsEqualTo(false);
foreach(string wordInQuery in wordsInQueryList)
{
q = q.And(InfopathArchive.XmlDocColumn).Like("%" + wordInQuery + "%");
}
return q.ExecuteTypedList();
Then if I look at the query that Subsonic generates:
SELECT (bunch of columns)
FROM [dbo].[Visit]
INNER JOIN [dbo].[InfopathArchive] ON [dbo].[Visit].[VisitId] = [dbo].[InfopathArchive].[VisitId]
WHERE [dbo].[Visit].[IsDeleted] = #IsDeleted
AND [dbo].[InfopathArchive].[XmlDoc] LIKE #XmlDoc
AND [dbo].[InfopathArchive].[XmlDoc] LIKE #XmlDoc
So it ends up that only the last keyword is being searched for.
Any ideas?
First question:
return new SubSonic.Select().From(Visit.Schema)
.InnerJoin(InfopathArchive.VisitIdColumn, Visit.VisitIdColumn)
.Where(InfopathArchive.XmlDocColumn).Like("%" + keywords + "%")
.ExecuteTypedList<Visit>();
Second question:
Pass a List of words in your query to a function that builds a SubSonic query as follows
SqlQuery query = DB.Select().From(Visit.Schema)
.InnerJoin(InfopathArchive.VisitIdColumn, Visit.VisitIdColumn)
.Where("1=1");
foreach(string wordInQuery in wordsInQueryList)
{
query = query.And(InfopathArchive.XmlDocColumn).Like("%" + wordInQuery + "%")
}
return query.ExecuteTypedList<Visit>();
Obviously this is untested but it should point you in the right direction.
You can do what Adam is suggesting or with 2.2 you can simply use "Contains()" instead of Like("%...%"). We also support StartsWith and EndsWith() :)