Does Coffeescript support nested list comprehensions? - coffeescript

For example, given the following structure
pages = [
[
{ myvar: 1},
{ myvar: 2},
{ myvar: 3},
]
];
How can I express the folling (python-like) comprehension?
v.myvar for p in pages for v in p

AFAIK, you can not, see: https://github.com/jashkenas/coffee-script/issues/1191
A workaround in the meantime (until CoffeeScript gets improved):
pages = [
[
{ myvar: 1},
{ myvar: 2},
{ myvar: 3},
]
];
result = []
for row in pages
for map in row
result.push map.myvar
console.log result
which outputs:
[ 1, 2, 3 ]

Related

Fetch a certain amount of array using Mongoose and Mongo [duplicate]

Let's say I've got a collection People in mongo database:
[{
id: 1,
name: "Tom",
animals: ["cat", "dog", "fish", "bear"]
},
{
id: 2,
name: "Rob",
animals: ["shark", "snake", "fish", "bear", "panda"]
},
{
id: 3,
name: "Matt",
animals: ["cat", "fish", "bear"]
}]
For the purpose of REST API I need to create a pagination system for viewing people's animals and return only 3 per request. So for example if you go to /people/2the API should return this array:
["shark", "snake", "fish"]
I'm trying to get this result using Mongo methods. Here's my attempt:
db.getCollection('people').find({id: 2}, {animals: 1, _id:0}, {limit: 3})
Unfortunatelly it doesn't work like that and returns the whole object. Can anybody tell me how to do it?
For you problem you need the $slice projection operator instead of limit. The later limits the number of documents returned as a result of the query. Instead, the $slice operator is intended for exactly what you need.
Here is an example how to use it in your use case:
> db.getCollection('people').find({id: 2}, {_id: 0, animals: {$slice: [0, 3]}})
{
"id" : 2,
"name" : "Rob",
"animals" : [
"shark",
"snake",
"fish"
]
}

C# mongodb - how to update nested array elements

I have the following JSON structure that represents an item
{
Id: "a",
Array1: [{
Id: "b",
Array2: [{
Id: "c",
Array3: [
{...}
]
}]
}]
}
I need to be able to either replace the array element in Array2 with a new item or to replace just Array3 with a new array.
Here is my code to replace the array item in Array2:
await Collection.UpdateOneAsync(
item => item.Id.Equals("a") &&
item.Array1.Any(a => a.Id.Equals("b")) &&
item.Array1[-1].Array2.Any(b => b.Id.Equals("c")),
Builders<Item>.Update.Set(s => s.Array1[-1].Array2[-1], newArray2Item)
);
When executing this code I'm getting this error:
"A write operation resulted in an error.
Too many positional (i.e. '$') elements found in path 'Array1.$.Array2.$'"
Here is my code to replace Array3 within Array2:
await Collection.UpdateOneAsync(
item => item.Id.Equals("a") &&
item.Array1.Any(a => a.Id.Equals("b")) &&
item.Array1[-1].Array2.Any(b => b.Id.Equals("c")),
Builders<Item>.Update.Set(s => s.Array1[-1].Array2[-1].Array3, newArray3)
);
And this is the error:
"A write operation resulted in an error.
Too many positional (i.e. '$') elements found in path 'Array1.$.Array2.$.Array3'"
I'm using C# MongoDB driver version 2.5.0 and MongoDB version 3.6.1
I found this Jira ticket Positional Operator Matching Nested Arrays that says the problem was fixed and they suggested this syntax for the update
Update all matching documents in nested array:
db.coll.update({}, {$set: {“a.$[i].c.$[j].d”: 2}}, {arrayFilters: [{“i.b”: 0}, {“j.d”: 0}]})
Input: {a: [{b: 0, c: [{d: 0}, {d: 1}]}, {b: 1, c: [{d: 0}, {d: 1}]}]}
Output: {a: [{b: 0, c: [{d: 2}, {d: 1}]}, {b: 1, c: [{d: 0}, {d: 1}]}]}
So I converted it to my elements:
db.getCollection('Items').update(
{"Id": "a"},
{$set: {"Array1.$[i].Array2.$[j].Array3": [newArray3]}},
{arrayFilters:
[
{"i.Id": "b"},
{"j.Id": "c"}
]}
)
And got this error:
cannot use the part (Array1 of Array.$[i].Array2.$[j].Array3) to traverse the element
Any ideas on how to solve this error?
Here's the C# version of what you need:
var filter = Builders<Item>.Filter.Eq("Id", "a");
var update = Builders<Item>.Update.Set("Array1.$[i].Array2.$[j].Array3", new[] { new Item { Id = "d" } });
var arrayFilters = new List<ArrayFilterDefinition> { new JsonArrayFilterDefinition<Item>("{'i.Id': 'b'}"), new JsonArrayFilterDefinition<Item>("{'j.Id': 'c'}") };
var updateOptions = new UpdateOptions { ArrayFilters = arrayFilters };
collection.UpdateOne(filter, update, updateOptions);

mongodb: same field value, but cannot find the specific document using find() function

Put my question more clear: I am using mongodb, each document in the collection looks like:
{
u'cell_latlng': {
u'type': u'Polygon',
u'coordinates': [
[
[
...
],
[
...
],
[
...
],
[
...
]
]
]
},
u'self': {
u'Bus Line': 1,
u'Meeting Room': 1,
u'Neighborhood': 1,
u'Home (private)': 1,
u'Food': 1,
u'High School': 2,
u'Elementary School': 1,
u'Beach': 1,
u'Other Repair Shop': 1
},
u'_id': ObjectId('5545bed4e139c7dcde660f5a'),
u'point_latlng': {
u'type': u'Point',
u'coordinates': [
...
]
},
u'area': 100
}
Now I need to find a specific doc in the collection (the collection is called citymap). I know the value of the field 'self' and the value is called feature[0], so I just use:
results = citymap.find({'self':feature[0]})
However,
results.count() == 0
I check the feature[0] again and I am sure it is:
feature[0] = {u'Bus Line': 1, u'Meeting Room': 1, u'Neighborhood': 1, u'Home (private)': 1, u'Food': 1, u'High School': 2, u'Elementary School': 1, u'Beach': 1, u'Other Repair Shop': 1}
which is exactly the same as that in the doc, so why cannot I find this doc using this sentence?
Great thanks to any help
Let's check this script. Suppose your feature object like this
var feature = {u'Bus Line': 1, u'Meeting Room': 1, u'Neighborhood': 1, u'Home (private)': 1, u'Food': 1, u'High School': 2, u'Elementary School': 1, u'Beach': 1, u'Other Repair Shop': 1}
Iterate over feature object and append every keys as self and push them into new object. Check below function:
var appendSelf = {};
function appendData(data) {
for (var k in data) {
if (data.hasOwnProperty(k)) {
appendSelf["self.".concat(k)] = data[k]; //append self to every keys and set it's respective values
}
}
}
Now call this function as appendData(feature) on console if you type appendSelf then it shows object with appending self like this
> appendSelf
{
"self.u'Bus Line": 1,
"self.u'Meeting Room": 1,
"self.u'Neighborhood": 1,
"self.u'Home (private)": 1,
"self.u'Food": 1,
"self.u'High School": 4,
"self.u'Elementary School": 1,
"self.u'Beach": 1,
"self.u'Other Repair Shop": 1
}
So use this appendSelf in find criteria as citymap.find(appendSelf[0]).pretty() it show expected result.

Unique Index on Array in MongoDB

Say we have a collection of documents similar to this:
{
foo: "Bar",
foos: [1, 2, 3]
}
I would like to define a unique index such that no document identical to this one can be inserted into the database.
db.stuffs.ensureIndex({ foos: 1 }, { unique: true })
Seems to block any document containing a foos array with any intersection, eg. if the document above was already in the database, then
{
foo: "Bar",
foos: [ 1 ]
}
Would also be blocked.
> db.stuffs.ensureIndex({ foos: 1 }, { unique: true })
> db.stuffs.insert({ foo: "Bar", foos: [ 1, 2, 3 ]})
> db.stuffs.insert({ foo: "Bar", foos: [ 1 ]})
E11000 duplicate key error index: test.stuffs.$foos_1 dup key: { : 1.0 }
I would like to be able to make insertions of [ 1, 2 ], [ 2, 1 ], [ 1, 3 ], etc. but not two [ 1, 2 ]
The array index will not meet your requirement. But I think you can switch to the other format to store your data.
If there is no need to use the feature of array (such as $addToSet, $push op), you can simply hash/map your data to another format. e.g.: [1,2,3] to the string "1,2,3".
While I assume that you want to remain the array operations in order to make some updates. Then you can try the subdocument below:
db.stuffs.ensureIndex({ foos: 1 }, { unique: true }) // build the index for the sub doc
db.stuffs.insert({ foo: "Bar", foos: {k:[ 1, 2, 3 ]}})
db.stuffs.insert({ foo: "Bar", foos: {k:[ 1 ]}})
db.stuffs.update({ "_id" : ObjectId("54081f544ea4d4e96bffd9ad")}, {$push:{"foos.k": 2}})
db.stuffs.insert({ foo: "Bar", foos: {k:[1, 2]}})
E11000 duplicate key error index: test.stuffs.$foos_1 dup key: { : { k: [ 1.0, 2.0 ] } }
Please refer to this question. Explains why Unique Index won't work in this case.
Unique index in MongoDB
To index a field that holds an array value, MongoDB creates an index
key for each element in the array.
If the array that you want to index is of constant size, you can create a unique multikey index like this:
collection.createIndex({"coordinate.0": 1, "coordinate.1": 1}, {unique: true})
coordinate is an array of size 2.
When I try to insert a duplicate coordinate, it returns an error as expected.

mongoid search in an array inside an array of hash

Say Object embeds_many searched_items
Here is the document:
{"_id": { "$oid" : "5320028b6d756e1981460000" },
"searched_items": [
{
"_id": { "$oid" : "5320028b6d756e1981470000" },
"hotel_id": 127,
"room_info": [
{
"price": 10,
"amenity_ids": [
1,
2
]
},
{
"price": 160,
"amenity_ids": null
}
]
},
{
"_id": { "$oid" : "5320028b6d756e1981480000" },
"hotel_id": 161,
"room_info": [
{
"price": 400,
"amenity_ids": [4,5]
}
]
}
]
}
I want to find the "searched_items" having room_info.amenity_ids IN [2,3].
I've tried
object.searched_items.where('room_info.amenity_ids' => [2, 3])
object.searched_items.where('room_info.amenity_ids' =>{'$in' => [2,3]}
with no luck
mongoid provides elem_match method for searching within objects of Array Type
e.g.
class A
include Mongoid::Document
field :some_field, type: Array
end
A.create(some_field: [{id: 'a', name: 'b'}, {id: 'c', name: 'd'}])
A.elem_match(some_field: { :id.in=> ["a", "c"] }) => will return the object
Let me know if you have any other doubts.
update
class SearchedHotel
include Mongoid::Document
field :hotel_id, type: String
field :room_info, type: Array
end
SearchedHotel.create(hotel_id: "1", room_info: [{id: 1, amenity_ids: [1,2], price: 600},{id: 2, amenity_ids: [1,2,3], price: 1000}])
SearchedHotel.create(hotel_id: "2", room_info: [{id: 3, amenity_ids: [1,2], price: 600}])
SearchedHotel.elem_match(room_info: {:amenity_ids.in => [1,2]})
Mongoid::Criteria
selector: {"room_info"=>{"$elemMatch"=>{"amenity_ids"=>{"$in"=>[1, 2]}}}}
options: {}
class: SearchedHotel
embedded: false
And it returns both the records. Am I missing something from your question/requirement. If yes, do let me know.
It's important to distinguish between top-level queries sent to the MongoDB server and
client-side operations on embedded-documents that are implemented by Mongoid.
This is the underlying confusion between the original question and the answer from #sandeep-kumar and associated comments.
The original question is all about the where clause on embedded documents after the query result has already been fetched.
The answer #sandeep-kumar and comments are all about top-level queries.
The following test covers both, showing how answers from #sandeep-kumar do work on the examples in your comments,
and also what does and does not work on your original question.
To summarize, Sandeep's answers do work for top-level queries.
Please review your code, if there are remaining problems, please post the exact Ruby code that summarizes the problem.
For your original question, please note that "object" has already been fetched from MongoDB,
and that you can verify this by looking at the log/test.log file.
The subsequent "where" operations are all client-side execution by Mongoid.
Simple "where" clauses do work at the embedded document level.
Complex "where" clauses involving nested array values don't seem to work -
I didn't really expect Mongoid to reimplement '$in' on the client-side.
Knowing that the "object" already has the query result,
and that the association "searched_items" gives you convenient access to the embedded documents,
you can write Ruby code to select what you want as in the following test.
Hope that this helps.
test/unit/my_object_test.rb
require 'test_helper'
require 'pp'
class MyObjectTest < ActiveSupport::TestCase
def setup
MyObject.delete_all
A.delete_all
SearchedHotel.delete_all
end
test "original question with client-side where operation on embedded documents" do
doc = {"_id"=>{"$oid"=>"5320028b6d756e1981460000"}, "searched_items"=>[{"_id"=>{"$oid"=>"5320028b6d756e1981470000"}, "hotel_id"=>127, "room_info"=>[{"price"=>10, "amenity_ids"=>[1, 2]}, {"price"=>160, "amenity_ids"=>nil}]}, {"_id"=>{"$oid"=>"5320028b6d756e1981480000"}, "hotel_id"=>161, "room_info"=>[{"price"=>400, "amenity_ids"=>[4, 5]}]}]}
MyObject.create(doc)
puts
object = MyObject.first
<<-EOT.split("\n").each{|line| puts "#{line}:"; eval "pp #{line}"}
object.searched_items.where('hotel_id' => 127).to_a
object.searched_items.where(:hotel_id.in => [127,128]).to_a
object.searched_items.where('room_info.amenity_ids' => {'$in' => [2,3]}).to_a
object.searched_items.where('room_info.amenity_ids'.to_sym.in => [2,3]).to_a
object.searched_items.select{|searched_item| searched_item.room_info.any?{|room_info| room_info['amenity_ids'] && !(room_info['amenity_ids'] & [2,3]).empty?}}.to_a
EOT
end
test "A comment - top-level queries" do
A.create(some_field: [{id: 'a', name: 'b', tag_ids: [6,7,8]}, {id: 'c', name: 'd'}, tag_ids: [5,6,7]])
A.create(some_field: [{id: 'a', name: 'b', tag_ids: [1,2,3]}, {id: 'c', name: 'd'}, tag_ids: [2,3,4]])
puts
pp A.where('some_field.tag_ids'.to_sym.in => [2,3]).to_a
pp A.elem_match(some_field: { :tag_ids.in => [2,3,4] }).to_a
end
test "SearchedHotel comment - top-level query" do
s = <<-EOT
[#<SearchedHotel _id: 53253c246d756e49a7030000, hotel_id: \"1\", room_info: [{\"id\"=>1, \"amenity_ids\"=>[1, 2], \"price\"=>600}, {\"id\"=>2, \"amenity_ids\"=>[1, 2, 3], \"price\"=>1000}]>, #<SearchedHotel _id: 53253c246d756e49a7040000, hotel_id: \"2\", room_info: [{\"id\"=>3, \"amenity_ids\"=>[1, 2], \"price\"=>600}]>]
EOT
a = eval(s.gsub('#<SearchedHotel ', '{').gsub(/>,/, '},').gsub(/>\]/, '}]').gsub(/_id: \h+, /, ''))
SearchedHotel.create(a)
puts
<<-EOT.split("\n").each{|line| puts "#{line}:"; eval "pp #{line}"}
SearchedHotel.elem_match(room_info: {:amenity_ids.in => [1,2]}).to_a
EOT
end
end
$ ruby -Ilib -Itest test/unit/my_object_test.rb
Run options:
# Running tests:
[1/3] MyObjectTest#test_A_comment_-_top-level_queries
[#<A _id: 5359329d7f11ba034b000002, some_field: [{"id"=>"a", "name"=>"b", "tag_ids"=>[1, 2, 3]}, {"id"=>"c", "name"=>"d"}, {"tag_ids"=>[2, 3, 4]}]>]
[#<A _id: 5359329d7f11ba034b000002, some_field: [{"id"=>"a", "name"=>"b", "tag_ids"=>[1, 2, 3]}, {"id"=>"c", "name"=>"d"}, {"tag_ids"=>[2, 3, 4]}]>]
[2/3] MyObjectTest#test_SearchedHotel_comment_-_top-level_query
SearchedHotel.elem_match(room_info: {:amenity_ids.in => [1,2]}).to_a:
[#<SearchedHotel _id: 5359329d7f11ba034b000003, hotel_id: "1", room_info: [{"id"=>1, "amenity_ids"=>[1, 2], "price"=>600}, {"id"=>2, "amenity_ids"=>[1, 2, 3], "price"=>1000}]>,
#<SearchedHotel _id: 5359329d7f11ba034b000004, hotel_id: "2", room_info: [{"id"=>3, "amenity_ids"=>[1, 2], "price"=>600}]>]
[3/3] MyObjectTest#test_original_question_with_client-side_where_operation_on_embedded_documents
object.searched_items.where('hotel_id' => 127).to_a:
[#<SearchedItem _id: 5359329d7f11ba034b000006, hotel_id: 127, room_info: [{"price"=>10, "amenity_ids"=>[1, 2]}, {"price"=>160, "amenity_ids"=>nil}]>]
object.searched_items.where(:hotel_id.in => [127,128]).to_a:
[#<SearchedItem _id: 5359329d7f11ba034b000006, hotel_id: 127, room_info: [{"price"=>10, "amenity_ids"=>[1, 2]}, {"price"=>160, "amenity_ids"=>nil}]>]
object.searched_items.where('room_info.amenity_ids' => {'$in' => [2,3]}).to_a:
[]
object.searched_items.where('room_info.amenity_ids'.to_sym.in => [2,3]).to_a:
[]
object.searched_items.select{|searched_item| searched_item.room_info.any?{|room_info| room_info['amenity_ids'] && !(room_info['amenity_ids'] & [2,3]).empty?}}.to_a:
[#<SearchedItem _id: 5359329d7f11ba034b000006, hotel_id: 127, room_info: [{"price"=>10, "amenity_ids"=>[1, 2]}, {"price"=>160, "amenity_ids"=>nil}]>]
Finished tests in 0.089544s, 33.5031 tests/s, 0.0000 assertions/s.
3 tests, 0 assertions, 0 failures, 0 errors, 0 skips