mongoid: select elements that have at least n elements in array - ruby-on-rails

In mongoid you can query items that have at least one element in array:
Item.any_in(tag_ids: [id1,id2,id3])
You can also select elements that have all elements in array:
Item.all_in(tag_ids: [id1,id2,id3])
My Question: Is there any way to query elements that have at least n elements in array ?
I'd like to query something like Item.at_least(tag_ids: [id1,id2,id3], n: 2) to return any Item that share at least two ids with [id1,id2,id3]
Thanks !

I don't know a pure Mongoid-solution. I also haven't found such query in the MongoDB manual: http://docs.mongodb.org/manual/reference/operator/query-array/
I would use some mix of Mongoid and array operations.
The disadvantage of it is, that all items which have at least 1 of these tags will be loaded.
searched_tag_ids = ['54253ad452656b1d25000000','54253adc52656b1d25010000','54253ae352656b1d25020000']
items_with_min_1_searched_tag = Item.any_in(tag_ids: searched_tag_ids).to_a
items_with_min_2_searched_tag = items_with_min_1_searched_tag.select{|item| (item.tag_ids.collect{|tag_id| tag_id.to_s} & searched_tag_ids).size >=2}

Related

Active Record Array array query - to check records that are present in an array

I have an Objective model which has an attribute called as labels whose values are array data type. I need to query all the Objectives whose labels attribute has values that are present in some particular array.
For Example:
I have an array
a = ["textile", "blazer"]
the Objective.labels may have values as ["textile, "ramen"]
I need to return all objectives that might have either "textile" or "blazer" as one of their labels array values
I tried the following:
Objective.where("labels #> ARRAY[?]::varchar[]", ["textile"])
This returns some records.Now when I try
Objective.where("labels #> ARRAY[?]::varchar[]", ["textile", "Blazer"])
I expect it to return all Objectives which contains at-least one of the labels array value as textile or blazer.
However, it returns an empty array. Any Solutions?
Try && overlap operator.
overlap (have elements in common)
Objective.where("labels && ARRAY[?]::varchar[]", ["textile", "Blazer"])
If you have many rows, a GIN index can speed it up.

How to remove all elements found in GROUP B to GROUP A in Lua

Let's say I have two groups defined in my Lua script
groupA = {"donkey", "goat", "eagle", "whale", "dolphine", "dog", "mosquito", ...}
groupB = {"goat", "mosquito", "donkey"}
After the remove operation, the value of groupA have no more elements: "goat", "mosquito", and "donkey"
How do I remove all items in groupA that are found in groupB. I know we can loop through the items and compare each one but I prefer any API or simple built in statements that solve this types of problem. The elements could also be any type like record.
There are no built-in operators that calculate set difference in Lua. You can do what you described and to speed up this process you can build a hash of elements from the second table and then iterate over the elements in the first table and check if they are present in the hash (of the elements in the second table).
If you end up using table.remove to remove elements from the first table while iterating, you need to be careful to iterate from the end, otherwise you may end up skipping elements you need to remove.
You can also check if some of the suggestions in this thread about set operators work for you.
local lookup = {}
for i, v in ipairs(groupB) do
lookup[v] = true
end
local answer = {}
for i, v in ipairs(groupA) do
if (not lookup[v]) then
table.insert(answer, v)
end
end
create a lookup table for the unique items in groupB
traverse groupA and lookup each item in the lookup table
add items from groupA not found in the lookup table to answer table
Note: this approach doesn't account for duplicates. For example, if groupB contains "goat" three times, and groupA contains "goat" four times, then answer will contain "goat" zero times.
After some time of researching, I found out that this simple subtraction in Lua works for groups (or table) to remove elements found in a group from another.
Ex.
groupA = groupA - groupB

Reorder elements in Erlang

I want to redefine the order of a tuple looking for specific words
Example, I have a list of tuples like this:
[{"a",["r001"]},
{"bi",["bidder"]},
{"bo",["an"]}]
But sometimes the order of the tuples can change for example:
[{"bi",["bidder"]},
{"a",["r001"]},
{"bo",["an"]}]
or
[{"bo",["an"]},
{"a",["r001"]},
{"bi",["bidder"]}]
The first string/list of the tuple is my unique key ("bo","a","bi")
But I want to be able to reorder the list of tuples, always like:
[{"a",["r001"]},
{"bi",["bidder"]},
{"bo",["an"]}]
How can I achieve this?
This will do it:
lists:sort(fun({A,_},{B,_}) -> A =< B end, List).
Or this, which will sort by the tuples second element after the first:
lists:sort(List).
I offer the second version, because without the custom sort function, it is faster for data like this.
If you need to sort by specified element, you just sort by specified element
lists:keysort(1, List).

.indexOf() equivalent in Neo4j Cypher

No matter how I swing it, I need some kind of function to find the index of a item in an array supplied as a parameter.
I am trying to simply update items in a collection based on the index of one of their properties in an array, and have been poring through Cypher docs for nearly 2 hours...
It would also be acceptable to order the items by that array, and then run a foreach on the ordered list...
Following #stefan-armbruster answer and great blog post, a slow but simple index_of can be done with:
reduce(x=[-1,0], i IN [1,2,7,5,21,5,1,435] |
CASE WHEN i = 21 THEN [x[1], x[1]+1] ELSE [x[0], x[1]+1] END
)[0]
Here reduce function works with a two elements array: the position and the current index. If an element in your array matches the given condition, the first element of the reduced array will be replaced with the current index.
I put an example on neo4j console http://console.neo4j.org/?id=34byv
I've blogged about that recently. You can use the reduce function with an three element array as state variable containing
the index of the highest occupation so far
the current index (aka iteration number)
the value of highest occupation so far
As an example to find the index of max element in an array:
RETURN reduce(x=[0,0,0], i IN [1,2,2,5,2,1] |
CASE WHEN i>x[2] THEN [x[1],x[1]+1,o] ELSE [x[0], x[1]+1,x[2]] END
)[0]

How can I find the max attribute across records in ruby?

I have several records with several attributes (A, B, C, D).
I want to be able to find which record has the higher value for a given attribute, such as D.
How do I do that?
You might give max_by a look.
objects = [some array of objects]
object_with_highest_value = objects.max_by {|obj| obj.desired_value }
Depending on how many records do you have, it can be more efficient to perform the search on the DB. I would order by the desired attribute descending, and take the first record:
User.order('field DESC').first

Resources