Rails 4 and Mongoid: programmatically build query to search for different conditions on the same field - ruby

I'm building a advanced search functionality and, thanks to the help of some ruby fellows on SO, I've been already able to combine AND and OR conditions programmatically on different fields of the same class.
I ended up writing something similar to the accepted answer mentioned above, which I report here:
query = criteria.each_with_object({}) do |(field, values), query|
field = field.in if(values.is_a?(Array))
query[field] = values
end
MyClass.where(query)
Now, what might happen is that someone wants to search on a certain field with multiple criteria, something like:
"all the users where names contains 'abc' but not contains 'def'"
How would you write the query above?
Please note that I already have the regexes to do what I want to (see below), my question is mainly on how to combine them together.
#contains
Regex.new('.*' + val + '.*')
#not contains
Regex.new('^((?!'+ val +').)*$')
Thanks for your time!
* UPDATE *
I was playing with the console and this is working:
MyClass.where(name: /.*abc.*/).and(name: /^((?!def).)*$/)
My question remains: how do I do that programmatically? I shouldn't end up with more than two conditions on the same field but it's something I can't be sure of.

You could use an :$and operator to combine the individual queries:
MyClass.where(:$and => [
{ name: /.*abc.*/ },
{ name: /^((?!def).)*$/ }
])
That would change the overall query builder to something like this:
components = criteria.map do |field, value|
field = field.in if(value.is_a?(Array))
{ field => value }
end
query = components.length > 1 ? { :$and => components } : components.first
You build a list of the individual components and then, at the end, either combine them with :$and or, if there aren't enough components for :$and, just unwrap the single component and call that your query.

Related

How to properly perform like queries with Quickbase

I am working with quicktable queries and everything seems to be fine.
Now I want to perform queries using like operators. For instance in PHP I can do something like:
$data ='content to search';
$stmt = $db->prepare('SELECT * FROM members where name like :name OR email like :email limit 20');
$stmt->execute(array(
':name' => '%'.$data.'%',
':email' => '%'.$data.'%',
));
Now in quick table, I have tried using CT, EX or HAS parameter etc with OR Operators. Only CT gives nearby result but not exact as per code below.
//Email = 7
//name =8
{
"from": "tableId",
"where": "{7.CT.'nancy#gmail.com'}OR{8.CT.'nancy'}"
}
Is there any way I can obtain a better search with like operators with Quickbase. The documentation here does not cover that.
CT is the closest string comparison operator in Quick Base to LIKE in SQL, but since you can't use wildcards in Quick Base queries you might need to group multiple query strings to achieve the same result. The is also a SW operator that can sometimes come in helpful for comparing parts of a strings.

Common part in associations rails, should I use merge?

Update 2
Cama::PostType.first.posts.joins(:custom_field_values)
.where("cama_custom_fields_relationships.custom_field_slug = ? AND
cama_custom_fields_relationships.value LIKE ?","localization",
"%Paris%").merge(Cama::PostType.first.posts.joins(:custom_field_values)
.where("cama_custom_fields_relationships.custom_field_slug = ? AND cama_custom_fields_relationships.value = ?","type-localization", "2"))
Why this merge doesn't work ?
It returns me same result when executed seperately... Merge should work as intersection so common part should be result. I dont get it
Update
I will try to ask in more conceptual way.
I have model B that have slug:text, value:text, belongs_to: Model A
I have model A that have name:string, has_many: Model B
#posts_one = I search for model B where slug="something", value = "city"
#posts_two = I search for model B where slug="mood", value="good"
I have 2 results based on diffrent parameters. Both belongs_to: model A
Now I want to return only the common belongs_to.
so if
#posts_one will return me 20 results with model_a_ids
#posts_two will return me 20 results with model_a_ids
I want to return only common model_a_ids of those 2 queries and right away to find posts. I try to make it in one query but dont know if its possible
Oryginal post
I use Camaleon CMS and I try to create filters based on additional "custom fields". I think to answer this question you dont have to know this cms.
I want to find common part of 2 queries or make it in one query(that would be the best)
I have
#posts = Cama::PostType.first.posts.includes(:custom_field_values)
#param_localization = "Paris"
#param_type_localization = "House"
#posts_one = #posts.merge(CamaleonCms::CustomFieldsRelationship.
where("cama_custom_fields_relationships.custom_field_slug = ? AND
LOWER(cama_custom_fields_relationships.value) LIKE ?", "localization",
"%#{#param_localization}%"))
puts #posts_one.count => 2
#posts_two = #posts.merge(CamaleonCms::CustomFieldsRelationship.where(custom_field_slug:
"type-localization", value: #param_type_localization))
puts #posts_two.count => 2
Question is how can I merge it together or make it one query ? When I made it in one where clause it returns me 0 results since I need to find 2 diffrent custom fields relationships that has diffrent values and slugs but it have relations to posts throught :custom_fields_values, so I have to make 2 queries I guess(like I did). First I find customFieldRelationship with slug = localization and second with slug = type_localization and then I need to find common part
I tried to #result = #posts_one.merge(#posts_two) but I got no result then. I thought it will return me "common part" of association which means 2 results
How can I combine it to find me posts that fullfil both queries ?
Let me know if I explained my problem not well enought.
You'll want to combine it in SQL: (untested)
#posts_combined = #posts.merge(CamaleonCms::CustomFieldsRelationship.
where("(cama_custom_fields_relationships.custom_field_slug = ?
OR cama_custom_fields_relationships.custom_field_slug = 'type-localization')
AND LOWER(cama_custom_fields_relationships.value) LIKE ? ", "localization",
"%#{#param_localization}%"))

Grails Where Query With Multiple Sort Fields

I can't find an example in the doc and I would prefer to use the nifty "where" functionality instead of the "criteria builder" functionality.
I have my "where" query:
def query = Nhrdist.where
{
nhrdistPidm == pidm
nhrdistFundCode == fundCode
nhrdistCategoryCode == 'A'
nhrdistAcctCode in ['20L0', '20L2', '20L3', '20L4', '20L5']
nhrdistTransDate in fiscalYearBeginDate..fiscalYearEndDate
}
And the following works when I sort by one field:
def results = query.list(sort:'nhrdistAcctCode')
But I cannot for the life of me figure out how to sort by two fields. I would like to sort by nhrdisAcctCode first, and then sort by nhrdistTransDate. I can't find any examples on http://grails.org/doc/2.3.x/guide/GORM.html#whereQueries
Can someone:
Provide me an example that shows how to sort with multiple fields OR
Belittle me and point out in the doc where it says I can't do this OR
Belittle me and give me the link to a StackOverflow page that answers my question OR
Tell me to quit my whining and just use a Criteria Builder query.
def results = query.order( 'nhrdistAcctCode' )
.order( 'nhrdistTransDate', 'desc' ) // or asc
.list()
should do it.
Here order() is called on DetachedCriteria ( where query ) as a builder pattern.

multiple levels of associated db objects to YAML

I need to create a 'List' object from the following db tables. I've already done this in a rails/datamapper application, but now I have a need to get specific lists into and out of a db through YAML.
List
Categories
Items
Item choices
e.g. given a list identifier, pull the initial list, the categories for that list, the items for those categories, and the choices for those items into some object, then output as a yaml file.
My first step is output a specific list to yaml, this shouldn't be a unique situation and I'm sure others have solved it before. From reading I'm guessing I need a multilevel hash of some sort, but all I've been able to do so far is get list and category...i.e. this is a bit out of my range right now, and I'm only working from the command line.
I'm asking for two things really to assist in sharpening my skill set:
guidance on working with a multiple level, nested hash situation to properly serialize an object for yaml, given a series of associated db tables
if there is an easier way that someone has already solved.
The included to_json (doc) method already allows you to easily nest related records, and choose what you want to output :
List.all.to_json(:only => {}, :include => {
:categories => { :only => {}, :include => {
:items => { :only => :your_attribute_name }
}
})
The next step is to convert it to yaml :
ActiveSupport::JSON.decode(your_json).to_yaml
Hope this helps

LINQ Query to find all tags?

I have an application that manages documents called Notes. Like a blog, Notes can be searched for matches against one or more Tags, which are contained in a Note.Tags collection property. A Tag has Name and ID properties, and matches are made against the ID. A user can specify multiple tags to match against, in which case a Note must contain all Tags specified to match.
I have a very complex LINQ query to perform a Note search, with extension methods and looping. Quite frankly, it has a real code smell to it. I want to rewrite the query with something much simpler. I know that if I made the Tag a simple string, I could use something like this:
var matchingNotes = from n in myNotes
where n.Tags.All(tag => searchTags.Contains(tag))
Can I do something that simple if my model uses a Tag object with an ID? What would the query look like. Could it be written in fluent syntax? what would that look like?
I believe you can find notes that have the relevant tags in a single LINQ expression:
IQueryable<Note> query = ... // top part of query
query = query.Where(note => searchTags.All(st =>
note.Tags.Any(notetag => notetag.Id == st.Id)));
Unfortunately there is no “fluent syntax” equivalent for All and Any, so the best you can do there is
query = from note in query
where searchTags.All(st =>
note.Tags.Any(notetag => notetag.Id == st.Id))
select note;
which is not that much better either.
For starters see my comment; I suspect the query is wrong anyway! I would simplifiy it, by simply enforcing separately that each tag exists:
IQueryable<Note> query = ... // top part of query
foreach(var tagId in searchTagIds) {
var tmpId = tagId; // modified closures...
query = query.Where(note => note.Tags.Any(t => t.Id == tmpId));
}
This should have the net effect of enforcing all the tags specified are present and accounted for.
Timwi's solution works in most dialects of LINQ, but not in Linq to Entities. I did find a single-statement LINQ query that works, courtesy of ReSharper. Basically, I wrote a foreach block to do the search, and ReSharper offered to convert the block to a LINQ statement--I had no idea it could do this.
I let ReSharper perform the conversion, and here is what it gave me:
return searchTags.Aggregate<Tag, IQueryable<Note>>(DataStore.ObjectContext.Notes, (current, tag) => current.Where(n => n.Tags.Any(t => t.Id == tag.Id)).OrderBy(n => n.Title));
I read my Notes collection from a database, using Entity Framework 4. DataStore is the custom class I use to manage my EF4 connection; it holds the EF4 ObjectContext as a property.

Resources