Rails & Mongoid unique results - ruby-on-rails

Consider the following example of mongo collection:
{"_id" : ObjectId("4f304818884672067f000001"), "hash" : {"call_id" : "1234"}, "something" : "AAA"}
{"_id" : ObjectId("4f304818884672067f000002"), "hash" : {"call_id" : "1234"}, "something" : "BBB"}
{"_id" : ObjectId("4f304818884672067f000003"), "hash" : {"call_id" : "1234"}, "something" : "CCC"}
{"_id" : ObjectId("4f304818884672067f000004"), "hash" : {"call_id" : "5555"}, "something" : "DDD"}
{"_id" : ObjectId("4f304818884672067f000005"), "hash" : {"call_id" : "5555"}, "something" : "CCC"}
I would like to query this collection and get only the first entry for each "call_id", in other words i'm trying to get unique results based on "call_id".
I tried to use .distinct method:
#result = Myobject.all.distinct('hash.call_id')
but the resulting array will contain only the unique call_id fields:
["1234", "5555"]
and I need all the other fields too.
Is it possible to make a query like this one?:
#result = Myobject.where('hash.call_id' => Myobject.all.distinct('hash.call_id'))
Thanks

You cannot simply return the document(or subset) by using the distinct. As per the documentation it only returns the distinct array of values based on the given key. But you can achieve this by using map-reduce
var _map = function () {
emit(this.hash.call_id, {doc:this});
}
var _reduce = function (key, values) {
var ret = {doc:[]};
var doc = {};
values.forEach(function (value) {
if (!doc[value.doc.hash.call_id]) {
ret.doc.push(value.doc);
doc[value.doc.hash.call_id] = true; //make the doc seen, so it will be picked only once
}
});
return ret;
}
The above code is self explanatory, on map function i am grouping it by key hash.call_id and returning the whole doc so it can be processed by reduce funcition.
On reduce function, just loop through the grouped result set and pick only one item from the grouped set (among the multiple duplicate key values - distinct simulation).
Finally create some test data
> db.disTest.insert({hash:{call_id:"1234"},something:"AAA"})
> db.disTest.insert({hash:{call_id:"1234"},something:"BBB"})
> db.disTest.insert({hash:{call_id:"1234"},something:"CCC"})
> db.disTest.insert({hash:{call_id:"5555"},something:"DDD"})
> db.disTest.insert({hash:{call_id:"5555"},something:"EEE"})
> db.disTest.find()
{ "_id" : ObjectId("4f30a27c4d203c27d8f4c584"), "hash" : { "call_id" : "1234" }, "something" : "AAA" }
{ "_id" : ObjectId("4f30a2844d203c27d8f4c585"), "hash" : { "call_id" : "1234" }, "something" : "BBB" }
{ "_id" : ObjectId("4f30a2894d203c27d8f4c586"), "hash" : { "call_id" : "1234" }, "something" : "CCC" }
{ "_id" : ObjectId("4f30a2944d203c27d8f4c587"), "hash" : { "call_id" : "5555" }, "something" : "DDD" }
{ "_id" : ObjectId("4f30a2994d203c27d8f4c588"), "hash" : { "call_id" : "5555" }, "something" : "EEE" }
and running this map reduce
> db.disTest.mapReduce(_map,_reduce, {out: { inline : 1}})
{
"results" : [
{
"_id" : "1234",
"value" : {
"doc" : [
{
"_id" : ObjectId("4f30a27c4d203c27d8f4c584"),
"hash" : {
"call_id" : "1234"
},
"something" : "AAA"
}
]
}
},
{
"_id" : "5555",
"value" : {
"doc" : [
{
"_id" : ObjectId("4f30a2944d203c27d8f4c587"),
"hash" : {
"call_id" : "5555"
},
"something" : "DDD"
}
]
}
}
],
"timeMillis" : 2,
"counts" : {
"input" : 5,
"emit" : 5,
"reduce" : 2,
"output" : 2
},
"ok" : 1,
}
You get the first document of the distinct set. You can do the same in mongoid by first stringify the map/reduce functions and call mapreduce like this
MyObject.collection.mapreduce(_map,_reduce,{:out => {:inline => 1},:raw=>true })
Hope it helps

Related

Ignoring Firebase child

How could I query this data in this way:
I would like to ignore the CurrentSubAdministrativeArea child and iterate every each sub child and find the right userKey
Actually I'm using this code, that isnt working:
self.ref.child("Ads").child("CurrentSubAdministrativeArea")
/*HERE I would like to ignore the childs*/
.queryOrdered(byChild: "userKey").queryEqual(toValue: uid).observeSingleEvent(of:.value, with: { (snapshot) in
--
{
"Ads" : {
"CurrentSubAdministrativeArea" : {
"Mantova" : {
"-L7ymBmmbHkNfhBRte9F" : {
"cost" : 200,
"date" : 1527256922000,
"info" : "Test",
"maxLimit" : 100,
"minLimit" : 10,
"personBadType" : [ "abitudinaria", "antipatica" ],
"personGoodType" : [ "simpatica", "felice" ],
"subAdministrativeArea" : "Mantova",
"title" : "Mantova Test",
"url" : "https://firebasestorage.googleapis.com/v0/b/team-34540.appspot.com/o/Mantova%20Test?alt=media&token=3a81ed1c-ecd6-4dc0-bd7c-45e093ce8188",
"userKey" : "OsJRc98sqxPx70iqxFtoqerMzHH2",
"via" : "viale dei test"
}
},
"Milano" : {
"-L6qywMC6nxi0fJNMHba" : {
"cost" : 454,
"date" : 1528298580000,
"info" : "Di pollo",
"maxLimit" : 100,
"minLimit" : 10,
"personBadType" : [ "abitudinaria", "antipatica" ],
"personGoodType" : [ "simpatica", "felice" ],
"subAdministrativeArea" : "Milano",
"title" : "Pollo 2",
"url" : "https://firebasestorage.googleapis.com/v0/b/team-34540.appspot.com/o/Pollo?alt=media&token=fc6a3ec8-5f9a-4347-bdad-2d9715af784d",
"userKey" : "OsJRc98sqxPx70iqxFtoqerMzHH2",
"via" : "viale test"
}
}
}
}
}
You could denormalize your data in such a way your query is easy to build and execute.
Together with the data structure you already have you would have another node (ie. another data structure) like
{
"AdsByUsers" : {
"OsJRc98sqxPx70iqxFtoqerMzHH2": {
"Mantova",
"Milano",
...
},
"abcde88qxPx70iqxFtoqerMzKh5": {
"Firenze",
...
}
With NoSQL database you should not hesitate to duplicate data in such a way your queries are easy and fast to execute.

Firebase, get property by email

I have this database on Firebase:
{
"issues" : {
"-L04771_EjrLlv5u1-GU" : {
"issue" : "Test insert 1",
"last_edit" : "d8QICgTG5xR20RBzAXfzfu8gLgw2",
"owner" : "d8QICgTG5xR20RBzAXfzfu8gLgw2",
"owner_email" : "example1#gmail.com",
"status" : 1,
"url" : "http://www.example.com/example.html"
},
"-L047pIoqxkj4saaTYyQ" : {
"issue" : "Test insert 2",
"last_edit" : "d8QICgTG5xR20RBzAXfzfu8gLgw2",
"owner" : "d8QICgTG5xR20RBzAXfzfu8gLgw2",
"owner_email" : "example2#gmail.com",
"status" : 1,
"url" : "http://www.example.com/example.html"
}
}
}
I have to extract only those who have owner_email "example1#gmail.com".
Is possible?
You're probably looking for Firebase Queries and especially the equalTo-Query.
Your code would be something like this:
// Find all issues with owner_email = example1#gmail.com
var ref = firebase.database().ref("issues");
ref.orderByChild("owner_email").equalTo("example1#gmail.com").on("value", function(snapshot) {
// Loops through the matching issues
snapshot.forEach(function(child) {
console.log(child.key);
});
});

How to boost the closest created_at field in Elasticsearch?

I want to sort my query results following some boost rules and in the same time i want them to be sorted as possible by creation date, if i add a created_at sort, it changes everything and my results are not relevant anymore. So i guess the only way to do that is to boost created_at field (the newest has the biggest bonus in calculating score for that boost) but i dont know how to implement it. This is my query:
query = {
"query" : {
"bool" : {
"must" : [
{
"range" : {
"deadline" : {
"gte" : "2016-05-30T11:39:10+02:00"
}
}
},
{
"terms" : {
"state" : [
"open"
]
}
},
{
"query_string" : {
"query" : "chant",
"default_operator" : "AND",
"analyzer" : "search_francais",
"fields" : [
"title^6",
"description",
"brand",
"category_name"
]
}
}
]
}
},
"filter" : {
"and" : [
{
"geo_distance" : {
"distance" : "40km",
"location" : {
"lat" : 48.855736,
"lon" : 2.32927300000006
}
}
}
]
},
"sort" : [
{
"_score" : "desc"
},
#{
# "created_at" : "desc" ==> i tried this but it doesnt change results
#}
]
}
Try adding your condition in should block.
i)If the created date should be closer to come value in the search query or you have any idea on how close the date should be, give a range query.
ii) If you are not sure of all those values, decay function can be used. In this case, query shall be changed to function query.
{
"query" : {
"bool" : {
"must" : [
{
"range" : {
"deadline" : {
"gte" : "2016-05-30T11:39:10+02:00"
}
}
},
{
"terms" : {
"state" : [
"open"
]
}
},
{
"query_string" : {
"query" : "chant",
"default_operator" : "AND",
"analyzer" : "search_francais",
"fields" : [
"title^6",
"description",
"brand",
"category_name"
]
}
}
],
"should": [
{"created_at" : "condition here .. "}
]
}
},
"filter" : {
"and" : [
{
"geo_distance" : {
"distance" : "40km",
"location" : {
"lat" : 48.855736,
"lon" : 2.32927300000006
}
}
}
]
}
}

Is it possible to create Salesreceipt without product/service value through QBO API?

Is it possible to create Salesreceipt without product/service value through QBO API? I have tried through API but it's not reflecting rate value and storing description value only.
If I remove ItemRef attribute(in request body) then it's reflecting rate and amount values and it's assigning some default and random product/service.
It is possible directly in QBO UI.
Request body where only description value storing:
{
"TxnDate" : "2016-05-27",
"Line" : [ {
"Amount" : 2222.00,
"Description" : "hi chk",
"DetailType" : "ItemReceiptLineDetail",
"ItemReceiptLineDetail" : {
"ItemRef" : { },
"Qty" : 1,
"UnitPrice" : 2222
} }
],
"CustomerRef" : {
"value" : "67"
},
"CustomerMemo" : {
"value" : "Thanks for your business! We appreciate referrals!"
},
"TotalAmt": 2222.00,
"PrivateNote" : "",
"CustomField" : [ {
"DefinitionId" : "1",
"Type" : "StringType",
"StringValue" : ""
} ]
}
Request body where default product/service assigning:
{
"TxnDate" : "2016-05-27",
"Line" : [ {
"Amount" : 2222.00,
"Description" : "hi chk",
"DetailType" : "ItemReceiptLineDetail",
"ItemReceiptLineDetail" : {
"Qty" : 1,
"UnitPrice" : 2222
} }
],
"CustomerRef" : {
"value" : "67"
},
"CustomerMemo" : {
"value" : "Thanks for your business! We appreciate referrals!"
},
"TotalAmt": 2222.00,
"PrivateNote" : "",
"CustomField" : [ {
"DefinitionId" : "1",
"Type" : "StringType",
"StringValue" : ""
} ]
}
No.
QuickBooks Online does not support this.

SwiftyJSON array empty

I want to retrieve data from a JSON file with SwiftyJSON and Alamofire. This is the JSON data:
json: {
"data" : {
"monitors" : [
{
"lines" : [
{
"towards" : "LEOPOLDAU",
"realtimeSupported" : true,
"departures" : {
"departure" : [
{
"departureTime" : {
"countdown" : 2,
"timePlanned" : "2015-09-09T18:47:03.000+0200",
"timeReal" : "2015-09-09T18:47:03.000+0200"
}
},
{
"departureTime" : {
"countdown" : 6,
"timePlanned" : "2015-09-09T18:51:03.000+0200",
"timeReal" : "2015-09-09T18:51:03.000+0200"
}
}
]
},
"lineId" : 301,
"platform" : "1",
"trafficjam" : false,
"richtungsId" : "1",
"direction" : "H",
"type" : "ptMetro",
"name" : "U1",
"barrierFree" : true
}
],
"attributes" : {
},
"locationStop" : {
"type" : "Feature",
"geometry" : {
"type" : "Point",
"coordinates" : [
16.4330680046932,
48.2430825589716
]
},
"properties" : {
"municipality" : "Wien",
"title" : "Kagran",
"coordName" : "WGS84",
"municipalityId" : 90000,
"type" : "stop",
"gate" : "1",
"name" : "60200627",
"attributes" : {
"rbl" : 4127
}
}
}
}
]
}
,
"message" : {
"value" : "OK",
"serverTime" : "2015-09-09T18:45:08.998+0200",
"messageCode" : 1
}
}
What I want to do now is get the data of lines into an array. I tried several combinations for that, e.g.
let departureArray = jsonData["data"]["monitors"][0]["lines"][0].array
or
let departureArray = jsonData["data"]["monitors"]["lines"].array
but my array is always empty. Does anyone has an idea how to do this properly?
Your expected array departureArray is in fact a dictionary
let departureDictionary = jsonData["data"]["monitors"][0]["lines"][0].dictionary

Resources