I am trying to match a field that is not a key with a remote hazelcast, the goal here is to create many remote instances and use it to store serialized objects.
what i noticed is that if i do both put and SQL in the same run, the return works, as follow :
my class
public class Provider implements Serializable {
private String resourceId;
private String first;
public String getResourceId() {
return resourceId;
}
public void setResourceId(String resourceId) {
this.resourceId = resourceId;
}
public String getFirst() {
return first;
}
public void setFirst(String first) {
this.first = first;
}
}
code :
/*********** map initlization ************/
Config config = new Config();
config.getNetworkConfig().setPublicAddress(host + ":" + port);
instance = Hazelcast.newHazelcastInstance(config);
map = instance.getMap("providerWithIndex");
String first = "XXXX"
/***** adding item ***************/
Provider provider = new Provider();
provider.setResourceId("11111");
provider.setFirst( first);
map.put(provider);
/********** getting items ************/
EntryObject e = new PredicateBuilder().getEntryObject();
Predicate predicate = e.get( "first" ).equal( first ) ;
Collection<Provider> providers = map.values(predicate);
once i run both put and get in different runs, the result is 0 - so to the same code i get no response.
my only thought is that it only does local fetch and not remote. but i hope i have a bug somewhere.
Your code looks good, apart from the fact that you can't do map.put(provider), you need to pass a key, so like map.put(someKey, provider);
The query looks & works fine.
Related
I'm trying to make Solr search phone numbers which are stored like this +79876543210 using a query like these:
+79876543210
79876543210
89876543210 <-- '+7' is replaced with region specific code '8'
9876543210 <-- '+7' entirely removed
This is just an example. Another one is wired line phone numbers:
+78662123456 <-- '+78662' is a specific region code
78662123456
88662123456
8662123456
123456 <-- region code entirely removed
One way I could manage this is using a separate field which is filled with these variants and used solely during search.
But this has issues with highlighting (it returns <em>123456</em> to be highlighted whereas the real value shown to user is +78662123456).
I thought that maybe it's best to make these indices using just Solr, but how?
First thought was to use managed synonyms filter and pass them along with each added record. But the docs explicitly states:
Changes made to managed resources via this REST API are not applied to the active Solr components until the Solr collection (or Solr core in single server mode) is reloaded.
So reloading a core every time after adding a record is not the way to go.
Other issues involve keeping these synonyms up to date with records.
Could there be another way to solve this?
Thanks to this comment (by MatsLindh) I've managed to assemble a simple filter based on bult-in EdgeNGramTokenFilter:
package com.step4;
import org.apache.lucene.analysis.TokenFilter;
import org.apache.lucene.analysis.TokenStream;
import org.apache.lucene.analysis.tokenattributes.CharTermAttribute;
import org.apache.lucene.analysis.tokenattributes.PositionIncrementAttribute;
import java.io.IOException;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class ReverseCustomFilter extends TokenFilter {
private static final PatternReplacementPair[] phonePatterns = {
new PatternReplacementPair("\\+7", "7"),
new PatternReplacementPair("\\+7", "8"),
new PatternReplacementPair("\\+7", ""),
new PatternReplacementPair("\\+78662", ""),
new PatternReplacementPair("\\+78663", ""),
};
private final CharTermAttribute termAtt = addAttribute(CharTermAttribute.class);
private final PositionIncrementAttribute posIncrAtt = addAttribute(PositionIncrementAttribute.class);
private int curPatternIndex;
private int curPosIncr;
private State curState;
public ReverseCustomFilter(TokenStream input) {
super(input);
}
#Override
public final boolean incrementToken() throws IOException {
while (true) {
if (curPatternIndex == 0) {
if (!input.incrementToken()) {
return false;
}
curState = captureState();
curPosIncr += posIncrAtt.getPositionIncrement();
curPatternIndex = 1;
}
if (curPatternIndex <= phonePatterns.length) {
PatternReplacementPair replacementPair = phonePatterns[curPatternIndex - 1];
curPatternIndex++;
restoreState(curState);
Matcher matcher = replacementPair.getPattern().matcher(termAtt);
if (matcher.find()) {
posIncrAtt.setPositionIncrement(curPosIncr);
curPosIncr = 0;
String replaced = matcher.replaceFirst(replacementPair.getReplacement());
termAtt.setEmpty().append(replaced);
return true;
}
}
else {
restoreState(curState);
posIncrAtt.setPositionIncrement(0);
curPatternIndex = 0;
return true;
}
}
}
#Override
public void reset() throws IOException {
super.reset();
curPatternIndex = 0;
curPosIncr = 0;
}
#Override
public void end() throws IOException {
super.end();
posIncrAtt.setPositionIncrement(curPosIncr);
}
private static class PatternReplacementPair {
private final Pattern pattern;
private final String replacement;
public PatternReplacementPair(String pattern, String replacement) {
this.pattern = Pattern.compile(pattern);
this.replacement = replacement;
}
public Pattern getPattern() {
return pattern;
}
public String getReplacement() {
return replacement;
}
}
}
I have a specific scenario here where I need to pass the connection string based on the user, because users may be mapped to the different databases based on his/her enterprise.
This is the code I use to resolve the dependency with a static variable:
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(
Component.For<IUserRepository>()
.ImplementedBy(typeof(IKS.Dare.Optimix.Repository.EntityFramework.UserModule.UserRepository))
.DependsOn(Dependency.OnValue("connectionString", DatabaseSettings.DefaultConnectionString))
);
}
Because this DefaultConnectionString is supposed to be a dynamic one, I don't want to lock this variable to make it thread safe, as this would degrade the performance. I would want a way so that I can deal with such situation.
Possible consideration which can be that we can give a session, which can be applied as follows:
DynamicParameters((k, d) => d["connectionString"] = Session["connectionString"])
But this is in a different project which doesn't utilize any web component, it's just an installer project which is basically designed for resolving the dependencies only.
My Generic repository looks like following
public class GenericRepository<T> : IGenericRepository<T> where T : BaseEntity
{
private const string IsActive = "IsActive", DbContext = "dbContext", EntityPropertyName = "Entity";
private string connectionString = String.Empty, provider = String.Empty;
public GenericRepository(string connectionString, string provider)
{
this.connectionString = connectionString;
this.provider = provider;
}
public int Count()
{
string tableName = typeof(T).Name;
string query = SqlQueryConstants.SelectCount + SqlQueryConstants.Space + tableName;
int count = DbHelper.ExecuteScalar<int>(query: query, commandType: System.Data.CommandType.Text, connectionString: connectionString, provider: provider, parameters: null);
return count;
}
}
DBHelper class looks like follows
public static int ExecuteNonQuery(string query, CommandType commandType = CommandType.StoredProcedure,
IList<DbParameter> parameters = null, int? timeout = null, string connectionString = "", string provider = "")
{
using (var connection = CreateDbConnection(connectionString, provider))
{
connection.Open();
using (DbCommand command = CreateDbCommand(sqlQuery: query, parameters: parameters,
connection: connection, commandType: commandType, timeout: timeout))
{
return command.ExecuteNonQuery();
}
}
}
public static DbParameter CreateParameter<TValue>(string name, TValue value, DbType dbType,
ParameterDirection parameterDirection = ParameterDirection.Input, string provider = "")
{
DbParameter param = CreateDbProviderFactory(provider).CreateParameter();
param.Value = value;
param.ParameterName = name;
param.DbType = dbType;
param.Direction = parameterDirection;
return param;
}
public static DbConnection CreateDbConnection()
{
return CreateDbConnection(String.Empty, String.Empty);
}
public static DbConnection CreateDbConnection(string connectionString = "", string provider = "")
{
DbConnection connection = null;
if (String.IsNullOrEmpty(provider))
{
if (String.IsNullOrEmpty(DatabaseSettings.DefaultProvider))
throw new ArgumentNullException("provider");
else
provider = DatabaseSettings.DefaultProvider;
}
connection = CreateDbProviderFactory(provider).CreateConnection();
connection.ConnectionString = connectionString;
return connection;
}
Any help would be greatly appreciated.
Note : I couldn't edit steven's answer.
[EDIT] To make it more clear it can be implemented as:
Here controller is inherited from BaseController
public class UserController : BaseController
{
//
// GET: /Index/
private IUserRepository userRepository;
public UserController(IUserRepository userRepository)
: base(userRepository)
{
this.userRepository = userRepository;
}
}
and BaseController is inherited from Controller where in the database settings are being set in the constructor of Base controller so that we don't need to set it everywhere
public abstract class BaseController : Controller
{
public BaseController(IUserRepository userRepository)
{
userRepository.connectionStringProvider.Provider = WebUtilities.CurrentUserData.Provider;
userRepository.connectionStringProvider.ConnectionString = WebUtilities.CurrentUserData.ConnectionString;
}
}
Since, the connection string is runtime data, you should not use it to construct your application components, as is described in this article. So as the article advices, you should hide the connection string behind a provider abstraction. For instance:
public interface IConnectionStringProvider {
string ConnectionString { get; }
}
This way your repositories can depend on IConnectionStringProvider and can call IConnectionStringProvider.ConnectionString at runtime:
public int Count()
{
string tableName = typeof(T).Name;
string query = SqlQueryConstants.SelectCount + SqlQueryConstants.Space + tableName;
return DbHelper.ExecuteScalar<int>(
this.connectionStringProvider.ConnectionString,
provider: provider, parameters: null);
}
It will be trivial to create an IConnectionStringProvider to will get the correct connection string for you:
class DatabaseConnectionStringProvider : IConnectionStringProvider
{
public string ConnectionString => Session["connectionString"];
}
Since this clas depends on application-specifics (the ASP.NET session in this case), the class should not be part of the application's core logic. Instead, this adapter should live in the application's start up path (a.k.a. the composition root, the place where you configure your container).
You might even want to consider not passing along the IConnectionStringProvider into your repositories, but instead create an abstraction that will create a connection itself. This will hide the fact that there is a connection string completely.
What you're looking for is multi tenancy. You can google "castle windsor multi tenancy" and find a number of useful articles.
Here's a similar Stackoverflow question that links to some good articles on Windsor and multi tenancy. In particular, look into Windsor's IHandlerSelector interface.
I've got this action in MVC
[OutputCache(Duration = 1200, VaryByParam = "*")]
public ActionResult FilterArea( string listType, List<int> designersID, int currPage = 1 )
{
// Code removed
}
that fails to present the correct HTML with url like
http://example.com/en-US/women/clothing?designersID=158
http://example.com/en-US/women/clothing?designersID=158&designersID=13
Is this a know bug of OutputCache in .NET cause cannot recognize VaryByParam with a list param or am I missing something?
I too had the same issue in MVC3 and I believe it's still the same case in MVC5.
Here is the setup I had.
Request
POST, Content-Type:application/json, passing in an array of string as the parameter
{ "options": ["option1", "option2"] }
Controller Method
[OutputCache(Duration = 3600, Location = OutputCacheLocation.Any, VaryByParam = "options")]
public ActionResult GetOptionValues(List<string> options)
I tried every option possible with OutputCache and it just wasn't caching for me. Binding worked fine for the actual method to work. My biggest suspicion was that OutputCache wasn't creating unique cache keys so I even pulled its code out of System.Web.MVC.OutputCache to verify. I've verified that it properly builds unique keys even when a List<string> is passed in. Something else is buggy in there but wasn't worth spending more effort.
OutputCacheAttribute.GetUniqueIdFromActionParameters(filterContext,
OutputCacheAttribute.SplitVaryByParam(this.VaryByParam);
Workaround
I ended up creating my own OutputCache attribute following another SO post. Much easier to use and I can go enjoy the rest of the day.
Controller Method
[MyOutputCache(Duration=3600)]
public ActionResult GetOptionValues(Options options)
Custom Request class
I've inherited from List<string> so I can call the overriden .ToString() method in MyOutputcache class to give me a unique cache key string. This approach alone has resolved similar issues for others but not for me.
[DataContract(Name = "Options", Namespace = "")]
public class Options: List<string>
{
public override string ToString()
{
var optionsString= new StringBuilder();
foreach (var option in this)
{
optionsString.Append(option);
}
return optionsString.ToString();
}
}
Custom OutputCache class
public class MyOutputCache : ActionFilterAttribute
{
private string _cachedKey;
public int Duration { get; set; }
public override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (filterContext.HttpContext.Request.Url != null)
{
var path = filterContext.HttpContext.Request.Url.PathAndQuery;
var attributeNames = filterContext.ActionParameters["Options"] as AttributeNames;
if (attributeNames != null) _cachedKey = "MYOUTPUTCACHE:" + path + attributeNames;
}
if (filterContext.HttpContext.Cache[_cachedKey] != null)
{
filterContext.Result = (ActionResult) filterContext.HttpContext.Cache[_cachedKey];
}
else
{
base.OnActionExecuting(filterContext);
}
}
public override void OnActionExecuted(ActionExecutedContext filterContext)
{
filterContext.HttpContext.Cache.Add(_cachedKey, filterContext.Result, null,
DateTime.Now.AddSeconds(Duration), System.Web.Caching.Cache.NoSlidingExpiration,
System.Web.Caching.CacheItemPriority.Default, null);
base.OnActionExecuted(filterContext);
}
}
I am using StackExchange.Redis client with Azure Redis Cache Service. Here is my class,
public class RedisCacheService : ICacheService
{
private readonly ISettings _settings;
private readonly IDatabase _cache;
public RedisCacheService(ISettings settings)
{
_settings = settings;
var connectionMultiplexer = ConnectionMultiplexer.Connect(settings.RedisConnection);
_cache = connectionMultiplexer.GetDatabase();
}
public bool Exists(string key)
{
return _cache.KeyExists(key);
}
public void Save(string key, string value)
{
var ts = TimeSpan.FromMinutes(_settings.CacheTimeout);
_cache.StringSet(key, value, ts);
}
public string Get(string key)
{
return _cache.StringGet(key);
}
public void Remove(string key)
{
// How to remove one
}
public void Clear()
{
// How to remove all
}
}
Update: From the help of Marc, Here is my final class
public class RedisCacheService : ICacheService
{
private readonly ISettings _settings;
private readonly IDatabase _cache;
private static ConnectionMultiplexer _connectionMultiplexer;
static RedisCacheService()
{
var connection = ConfigurationManager.AppSettings["RedisConnection"];
_connectionMultiplexer = ConnectionMultiplexer.Connect(connection);
}
public RedisCacheService(ISettings settings)
{
_settings = settings;
_cache = _connectionMultiplexer.GetDatabase();
}
public bool Exists(string key)
{
return _cache.KeyExists(key);
}
public void Save(string key, string value)
{
var ts = TimeSpan.FromMinutes(_settings.CacheTimeout);
_cache.StringSet(key, value, ts);
}
public string Get(string key)
{
return _cache.StringGet(key);
}
public void Remove(string key)
{
_cache.KeyDelete(key);
}
public void Clear()
{
var endpoints = _connectionMultiplexer.GetEndPoints(true);
foreach (var endpoint in endpoints)
{
var server = _connectionMultiplexer.GetServer(endpoint);
server.FlushAllDatabases();
}
}
}
Now I don't know how to remove all items or single item from redis cache.
To remove a single item:
_cache.KeyDelete(key);
To remove all involves the FLUSHDB or FLUSHALL redis command; both are available in StackExchange.Redis; but, for reasons discussed here, they are not on the IDatabase API (because: they affect servers, not logical databases).
As per the "So how do I use them?" on that page:
server.FlushDatabase(); // to wipe a single database, 0 by default
server.FlushAllDatabases(); // to wipe all databases
(quite possibly after using GetEndpoints() on the multiplexer)
I could not able to flush database in Azure Redis Cache, got this error:
This operation is not available unless admin mode is enabled: FLUSHDB
Instead iterate through all keys to delete:
var endpoints = connectionMultiplexer.GetEndPoints();
var server = connectionMultiplexer.GetServer(endpoints.First());
//FlushDatabase didn't work for me: got error admin mode not enabled error
//server.FlushDatabase();
var keys = server.Keys();
foreach (var key in keys)
{
Console.WriteLine("Removing Key {0} from cache", key.ToString());
_cache.KeyDelete(key);
}
Both answers by #Rasi and #Marc Gravell contain pieces of code needed.
Based on above, here is working snippet assuming there is just 1 server:
You need to connect to redis with allowAdmin=true, one way to obtain such options is to assign AllowAdmin to already parsed string:
var options = ConfigurationOptions.Parse("server:6379");
options.AllowAdmin = true;
var redis = ConnectionMultiplexer.Connect(options);
Then to flush all databases:
var endpoints = redis.GetEndPoints();
var server = redis.GetServer(endpoints[0]);
server.FlushAllDatabases();
Above will work on any redis deployment, not just Azure.
You can delete hash as well ie if you want to clear specific value from any cached list.
For example, we have an emp list and inside with different department as cached.
public static void DeleteHash(string key, string cacheSubKey)
{
if (string.IsNullOrEmpty(key))
throw new ArgumentNullException("key");
Cache.HashDelete(key, cacheSubKey);
}
so you can pass Key name and cache subkey as well.
I am developing an e-commerce website that utilises db4o as the backend. All was well until last week when I came across a problem that I have been unable to solve. The code below is quite straight forward. I open a database file, save an object and then try to retrieve it. However I get nothing back. The "users" variable has a count of zero.
public class Program
{
private static string _connectionString = string.Format(#"c:\aaarrrr.db4o");
static void Main(string[] args)
{
TestUser container = new TestUser() { id = 1, Name = "Mohammad", Surname = "Rafiq" };
Db4oFactory.Configure().Diagnostic().AddListener(new DiagnosticToConsole());
using (var dbc = Db4oFactory.OpenFile(_connectionString))
{
dbc.Store(container);
}
IList<TestUser> users = null;
using (var dbc = Db4oFactory.OpenFile(_connectionString))
{
users = dbc.Query<TestUser>(x => x.id == 1).ToList();
}
if (users.Count > 0)
{
Console.WriteLine("{0} {1} with id of {2}", users.First().Name, users.First().Surname, users.First().id);
}
else
{
Console.WriteLine("\nNo data returned.");
}
Console.ReadLine();
}
}
public class TestUser
{
[Indexed]
private int _id = 0;
private string _name = string.Empty;
private string _surname = string.Empty;
public int id { get { return _id; } set { _id = value; } }
public string Name { get { return _name; } set { _name = value; } }
public string Surname { get { return _surname; } set { _surname = value; } }
}
I have attached db4o diagnostic listener and I see nothing in the console output. Everything seems fine. I know I am writing to the file because I can see the file size increase and the timestamp is also updated. I have checked all the project settings and they are all set to default. I am using .net 4, visual studio 2010 beta and windows 7. I have done some reading regarding reflection permission but I cant see how this applies here. Any help or ideas would be knidly appreciated.
After calling store(), you need to commit() before leaving the using{} statement. You closed your database before committing your changes.