Here is an example demonstrating searching:
#include "librets.h" #include <iostream> using namespace librets; using std::cout; using std::cerr; using std::endl; int main(int argc, char * argv[]) { try { RetsSessionPtr session( new RetsSession("http://demo.crt.realtors.org:6103/rets/login")); session->Login("Joe", "Schmoe"); if (session->GetDetectedRetsVersion() != session->GetRetsVersion()) { cout << "** Warning, requested RETS version \"" << session->RetsVersionToString(session->GetRetsVersion()) << "\", got version \"" << session->RetsVersionToString(session->GetDetectedRetsVersion()) << "\" ** " << endl; } SearchRequestAPtr searchRequest( new SearchRequest("Property", "RES", "(ListPrice=300000-)")); SearchResultSetAPtr results = session->Search(searchRequest.get()); while (results->HasNext()) { cout << "ListingID: " << results->GetString("ListingID") << endl; cout << "ListPrice: " << results->GetString("ListPrice") << endl; cout << " Bedrooms: " << results->GetString("Bedrooms") << endl; cout << " City: " << results->GetString("City") << endl; cout << endl; } session->Logout(); } catch (RetsException & e) { e.PrintFullReport(cerr); } }
Here is an example showing how to use metadata:
/* * Copyright (C) 2005-2009 National Association of REALTORS(R) * * All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, and/or sell copies of the * Software, and to permit persons to whom the Software is furnished * to do so, provided that the above copyright notice(s) and this * permission notice appear in all copies of the Software and that * both the above copyright notice(s) and this permission notice * appear in supporting documentation. */ #include "librets.h" #include "Options.h" #include <iostream> using namespace librets; using std::string; using std::vector; using std::cout; using std::endl; using std::exception; void dumpSystem(RetsMetadata * metadata); void dumpAllForeignKeys(RetsMetadata * metadata); void dumpAllResources(RetsMetadata * metadata); void dumpAllClasses(RetsMetadata * metadata, MetadataResource * resource); void dumpAllTables(RetsMetadata * metadata, MetadataClass * aClass); void dumpAllLookups(RetsMetadata * metadata, MetadataResource * resource); void dumpAllLookupTypes(RetsMetadata * metadata, MetadataLookup * lookup); int main(int argc, char * argv[]) { try { Options options; if (!options.ParseCommandLine(argc, argv)) { return 0; } RetsSessionPtr session = options.RetsLogin(); if (!session) { cout << "Login failed\n"; return -1; } if (session->GetDetectedRetsVersion() != session->GetRetsVersion()) { cout << "** Warning, requested RETS version \"" << session->RetsVersionToString(session->GetRetsVersion()) << "\", got version \"" << session->RetsVersionToString(session->GetDetectedRetsVersion()) << "\" ** " << endl; } if (session->GetDetectedRetsVersion() == RETS_1_7) { try { ServerInformationResponseAPtr serverInfo = session->GetServerInformation(); if (serverInfo.get()) { StringVector parameters = serverInfo->GetParameters(); StringVector::const_iterator i; for (i = parameters.begin(); i != parameters.end(); i++) { if (i->empty()) { continue; } cout << *i << ": " << serverInfo->GetValue(*i) << endl; } } } catch (RetsException & e) { /* * The ServerInformation Transaction is not supported. * Continue silently. */ } } RetsMetadata * metadata = session->GetMetadata(); dumpSystem(metadata); dumpAllForeignKeys(metadata); dumpAllResources(metadata); session->Logout(); } catch (RetsException & e) { e.PrintFullReport(cout); return 1; } catch (exception & e) { cout << e.what() << endl; return 2; } return 0; } void dumpSystem(RetsMetadata * metadata) { MetadataSystem * system = metadata->GetSystem(); cout << "System ID: " << system->GetSystemID() << endl; cout << "System Description: " << system->GetSystemDescription() << endl; cout << "Comments: " << system->GetComments() << endl; } void dumpAllForeignKeys(RetsMetadata * metadata) { MetadataForeignKeyList foreign_keys = metadata->GetAllForeignKeys(); MetadataForeignKeyList::iterator i; cout << endl; for (i = foreign_keys.begin(); i != foreign_keys.end(); i++) { MetadataForeignKey * foreign_key = *i; cout << "Foreign Key ID:" << foreign_key->GetForeignKeyID() << endl; cout << " Parent Resource: " << foreign_key->GetParentResourceID(); cout << ", Class: " << foreign_key->GetParentClassID(); cout << ", Name: " << foreign_key->GetParentSystemName() << endl; cout << " Child Resource: " << foreign_key->GetChildResourceID(); cout << ", Class: " << foreign_key->GetChildClassID(); cout << ", Name: " << foreign_key->GetChildSystemName() << endl; } } void dumpAllResources(RetsMetadata * metadata) { MetadataResourceList resources = metadata->GetAllResources(); MetadataResourceList::iterator i; cout << endl; for (i = resources.begin(); i != resources.end(); i++) { MetadataResource * resource = *i; dumpAllClasses(metadata, resource); } for (i = resources.begin(); i != resources.end(); i++) { MetadataResource * resource = *i; dumpAllLookups(metadata, resource); } } void dumpAllClasses(RetsMetadata * metadata, MetadataResource * resource) { string resourceName = resource->GetResourceID(); MetadataClassList classes = metadata->GetAllClasses(resourceName); MetadataClassList::iterator i; for (i = classes.begin(); i != classes.end(); i++) { MetadataClass * aClass = *i; cout << "Resource name: " << resourceName << " [" << resource->GetStandardName() << "]" << endl; cout << "Class name: " << aClass->GetClassName() << " [" << aClass->GetStandardName() << "]" << endl; dumpAllTables(metadata, aClass); cout << endl; } } void dumpAllTables(RetsMetadata * metadata, MetadataClass * aClass) { MetadataTableList tables = metadata->GetAllTables(aClass); MetadataTableList::iterator i; for (i = tables.begin(); i != tables.end(); i++) { MetadataTable * table = *i; cout << "Table name: " << table->GetSystemName() << " [" << table->GetStandardName() << "]" << " (" << table->GetDataType() << ")"; if (!table->GetMetadataEntryID().empty()) { cout << " MetadataEntryID: " << table->GetMetadataEntryID(); } cout << endl; } } void dumpAllLookups(RetsMetadata * metadata, MetadataResource * resource) { string resourceName = resource->GetResourceID(); MetadataLookupList classes = metadata->GetAllLookups(resourceName); MetadataLookupList::iterator i; for (i = classes.begin(); i != classes.end(); i++) { MetadataLookup * lookup = *i; cout << "Resource name: " << resourceName << " [" << resource->GetStandardName() << "]" << endl; cout << "Lookup name: " << lookup->GetLookupName() << " (" << lookup->GetVisibleName() << ")"; if (!lookup->GetMetadataEntryID().empty()) { cout << " MetadataEntryID: " << lookup->GetMetadataEntryID(); } cout << endl; dumpAllLookupTypes(metadata, lookup); cout << endl; } } void dumpAllLookupTypes(RetsMetadata * metadata, MetadataLookup * lookup) { MetadataLookupTypeList lookupTypes = metadata->GetAllLookupTypes(lookup); MetadataLookupTypeList::const_iterator i ; for (i = lookupTypes.begin(); i != lookupTypes.end(); i++) { MetadataLookupType * lookupType = *i; cout << "Lookup value: " << lookupType->GetValue() << " (" << lookupType->GetShortValue() << ", " << lookupType->GetLongValue() << ")"; if (!lookupType->GetMetadataEntryID().empty()) { cout << " MetadataEntryID: " << lookupType->GetMetadataEntryID(); } cout << endl; } }
Lastly, here is an example showing more sophisticated usage. In this example, metadata is searched to determine the key field for the given resource and class. If this is a RETS 1.7 and later server, then the timestamp field is also determined from metadata. If pre RETS 1.7, then the user must provide the name of the timetsamp field. By default, and using the demo server, this example will search residential property listings.
Using the keyfield, all listings are returned that have been changed since the specified timestamp. The first search only returns the keyfield. The reason for this is that many servers limit the amount of data to be returned at any one time. If more columns are returned, less total listings can be returned. The intent is that given only the keyfield, we should be able to retrieve all the keys since the date in the timestamp.
Next, using the keyfield, the full listing is fetched and returned. Then all of the Photos for that listing are retrieved and their names displayed.
/* * Copyright (C) 2008 National Association of REALTORS(R) * * All rights reserved. * * Permission is hereby granted, free of charge, to any person * obtaining a copy of this software and associated documentation * files (the "Software"), to deal in the Software without * restriction, including without limitation the rights to use, copy, * modify, merge, publish, distribute, and/or sell copies of the * Software, and to permit persons to whom the Software is furnished * to do so, provided that the above copyright notice(s) and this * permission notice appear in all copies of the Software and that * both the above copyright notice(s) and this permission notice * appear in supporting documentation. */ #include "librets.h" #include "Options.h" #include <iostream> #include <iomanip> #include <boost/date_time.hpp> #include "librets/str_stream.h" using namespace librets; using std::cout; using std::cerr; using std::endl; using std::string; using std::stringstream; using std::setw; using std::vector; namespace po = boost::program_options; int main(int argc, char * argv[]) { try { string classTimeStamp; SearchRequest::CountType count; string countString; int defaultLimit = SearchRequest::LIMIT_DEFAULT; int defaultOffset = SearchRequest::OFFSET_NONE; SearchRequest::FormatType format = SearchRequest::COMPACT_DECODED; string keyField; string lastModified; vector<string> listingIds; int limit; RetsMetadata * metadata; MetadataClass * metadataClass; MetadataResource * metadataResource; int offset; bool printCount; string query; string resource; string searchClass; SearchRequestAPtr searchRequest; string select; bool standardNames = true; int totalListings = 0; string type; Options options; options.descriptions.add_options() ("resource,r", po::value<string>(&resource) ->default_value("Property"), "Search resource") ("class,C", po::value<string>(&searchClass) ->default_value("RES"), "Search class") ("timestamp,T", po::value<string>(&classTimeStamp) ->default_value(""), "Systemname of the Class TimeStamp field") ("lastmodified,L", po::value<string>(&lastModified) ->default_value(""), "RETS timestamp of the earliest date from which to select") ("select,s", po::value<string>(&select) ->default_value("ListingID,ListPrice,Beds,City"), "Search select") ("query,q", po::value<string>(&query) ->default_value("(ListPrice=300000-)"), "Search query") ("system-names,S", "Use system names, instead of standard names") ("type,t", po::value<string>(&type) ->default_value("Photo"), "Media Type") ("compact", "Use COMPACT instead of COMPACT-DECODED") ("limit,L", po::value<int>(&limit) ->default_value(defaultLimit), "Set the limit") ("offset,O", po::value<int>(&offset) ->default_value(defaultOffset), "Set the offset") ("count,n", po::value<string>(&countString) ->default_value("yes"), "Set the count type: no, yes or count-only)") ; if (!options.ParseCommandLine(argc, argv)) { return 0; } if (options.count("system-names")) { standardNames = false; } if (options.count("compact")) { format = SearchRequest::COMPACT; } if (countString == "yes") { count = SearchRequest::RECORD_COUNT_AND_RESULTS; printCount = true; } else if (countString == "no") { count = SearchRequest::NO_RECORD_COUNT; printCount = false; } else if (countString == "count-only") { count = SearchRequest::RECORD_COUNT_ONLY; printCount = true; } else { count = SearchRequest::RECORD_COUNT_AND_RESULTS; printCount = true; } RetsSessionPtr session = options.RetsLogin(); if (!session) { cout << "Login failed\n"; return -1; } /* * Find the keyfield for the resource. */ metadata = session->GetMetadata(); metadataResource = metadata->GetResource(resource); if (metadataResource == NULL) { cout << "Invalid resource: " << resource << std::endl; session->Logout(); return -1; } keyField = metadataResource->GetKeyField(); /* * Find the timetsamp field if it is known (RETS 1.7 and later). If * not known, the user must provide it. */ metadataClass = metadata->GetClass(resource, searchClass); if (metadataClass == NULL) { cout << "Invalid resource:class: " << resource << ":" << searchClass << std::endl; session->Logout(); return -1; } if (classTimeStamp.length() == 0) classTimeStamp = metadataClass->GetStringAttribute("ClassTimeStamp"); if (classTimeStamp.length() == 0) { cout << "Class " << resource << ":" << searchClass << " has no ClassTimeStamp specified in the metadata. " << std::endl << "Please manually provide one using the --timestamp switch." << std::endl; session->Logout(); return -1; } /* * See if the last modified timestamp has been provided. If not, take the * current time less 24 hours. */ if (lastModified.length() == 0) { stringstream ss; boost::gregorian::date_facet *output_facet = new boost::gregorian::date_facet("%Y-%m-%d"); ss.imbue(std::locale(std::locale::classic(), output_facet)); boost::gregorian::date d(boost::gregorian::day_clock::local_day()); d -= boost::gregorian::days(1); ss << d; lastModified = ss.str(); } /* * OK - let's find all listings that have changed since the lastModified date */ /* * Construct the query. */ searchRequest = session->CreateSearchRequest( resource, searchClass, str_stream() << "(" << classTimeStamp << "=" << lastModified << "+)"); //searchRequest->SetSelect(keyField); /* * Must use system names for this search. */ searchRequest->SetStandardNames(false); searchRequest->SetLimit(SearchRequest::LIMIT_DEFAULT); searchRequest->SetOffset(SearchRequest::OFFSET_NONE); searchRequest->SetCountType(SearchRequest::RECORD_COUNT_AND_RESULTS); searchRequest->SetFormatType(SearchRequest::COMPACT); SearchResultSetAPtr results = session->Search(searchRequest.get()); if (printCount) { cout << "Matching record count: " << results->GetCount() << endl; } /* * For all listings found, fetch the full listing detail and then the * associated Photos. */ while (results->HasNext()) { totalListings++; listingIds.push_back(results->GetString(keyField)); /* * Create a new search to fetch all the detail for the listing. */ SearchRequestAPtr listingRequest = session->CreateSearchRequest( resource, searchClass, str_stream() << "(" << keyField << "=" << results->GetString(keyField) << ")"); listingRequest->SetStandardNames(false); listingRequest->SetLimit(SearchRequest::LIMIT_DEFAULT); listingRequest->SetOffset(SearchRequest::OFFSET_NONE); listingRequest->SetCountType(SearchRequest::NO_RECORD_COUNT); listingRequest->SetFormatType(SearchRequest::COMPACT); SearchResultSetAPtr listingResult = session->Search(listingRequest.get()); StringVector columns = listingResult->GetColumns(); while (listingResult->HasNext()) { /* * Show the listing detail. */ for (StringVector::iterator i = columns.begin(); i != columns.end(); i++) { string column = *i; cout << setw(15) << column << ": " << setw(0) << listingResult->GetString(column) << endl; } } /* * Now set up to fetch the objects associated with this listing. */ GetObjectRequest getObjectRequest(resource, type); getObjectRequest.AddAllObjects(results->GetString(keyField)); GetObjectResponseAPtr getObjectResponse = session->GetObject(&getObjectRequest); ObjectDescriptor * objectDescriptor; while ((objectDescriptor = getObjectResponse->NextObject())) { /* * Report the object details. */ string objectKey = objectDescriptor->GetObjectKey(); int objectId = objectDescriptor->GetObjectId(); string contentType = objectDescriptor->GetContentType(); string description = objectDescriptor->GetDescription(); int replyCode = objectDescriptor->GetRetsReplyCode(); string replyText = objectDescriptor->GetRetsReplyText(); cout << "Object " << objectKey << ":" << objectId; if (!description.empty()) cout << ", description: " << description; cout << endl; if (replyCode != 0) cout << "***: " << replyCode << ": " << replyText << endl; } cout << endl; } cout << "Total Listings Retrieved: " << totalListings << endl; cout << "Listing IDs:" << endl; for (vector<string>::iterator i = listingIds.begin(); i != listingIds.end(); i++) cout << *i << endl; session->Logout(); } catch (RetsException & e) { e.PrintFullReport(cerr); } catch (std::exception & e) { cerr << e.what() << endl; } }
Here is the same example using the .NET language C#:
using System; using System.Collections; using System.IO; using System.Collections.Specialized; using librets; public class Interleaved { /* * This class demonstrates the interleaving of search transactions. */ static void Main(string[] args) { Options options = new Options(); if (!options.Parse(args)) Environment.Exit(1); RetsSession session = options.SessionFactory(); try { if (!session.Login(options.user_name, options.user_password)) { Console.WriteLine("Invalid login"); Environment.Exit(1); } } catch (Exception e) { Console.WriteLine("RetsException: " + e); Environment.Exit(1); } RetsVersion version = session.GetDetectedRetsVersion(); Console.WriteLine("RETS Version: " + ((version == RetsVersion.RETS_1_5) ? "1.5" : ((version == RetsVersion.RETS_1_7) ? "1.7" : "1.0"))); /* * Find the key field for the resource. */ RetsMetadata metadata = session.GetMetadata(); MetadataResource metadataResource = metadata.GetResource(options.search_type); if (metadataResource == null) { Console.WriteLine("Invalid resource: " + options.search_type); session.Logout(); Environment.Exit(1); } string keyField = metadataResource.GetKeyField(); /* * Find the timestamp field if it is known (RETS 1.7 and later). If * not known, then the user must provide it. */ MetadataClass metadataClass = metadata.GetClass(options.search_type, options.search_class); if (metadataClass == null) { Console.WriteLine("Invalid resource:class: " + options.search_type + ":" + options.search_class); session.Logout(); Environment.Exit(2); } if (options.classTimeStamp != null && options.classTimeStamp.Length == 0) options.classTimeStamp = metadataClass.GetStringAttribute("ClassTimeStamp"); if (options.classTimeStamp == null || options.classTimeStamp.Length == 0) { Console.WriteLine("Class " + options.search_type + ":" + options.search_class + " has no ClassTimeStamp specified in the metadata."); Console.WriteLine("Please manually provide one using the --timetsamp switch."); session.Logout(); Environment.Exit(2); } /* * See if the last modified timestamp has been provided. If not, use yesterday. */ if (options.lastModified == null || options.lastModified.Length == 0) { DateTime ts = DateTime.Now; options.lastModified = ts.AddDays(-1).ToString("yyyy-MM-dd"); } /* * OK - let's find all listings that have changed since the lastModified date. */ SearchRequest searchRequest = session.CreateSearchRequest( options.search_type, options.search_class, "(" + options.classTimeStamp.ToString() + "=" + options.lastModified.ToString() + "+)"); searchRequest.SetSelect(keyField); searchRequest.SetLimit(SearchRequest.LIMIT_NONE); searchRequest.SetOffset(SearchRequest.OFFSET_NONE); searchRequest.SetCountType(SearchRequest.CountType.RECORD_COUNT_AND_RESULTS); searchRequest.SetStandardNames(false); /* * This starts the outer search. */ SearchResultSet results = session.Search(searchRequest); Console.WriteLine("Record count: " + results.GetCount()); Console.WriteLine(); while (results.HasNext()) { /* * Fetch the listing detail and media. This will cause a separate search transaction * to be open within the outer search transaction. */ SearchRequest listingRequest = session.CreateSearchRequest( options.search_type, options.search_class, "(" + keyField + "=" + results.GetString(keyField) + ")"); listingRequest.SetStandardNames(false); listingRequest.SetLimit(SearchRequest.LIMIT_DEFAULT); listingRequest.SetOffset(SearchRequest.OFFSET_NONE); listingRequest.SetCountType(SearchRequest.CountType.NO_RECORD_COUNT); listingRequest.SetFormatType(SearchRequest.FormatType.COMPACT); SearchResultSet listingResult = session.Search(listingRequest); IEnumerable columns = listingResult.GetColumns(); while (listingResult.HasNext()) { /* * Show the listing detail. */ foreach (string column in columns) { Console.WriteLine("{0,15}: {1}", column, listingResult.GetString(column)); } Console.WriteLine(); /* * Now set up to fetch the objects associated with this listing. */ GetObjectRequest getObjectRequest = new GetObjectRequest(options.search_type, "Photo"); getObjectRequest.AddAllObjects(listingResult.GetString(keyField)); GetObjectResponse getObjectResponse = session.GetObject(getObjectRequest); foreach(ObjectDescriptor objectDescriptor in getObjectResponse) { /* * Report the object details. */ string objectKey = objectDescriptor.GetObjectKey(); int objectId = objectDescriptor.GetObjectId(); //string contentType = objectDescriptor.GetContentType(); string description = objectDescriptor.GetDescription(); Console.Write ("Object " + objectKey + ":" + objectId.ToString()); if (description.Length > 0) Console.Write (", description: " + description); Console.WriteLine(); } Console.WriteLine("================="); } } session.Logout(); } }