Search Android C# Xamarin Sqlite Database - c#

I need some help to figure out how to search my SQLite database.
I am trying to search for their cake and stars; this is my connection:
// Create our connection
string folder = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal);
var db = new SQLiteConnection(System.IO.Path.Combine(folder, "OnTopFiles.db"));
db.CreateTable<User>();
// Insert note into the database
var note = new User { cake = "frosting marble", stars = "5" };
db.Insert(note);
// Show the automatically set ID and message.
Console.WriteLine("{0}: {1}", note.cake, note.stars);

You can use a WHERE query:
var query = conn.Table<User>().Where(u => u.Cake.StartsWith("frosting"));
foreach (var user in query)
{
// Use user.cake or other properties
}

Alternatively, you can use SQL directly. The library you are using hides the SQLite SQL so most of the SQLite syntax is hidden from you. This library is pure .NET Standard and runs on all Xamarin Platforms. You have the full power of SQL so searching is the same as other platforms.
https://github.com/MelbourneDeveloper/SQLite.Net.Standard

Related

Phrase search on PostgreSQL 11 using Entity Framework

I've implemented Full Text on PostgreSQL 11 using an API made with .NET Core 3 and Entity Framework Core 3.
I'm able to filter with a single word, but as soon as I use two or more words it doesn't work anymore.
I've tried using the proximity operator <->, using also 10~20 words distance, like <10> between each word, but it doesn't work, and I don't get any result back.
Here is the code:
public async Task<List<QuestionListItemSearch>> SearchQuestions(string search)
{
search = search.Replace(" ", "<20>");
return await (from uiEnt in base.context.UserInfos
join efq in base.context.ExpertsForumQuestions on uiEnt.UserInfoId equals efq.UserInfoId
join efa in base.context.ExpertsForumAnswers on efq.ExpertsForumQuestionId equals efa.ExpertsForumQuestionId into ExpertsForum
from n in ExpertsForum.DefaultIfEmpty()
where efq.SearchVector.Matches(search)
group new { uiEnt, efq, n } by new {
uiEnt.FirstName,
uiEnt.LastName,
efq.ExpertsForumQuestionId,
efq.Title,
efq.Description,
efq.SubmissionDate,
SearchRanking = efq.SearchVector.Rank(EF.Functions.ToTsQuery(search)) } into newGroup
select new QuestionListItemSearch
{
ExpertsForumQuestionId = newGroup.Key.ExpertsForumQuestionId,
Title = newGroup.Key.Title,
Description = newGroup.Key.Description,
SubmissionDate = newGroup.Key.SubmissionDate,
FirstNameSubmitter = newGroup.Key.FirstName,
LastNameSubmitter = newGroup.Key.LastName,
NumberOfAnswers = newGroup.Max(x => x.n.ExpertsForumAnswerId) == null ? 0 : newGroup.Count(),
SearchRanking = newGroup.Key.SearchRanking
}).OrderByDescending(q => q.SearchRanking).ToListAsync();
}
I've tried directly the query to PostgreSQL using SQL but I can't get any result:
SELECT "Title", "SearchVector", ts_rank(c."SearchVector", to_tsquery('crop<10>rotation')) FROM PUBLIC."ExpertsForumQuestions" c
WHERE (c."SearchVector" ## to_tsquery('crop<10>rotation'))
Please note that the database has several records with both crop and rotation words in the text. There is also a record containing the two words close to each other "crop rotation"
Using the operator | instead of the proximity operator it works.
Is there a simple way to perform a phase search like in Microsoft SQL Server?

IPP .NET c# SDK allows query filters, but this does not reduce payload?

Referencing the following IPP Documentation:
https://developer.intuit.com/docs/0025_quickbooksapi/0055_devkits/0150_ipp_.net_devkit_3.0/query_filters
I made the assumption that the following code using the Linq Extentions Projection would alter the request and reduce the payload of the response by only querying for the requested fields and only including those fields (narrow result set) in the response:
public List<ShortAccount> GetFullShortAccountList(bool logRequestResponse)
{
var accounts = new List<ShortAccount>();
var accountQueryService = new QueryService<Account>
(GetIppServiceContext(logRequestResponse));
var selected = accountQueryService.Select(a => new { a.Id, a.Name });
foreach (var account in selected)
{
accounts.Add(new ShortAccount { Id = account.Id, Name = account.Name });
}
return accounts;
}
Now the behavior of this method is as expected, but if I look at the request/response logs (or the actual request and response using Fiddler) the request doesn't change -- it is still "Select * from Account", and the response still includes all the other properties in the Account entity.
In other words, the payload is not reduced one iota.
Am I doing something wrong here? Or do I just understand this incorrectly?
How can I use the SDK to generate a query that would look more like "Select Id, Name from Account", and only return that result set?
Related question -- if this mode of query filtering does not reduce the payload, what is its purpose? You might as well get the whole shebang and just take the fields you need?
Thanks in advance.
That's right #Barrick. The implementation of our query providers is not exactly the same as the standard LINQ. So, Stephan, that's the issue.
If you just want to get specific fields I would suggest you to use IDSQuery like:
QueryService<Account> AccQueryService22 = new QueryService<Account>(context);
var t13 = AccQueryService22.ExecuteIdsQuery("Select Id, Name From Account Where Active in (true, false)");
I will forward the feedback to our team.
Thanks!

Dapper parameters not working

I'm trying to use the Dapper orm with the following simple query:
var sqlString = new StringBuilder();
sqlString.Append("select a.acct AccountNumber,");
sqlString.Append(" b.first_name FirstName,");
sqlString.Append(" b.last_name LastName,");
sqlString.Append(" a.rr RrNumber,");
sqlString.Append(" c.addr1 AddressLine1,");
sqlString.Append(" c.addr2 AddressLine2,");
sqlString.Append(" c.addr3 AddressLine3,");
sqlString.Append(" c.addr4 AddressLine4,");
sqlString.Append(" c.addr5 AddressLine5,");
sqlString.Append(" c.addr6 AddressLine6,");
sqlString.Append(" c.addr7 AddressLine7,");
sqlString.Append(" c.addr8 AddressLine8 ");
sqlString.Append("from (pub.mfclac as a left join pub.mfcl as b on a.client=b.client) ");
sqlString.Append("left join pub.mfclad as c on a.client=c.client ");
sqlString.Append("where a.acct = '#ZYX'");
var connection = new OdbcConnection(_connectionString);
var result = connection.Query(sqlString.ToString(),
new
{
ZYX = accountNumber
});
However when I execute this with an accountNumber known to exist, dapper returns nothing. So I tried to remove the quotes to verify that the parameter is in fact being replaced with the account number, however the error being returned from the server indicates a syntax error around "#ZYX". Which means dapper is not replacing the parameter with it's given value. Any ideas why this is happening? From the limited documentation out there, this should 'just work'.
Edit1
Couldn't get this to work. Using string.format to insert the parameter as a work around.
There are two issues here; firstly (although you note this in your question) where a.acct = '#ZYX', under SQL rules, does not make use of any parameter - it looks to match the literal string that happens to include an # sign. For SQL-Server (see note below), the correct usage would be where a.acct = #ZYX.
However! Since you are use OdbcConnection, named parameters do not apply. If you are actually connecting to something like SQL-Server, I would strongly recommend using the pure ADO.NET clients, which have better features and performance than ODBC. However, if ODBC is your only option: it does not use named parameters. Until a few days ago, this would have represented a major problem, but as per Passing query parameters in Dapper using OleDb, the code (but not yet the NuGet package) now supports ODBC. If you build from source (or wait for the next release), you should be able to use:
...
where a.acct = ?
in your command, and:
var result = connection.Query(sqlString.ToString(),
new {
anythingYouLike = accountNumber
});
Note that the name (anythingYouLike) is not used by ODBC, so can be... anything you like. In a more complex scenario, for example:
.Execute(sql, new { id = 123, name = "abc", when = DateTime.Now });
dapper uses some knowledge of how anonymous types are implemented to understand the original order of the values, so that they are added to the command in the correct sequence (id, name, when).
One final observation:
Which means dapper is not replacing the parameter with it's given value.
Dapper never replaces parameters with their given value. That is simply not the correct way to parameterize sql: the parameters are usually sent separately, ensuring:
there is no SQL injection risk
maximum query plan re-use
no issues of formatting
Note that some ADO.NET / ODBC providers could theoretically choose to implement things internally via replacement - but that is separate to dapper.
I landed here from dublicate question: Dapper must declare the scalar variable
Error: Must declare the scalar variable "#Name".
I created queries dynamically with this piece of code:
public static bool Insert<T>(T entity)
{
var tableName = entity.GetType().CustomAttributes.FirstOrDefault(x => x.AttributeType.Name == nameof(TableAttribute))?.ConstructorArguments?.FirstOrDefault().Value as string;
if (string.IsNullOrEmpty(tableName))
throw new Exception($"Cannot save {entity.GetType().Name}. Database models should have [Table(\"tablename\")] attribute.");
DBSchema.TryGetValue(tableName.ToLower(), out var fields);
using (var con = new SqlConnection(ConnectionString))
{
con.Open();
var sql = $"INSERT INTO [{tableName}] (";
foreach (var field in fields.Where(x => x != "id"))
{
sql += $"[{field}]"+",";
}
sql = sql.TrimEnd(',');
sql += ")";
sql += " VALUES (";
foreach (var field in fields.Where(x => x != "id"))
{
sql += "#"+field + ",";
}
sql = sql.TrimEnd(',');
sql += ")";
var affectedRows = con.Execute(sql, entity);
return affectedRows > 0;
}
}
And I got the same error when my models was like this:
[Table("Users")]
public class User
{
public string Name;
public string Age;
}
I changed them to this:
[Table("Users")]
public class User
{
public string Name { get; set; }
public string Age { get; set; }
}
And it solved the problem for me.

MongoDB: update only specific fields

I am trying to update a row in a (typed) MongoDB collection with the C# driver. When handling data of that particular collection of type MongoCollection<User>, I tend to avoid retrieving sensitive data from the collection (salt, password hash, etc.)
Now I am trying to update a User instance. However, I never actually retrieved sensitive data in the first place, so I guess this data would be default(byte[]) in the retrieved model instance (as far as I can tell) before I apply modifications and submit the new data to the collection.
Maybe I am overseeing something trivial in the MongoDB C# driver how I can use MongoCollection<T>.Save(T item) without updating specific properties such as User.PasswordHash or User.PasswordSalt? Should I retrieve the full record first, update "safe" properties there, and write it back? Or is there a fancy option to exclude certain fields from the update?
Thanks in advance
Save(someValue) is for the case where you want the resulting record to be or become the full object (someValue) you passed in.
You can use
var query = Query.EQ("_id","123");
var sortBy = SortBy.Null;
var update = Update.Inc("LoginCount",1).Set("LastLogin",DateTime.UtcNow); // some update, you can chain a series of update commands here
MongoCollection<User>.FindAndModify(query,sortby,update);
method.
Using FindAndModify you can specify exactly which fields in an existing record to change and leave the rest alone.
You can see an example here.
The only thing you need from the existing record would be its _id, the 2 secret fields need not be loaded or ever mapped back into your POCO object.
It´s possible to add more criterias in the Where-statement. Like this:
var db = ReferenceTreeDb.Database;
var packageCol = db.GetCollection<Package>("dotnetpackage");
var filter = Builders<Package>.Filter.Where(_ => _.packageName == packageItem.PackageName.ToLower() && _.isLatestVersion);
var update = Builders<Package>.Update.Set(_ => _.isLatestVersion, false);
var options = new FindOneAndUpdateOptions<Package>();
packageCol.FindOneAndUpdate(filter, update, options);
Had the same problem and since I wanted to have 1 generic method for all types and didn't want to create my own implementation using Reflection, I end up with the following generic solution (simplified to show all in one method):
Task<bool> Update(string Id, T item)
{
var serializerSettings = new JsonSerializerSettings()
{
NullValueHandling = NullValueHandling.Ignore,
DefaultValueHandling = DefaultValueHandling.Ignore
};
var bson = new BsonDocument() { { "$set", BsonDocument.Parse(JsonConvert.SerializeObject(item, serializerSettings)) } };
await database.GetCollection<T>(collectionName).UpdateOneAsync(Builders<T>.Filter.Eq("Id", Id), bson);
}
Notes:
Make sure all fields that must not update are set to default value.
If you need to set field to default value, you need to either use DefaultValueHandling.Include, or write custom method for that update
When performance matters, write custom update methods using Builders<T>.Update
P.S.: It's obviously should have been implemented by MongoDB .Net Driver, however I couldn't find it anywhere in the docs, maybe I just looked the wrong way.
Well there are many ways to updated value in mongodb.
Below is one of the simplest way I choose to update a field value in mongodb collection.
public string UpdateData()
{
string data = string.Empty;
string param= "{$set: { name:'Developerrr New' } }";
string filter= "{ 'name' : 'Developerrr '}";
try
{
//******get connections values from web.config file*****
var connectionString = ConfigurationManager.AppSettings["connectionString"];
var databseName = ConfigurationManager.AppSettings["database"];
var tableName = ConfigurationManager.AppSettings["table"];
//******Connect to mongodb**********
var client = new MongoClient(connectionString);
var dataBases = client.GetDatabase(databseName);
var dataCollection = dataBases.GetCollection<BsonDocument>(tableName);
//****** convert filter and updating value to BsonDocument*******
BsonDocument filterDoc = BsonDocument.Parse(filter);
BsonDocument document = BsonDocument.Parse(param);
//********Update value using UpdateOne method*****
dataCollection.UpdateOne(filterDoc, document);
data = "Success";
}
catch (Exception err)
{
data = "Failed - " + err;
}
return data;
}
Hoping this will help you :)

Querying data from a WCF data service in Silverlight error

I have a ADO.NET model entity that reflects an oracle database and a WCF Service that provides access to this ADO.NET model. In my WCF Service code, I have the following:
config.SetEntitySetAccessRule("*", EntitySetRights.AllRead);
//config.SetServiceOperationAccessRule("MyServiceOperation",ServiceOperationRights.All);
config.DataServiceBehavior.MaxProtocolVersion = DataServiceProtocolVersion.V2;
I have added a reference to this service in my silverlight 5 application. Then in the following code, I am getting an error:
Uri WCFUri = new Uri(HtmlPage.Document.DocumentUri, "WcfDataService.svc");
WCF_Service.Entities Database = new WCF_Service.Entities(WCFUri);
var buildings = from building in Config.Database.BUILDINGs
select building.BLDG_ID;
var buildingsQuery = (DataServiceQuery<string>)buildings;
buildingsQuery.BeginExecute(buildingsResult =>
{
foreach (string buildingId in buildingsQuery.EndExecute(buildingsResult))
BuildingsList.Items.Add(new ListItem(buildingId.Trim(), false));
BuildingListBusyIndicator.IsBusy = false;
}, null);
The error is:
Navigation properties can only be selected from a single resource. Specify a key predicate to restrict the entity set to a single instance.
I am lost on what the problem is. Thanks in advance.
The problem is that ADO.NET Data Services doesn't support projection feature.
Instead of selecting properties, you must select entities.
WRONG
var buildings = from building in Config.Database.BUILDINGs
select building.BLDG_ID;
CORRECT
var buildings = from building in Config.Database.BUILDINGs
select building;
(I understand that in your case this correction pointless, but technically this is true)
OR you can use shell, for example:
var buildings = from building in Config.Database.BUILDINGs
select new { Id = building.BLDG_ID };

Categories