Hello Neil,
Thank you for the excellent support!
I have all my queries on the Join operation, answered.
With Best Regards,
Himanshu
-----Original Message-----
From: nei...@un... [mailto:nei...@un...]
Sent: Thursday, October 01, 2015 8:22 PM
To: lda...@li...; SINGH, HIMANSHU KUMAR (HIMANSHU KUMAR)
Subject: RE: [ldap-sdk-discuss] LDAP SDK - multiple joins not supported??
Because you have multiple types of entries, there really isn't a good way to return only entries that are joined with all three types of entries. The closest you can get is to set requireMatch to true, which will cause the server to only return entries that are joined with at least one other entry. But it isn't possible to require joining with multiple matching entries. You'll just have to look at the entries returned to see what they were joined with and ignore any that weren't joined with all three types. A simple client-side optimization would be to ignore entries that aren't joined with at least three other entries. If there are a lot of entries that are only joined with one or two but not all three types, then this will cause more data to be transferred over the network than you need, but it's still a pretty substantial savings over doing it without the join.
As far as handling searches that have the potential to match a large number of entries, the best thing to do is to use the simple paged results control as described in RFC 2696. This control allows you to request that the server return entries in batches of a specified size, and each set of results will include a token that you can use to issue the same search but pick up where it left off. The LDAP SDK supports this control, and the javadoc (available at https://docs.ldap.com/ldap-sdk/docs/javadoc/index.html?com/unboundid/ldap/sdk/controls/SimplePagedResultsControl.html) includes an example that demonstrates how to use it.
Neil
-----Original Message-----
From: "SINGH, HIMANSHU KUMAR (HIMANSHU KUMAR)" <him...@al...>
To: "Neil A. Wilson" <nei...@un...>, "lda...@li..." <lda...@li...>
Sent: Wed, 30 Sep 2015 11:20 PM
Subject: RE: [ldap-sdk-discuss] LDAP SDK - multiple joins not supported??
Hello Neil,
This is exactly what I was looking for. Thank you.
It is now clear where to use a nested join and where to use a simple search request with a single join.
I could also use the tip on handling the possibly large number of search results.
I implemented the code as you suggested and it works fine.
The only problem I am left with now is "how to return ONLY those results that contain entries from ALL the constituent objects?"
Details:
I have a few cases where the join attribute's name is not the same in the dependent objects.
For example, we may have the join conditions as:
1. Subscriber.cardNumber = bundle.subscriberNumber 2. Subscriber.cardNumber = subscription.subNumber 3. Subscriber.cardNumber = profile.subscriberNum
To handle the different attribute names, I changed the join rule as:
JoinRule joinRule = JoinRule.createORRule(
JoinRule.createEqualityJoin("cardNumber", "subscriberNumber", false),
JoinRule.createEqualityJoin("cardNumber", "subNumber", false),
JoinRule.createEqualityJoin("cardNumber", "subscriberNum", false));
My requirement is to fetch ONLY those joined entries that contain entries from ALL the objects(Subscriber, Bundle, Subscription and Profile).
But with the curent code, I get even those joined entries that do not contain entries from one or more of the dependent objects.
I did try a few filters but nothing worked so far.
With Best Regards,
Himanshu
-----Original Message-----
From: Neil A. Wilson [mailto:nei...@un...]
Sent: Wednesday, September 30, 2015 1:30 PM
To: SINGH, HIMANSHU KUMAR (HIMANSHU KUMAR); lda...@li...
Subject: Re: [ldap-sdk-discuss] LDAP SDK - multiple joins not supported??
It's still not completely clear what you're asking, but it sounds like you're searching to retrieve a subscriber entry, and you want to join that with the associated bundle, subscription, and profile entries.
Further, it sounds like you've got an attribute (let's assume the attribute is cardNumber; card_number isn't a valid LDAP attribute name since underscores aren't allowed) in the subscriber entry whose value matches the subscriberNumber attribute in the bundle, subscription, and profile entries.
If that's the case, then it should be a pretty simple matter. You shouldn't use a nested join for this because even though you want to join with three different kinds of entries, you want to join each of them using information contained the subscriber entry. A nested join is primarily useful in the case where you have to join with other joined entries instead of joining with the original search result entry.
Here's some code to get you started. Since you didn't provide me with all the details about the structure of the entries you're working with, you'll have to edit it as laid out in the comments.
// Create a search request to retrieve subscriber entries.
//
// NOTE: If this search will match a large number of entries,
// then you should probably use something like the simple
// paged results control to retrieve matching entries in
// batches rather than trying to have them all returned in
// a single search. Or at least use a search result
// listener so that the client doesn't have to hold all of
// the matching entries in memory simultaneously.
SearchRequest searchRequest = new SearchRequest(
"ospobject=subscriber,dc=alcatel,dc=com", SearchScope.SUB,
Filter.createPresenceFilter("cardNumber"),
"name", "credit", "cardNumber");
// Create a join rule that will match entries that have a
// subscriberNumber value that is the same as the cardNumber
// value from each entry that matches the search request.
JoinRule joinRule = JoinRule.createEqualityJoin("cardNumber",
"subscriberNumber", false);
// Define the join base DN, which is where we will search for
// the entries to be joined. In this case, we want to join with
// three different types of entries, and those entries exist in
// different subtrees. However, each of those subtrees is below
// "dc=alcatel,dc=com", so we'll use that as the common search
// base DN for the join processing.
JoinBaseDN joinBaseDN =
JoinBaseDN.createUseCustomBaseDN("dc=alcatel,dc=com");
// Next, define a filter that will match only bundle,
// subscription, and profile entries. This may not be needed in
// all cases, but it will be if there are other kinds of entries
// that might have a subscriberNumber attribute that you don't
// want to include in the join. I don't know what the real
// object class names you're using for bundle, subscriber, and
// profile entries, so you should replace what I've used here
// with the appropriate names.
Filter additionalJoinFilter = Filter.createORFilter(
Filter.createEqualityFilter("objectClass", "bundleOC"),
Filter.createEqualityFilter("objectClass", "subscriptionOC"),
Filter.createEqualityFilter("objectClass", "profileOC"));
// Define the set of attributes that we want to be returned.
// Since we're returning three different types of entries, then
// we should include all attributes we want to get from all three
// types of entries. Also include the objectClass attribute so
// that we can use that to determine the type of each joined
// entry.
String[] attributesInJoinedEntries =
{
"bundleName",
"bundleCredit",
"subscriptionName",
"subscriptionCredit",
"profileName",
"objectClass"
};
// Define the behavior that the search should use for entries
// that match the search criteria (in this case, for entries that
// are subscribers that have a cardNumber) but for which there
// aren't any associated bundle, subscription, or profile
// entries. A requireMatch value of false indicates that we
// should return matching subscriber entries even if there aren't
// any associated bundle, subscription, or profile entries. A
// requireMatch value of true indicates that we should only
// return subscriber entries that are joined with at least one
// bundle, subscription, or profile.
boolean requireMatch = false;
// Create the join request value and use that to create the join
// request control that is assigned to the search.
JoinRequestValue joinValue = new JoinRequestValue(
joinRule,
joinBaseDN,
SearchScope.SUB,
DereferencePolicy.NEVER,
0, // No size limit
additionalJoinFilter,
attributesInJoinedEntries,
requireMatch,
null); // No nested join.
searchRequest.addControl(new JoinRequestControl(joinValue));
// Process the search and iterate through the matching entries.
SearchResult searchResult = connection.search(searchRequest);
for (SearchResultEntry subscriberEntry :
searchResult.getSearchEntries())
{
// Get the attributes we want from the subscriber entry.
String subscriberName =
subscriberEntry.getAttributeValue("subscriberName");
String subscriberCredit =
subscriberEntry.getAttributeValue("subscriberCredit");
String subscriberCardNumber =
subscriberEntry.getAttributeValue("cardNumber");
// Get the join result that will link the subscriber entry with
// any associated bundle, subscription, and/or profile entries.
JoinResultControl joinResultControl =
JoinResultControl.get(subscriberEntry);
if (joinResultControl != null)
{
for (JoinedEntry joinedEntry :
joinResultControl.getJoinResults())
{
if (joinedEntry.hasAttributeValue("objectClass",
"bundleOC"))
{
// It's a bundle entry.
String bundleName =
joinedEntry.getAttributeValue("bundleName");
String bundleCredit =
joinedEntry.getAttributeValue("bundleCredit");
}
else if (joinedEntry.hasAttributeValue("objectClass",
"subscriptionOC"))
{
// It's a subscription entry.
String subscriptionName =
joinedEntry.getAttributeValue("subscriptionName");
String subscriptionCredit =
joinedEntry.getAttributeValue("subscriptionCredit");
}
else if (joinedEntry.hasAttributeValue("objectClass",
"profileOC"))
{
// It's a profile entry.
String profileName =
joinedEntry.getAttributeValue("profileName");
}
}
}
}
On 09/29/2015 10:22 AM, SINGH, HIMANSHU KUMAR (HIMANSHU KUMAR) wrote:
> Hello Neil,
>
>
>
> Thank you for your response and pardon me for the non-technical jargon I used. I will be as precise as possible in this text.
>
>
>
> We have to replace an RDBMS database(specifically Oracle) with LDAP; hence the non-LDAP terms. Please excuse me for that too.
>
> The risks, limitations and advantages are well understood; the decision has been made and design is in progress.
>
>
>
> THE JOIN PROBLEM GENERALIZED WITH AN EXAMPLE:
>
>
>
> - We have 4 objects, each made up of a few attributes and represented by a unique DN.
>
>
>
> Dn: ospobject=subscriber,dc=alcatel,dc=com
>
>
> Card_number
>
>
> Name
>
>
> Credit
>
>
>
>
> Dn: ospobject=bundle,dc=alcatel,dc=com
>
>
> Bundle_name
>
>
> Bundle_credit
>
>
> Expiry
>
>
> Subscriber_number
>
>
>
>
> Dn: ospobject=subscription,dc=alcatel,dc=com
>
>
> Subscription_name
>
>
> Expiry
>
>
> Subscriber_number
>
>
>
>
> Dn: ospobject=profile,dc=alcatel,dc=com
>
>
> Profile_name
>
>
> Profile_validity
>
>
> Subscriber_number
>
>
>
>
> **there are a huge number of entries in these objects. It is not possible to combine all these tables in a single table.
>
>
>
> - Our objective is to fetch all of a subscriber’s information, including all his bundles, subscriptions and profile in a single search.
>
> Our JOIN conditions are:
>
> 1. Subscriber.card_number = bundle.Subscriber_number
>
> 2. Subscriber.card_number = subscription.Subscriber_number
>
> 3. Subscriber.card_number = profile.Subscriber_number
>
>
>
>
>
>
>
> What we have tried:
>
> We came across the documentation of the commercial version of the LDAP SDK. We are trying to see if the JoinRequestControl can solve our problem.
>
> Below is an abstract of what we have tried so far:
>
>
>
> ----------------------------------------------------------------------
> ----------------------------------------------------------------------
> ----------------------------------
> //The attributes to be fetched
> String args_subscriber[] ={"name","credit",”card_number”}; //attributes to be fetched from the subscriber object
> String args_bundle[] ={"Bundle_name","Bundle_credit"}; //attributes to be fetched from the bundle object
>
> String args_subscription[]={"Subscription_name","Subscription_credit"}; //attributes to be fetched from the subscription object
>
> String args_profile[] ={"Profile_name”}; //attributes to be fetched from the profile object
>
>
>
>
>
> //Define the SearchRequest on the ‘Subscriber’ object
>
> SearchRequest searchRequest = new SearchRequest(“ospobject=subscriber,dc=alcatel,dc=com”, SearchScope.SUB, Filter.create("card_number=*"),args_subscriber); //the ‘subscriber’ DN.
>
> .
>
> .
>
> .
>
>
>
> //define the nested JoinRequestValue for the join between ‘subscriber’
> and ‘profile’ objects JoinRequestValue nestedJoinval2 = new JoinRequestValue(
> JoinRule.createEqualityJoin("card_number”,
> ”subscriber_number", false),
>
> JoinBaseDN.createUseCustomBaseDN("ospobject=profile,dc=alcatel,dc=com"), //the ‘profile’ object DN.
> SearchScope.SUB,
> DereferencePolicy.NEVER,
> null,
> null,
> args_subscription,
>
> true, null);
>
> .
>
> .
>
> .
>
> //define the nested JoinRequestValue for the join between ‘subscriber’
> and ‘subscription’ objects
>
> //Also add the nested JoinRequestValue defined above, to this
> JoinRequestValue JoinRequestValue nestedJoinval1 = new JoinRequestValue(
> JoinRule.createEqualityJoin("card_number”,
> ”subscriber_number", false),
>
> JoinBaseDN.createUseCustomBaseDN("ospobject=subscription,dc=alcatel,dc=com"), //the ‘subscription’ object DN.
> SearchScope.SUB,
> DereferencePolicy.NEVER,
> null,
> null,
> args_subscription,
>
> true, nestedJoinval2);
>
> .
>
> .
>
> .
>
> //define the nested JoinRequestValue for the join between ‘subscriber’
> and ‘bundle’ objects
>
> //Also add the JoinRequestControl to the SearchRequest
> searchRequest.addControl(new JoinRequestControl(new JoinRequestValue(
> JoinRule.createEqualityJoin("card_number", "subscriber_number", false),
> JoinBaseDN.createUseCustomBaseDN("ospobject=bundle,dc=alcatel,dc=com "), //the ‘bundle’ object DN.
> SearchScope.SUB,
> DereferencePolicy.NEVER,
> null,
> null,
> args_bundle,
>
> true, nestedJoinval1)));
>
>
>
> ----------------------------------------------------------------------
> ----------------------------------------------------------------------
> ----------------------------------
>
>
>
>
>
> The intention of the nested join is to join the subscriber and subscription objects.
>
> But it seems that the nested join works only on the DN specified in its corresponding parent join.
>
>
>
> I can’t find a way of specifying the nested join to work on the Join results of the parent join.
>
>
>
> Is my understanding correct?
>
> Do I have a way out?
>
>
>
>
>
>
>
> With Best Regards,
>
> Himanshu
>
>
>
> -----Original Message-----
>
> From: nei...@un... [mailto:nei...@un...]
>
> Sent: Monday, September 28, 2015 7:43 PM
>
> To: lda...@li...; SINGH, HIMANSHU KUMAR
> (HIMANSHU KUMAR)
>
> Subject: Re: [ldap-sdk-discuss] LDAP SDK - multiple joins not supported??
>
>
>
> It's not really clear what you're asking. Could you provide a specific example that illustrates what you're trying to do? And could you please use LDAP terminology, since LDAP doesn't have primary keys or tables. Do you mean DNs and entries?
>
>
>
>
>
> Neil
>
>
>
>
>
> -----Original Message-----
>
> From: "SINGH, HIMANSHU KUMAR (HIMANSHU KUMAR)"
> <him...@al...>
>
> To: "lda...@li..."
> <lda...@li...>
>
> Sent: Mon, 28 Sep 2015 9:05 AM
>
> Subject: [ldap-sdk-discuss] LDAP SDK - multiple joins not supported??
>
>
>
> Hello LDAP SDK teams,
>
>
>
> I am trying to implement a multi-join using the SDK.
>
> My problem is that I have a "master" entry with 4 different attributes.
>
> All 4 of them refer to the PK attribute of 4 other tables.
>
> These other tables are not directly related to each other.
>
>
>
> I have to implement a join on all 5 tables in a single search request.
>
>
>
> The nestedJoin feature of the JoinRequestControl comes close but cannot completely solve this problem.
>
>
>
> Any ideas??
>
>
>
>
>
> - Himanshu
>
>
>
>
|