eq_join like functionality on a single object - rethinkdb

I have 2 tables child and parent:
child
{id:1, parent_id: 10, name:"blah" ...}
parent
{id:10, name: "parent blah" ....}
I know the id of the child object and want to query the child object joined with the parent object.
Is there a way i can do the equivalent of :
r.table('child').get(10).eq_join('parent_id', r.table('parent'))
eq_joins work great on the results of a filter operation since it returns a sequence. I want to perform a similar operation when i use get
so an eq_join of the form:
r.table('child').filter({'id': 1}).eq_join('parent_id', r.table('parent'))
gives me records
{left: {id: 10, parent_id:1, ...},
right: {id:1, ....}}
which works great for a sequence.
I want to carry out the same operations in a case where I use a get instead of filter.

You can do something like that (in Python)
r.table('child').get(10).merge(lambda child:
{
'parent': r.table('parents').get(child['parent_id'])
}
)
If you want exactly the same output as with eq_join, you can do
r.expr([r.table('child').get(10)]).eq_join('parent_id', r.table('parent'))

Related

how to correctly render referenced entities in list when I have objects instead of numberic ids?

right now in order for the list to render properly I need to have this kind of data passed in:
row = {
id: value,
name: value,
height: value,
categories: [1,2,3,4]
}
how can I adapt the code so that a list works with this kind of data?
row = {
id: value,
name: value,
height: value,
categories: [{id: "1"},{id: "2"},{id: "3"},{id: "4"}]
}
when I try to do that it seems that it applies JSON.stringify to the objects so it is trying to find category with id [Object object]
I would to avoid a per case conversion of data as I do now..
it seems that I cannot do anything in my restClient since the stringify was already applied
I have the same issue when I fetch just one data row e.g in Edit or Create.. categories ReferenceArrayInput is not populated when categories contains objects
Have you tried using format?
https://marmelab.com/admin-on-rest/Inputs.html#transforming-input-value-tofrom-record
Might help transform your input value. Then you can use format() to change values back to the format your API expects.
If this does not work then you will have to probably create a custom component out of ReferenceArrayInput.

CouchDB 2.0 - How to autoincrement keys in a View?

In CouchDB 2.0, I'm trying to create an ordered list as the keys from a View, but it doesn't work.
My code for the View document:
var i = 0;
function (doc) {
if (doc.type === "comment") {
emit(i++, doc.webpages);
}
}
The result is that all keys are equal to 0. How can I make it so that each document gets an autoincremented key?
Thanks!
A sequential ID probably isn't the best choice for most real applications. For example, if you were to build a commenting system I would approach it like this (there's a similar example in the couch docs):
Comments would be docs with a structure like this:
{
"_id": "comment_id",
"parent":"comment_id, or article_id if a top level comment"
"timestamp" : "iso datetime populated by the server",
"user_id": "the person who wrote the comment",
"content": "content of the comment"
}
To display all the top level comments of a given parent (either article or parent comment), you could use a view like this:
def function(doc){
emit([doc.parent, doc.timestamp, doc.user_id], doc._id)
}
To query this efficiently, you'd could use the following query options to grab the first twenty:
{
"startkey": ["parent_id"],
"endkey": ["parent_id", {}],
"limit": 20,
"skip": 0,
"include_docs": true
}
The comments will automatically be sorted by the date they were posted because the view is ordered by [parent, datetime, and then user]. You don't have the pass a value for anything other than parent with your key for benefit from this.
Another thing of note is by not passing the content of the comment to the view and instead using include_docs, your index will remain as slim as possible.
To expand on this:
If you want to show replies to a base comment, you can just change
the start and end keys to that comment's id.
If you want to show the next 20 comments, just change skip to 20.
If you want more comments shown initially, just up the limit value.
In answer to your comment, if you had an array or parents in your document like:
"parents" : ["a100", "a101", "a102"]
Everything else would remain the same, except you would emit a row for each parent.
def function(doc){
doc.parents.map( function (parent){
emit([doc.parent, doc.timestamp, doc.user_id], doc._id)
});
}

Rails5: How can I use the ActiveRecord "OR" query with "includes"?

class Parent < ApplicationRecord
has_many :children
enum status: {
status1: 0,
status2: 1
}
end
class Child < ApplicationRecord
belongs_to :parent
end
# error
# "Relation passed to #or must be structurally compatible. Incompatible values: [:references]"
combination = Parent.status1.or(Parent.status2.includes(:children).where(children: {name: 'ABC'}))
I want to get the data "status1" or "status2 has children named 'ABC'", but error occurs.
The or method takes another relation that has a similar filter pattern, and combines it with the already-existing filters on the object being called.
For example, Parent.status1.or(Parent.status2) would give you a set of records that have either status: 1 or status: 2.
(In case someone is not familiar with it, the example in the question also uses enum, which allows filtering the enum's attribute value using the name of the value. #status1 and #status2 in this case correspond to { status: 0 } and {status: 1} respectively.)
In order to call more relation methods to modify the final result, you must call them on the result of calling #or, like this:
Parent.status1.or(Parent.status2).includes(:children).where(children: {name: 'ABC'})
Based on your comment I see now that you want records that either (have status1) or (have status2 and have a matching children record).
Note that in order to use a relation in a where (like where(children: { name: value }) you must join with the related table (joins(:children).where(children: { name: value }). It seems that ActiveRecord will infer the join if you use only includes, but that's not documented as far as I can tell. This is why or sees the two relations as incompatible: one has children in the references list, while the other does not.
If you write the where clause by hand as a string, it does not change the references list, so or does not see the relation as incompatible. When you write a where clause by hand, you must explicitly use joins:
Parent.status1.joins(:children).or(Parent.status2.joins(:children).where("children.name = 'ABC'"))
You are not calling "includes" on the final or result.
parent = Parent.status1.or(Parent.status2)
parent.includes(:chilren).where(children: {name: "ABC"})

RethinkDB: removing item from array in one table by value from another table

I use RethinkDB in my project and have the following table structure:
data_item {
id: "generated_thing",
slug: "slug"
}
aggregation_of_data_items {
items: ["some", "ids", "from", "data_item", "table"]
}
When I delete item from content table I want to keep data consistent - delete ID from aggregation_of_data_items.items array - is there any opportunity to do this in one request (something like $pull or $pullAll in MongoDB)?
To delete an item from an array you can do the following (this is in Python but it works in any supported language):
def remove(doc, value):
doc.replace(
lambda doc: doc.merge({"items" : doc["items"].set_difference([value])}))
Now we just need to run a query that does both, the easiest way to do this is to put them in an array:
[r.table("data_item").get(id).delete(),
remove(r.table("aggregation_of_..").get(...), id)]
.run()

CouchDB Views: remove duplicates *and* order by time

Based on a great answer to my previous question, I've partially solved a problem I'm having with CouchDB.
This resulted in a new view.
Now, the next thing I need to do is remove duplicates from this view while ordering by date.
For example, here is how I might query that view:
GET http://scoates-test.couchone.com/follow/_design/asset/_view/by_userid_following?endkey=[%22c988a29740241c7d20fc7974be05ec54%22]&startkey=[%22c988a29740241c7d20fc7974be05ec54%22,{}]&descending=true&limit=3
Resulting in this:
HTTP 200 http://scoates-test.couchone.com/follow/_design/asset/_view/by_userid_following
http://scoates-test.couchone.com > $_.json.rows
[ { id: 'c988a29740241c7d20fc7974be067295'
, key:
[ 'c988a29740241c7d20fc7974be05ec54'
, '2010-11-26T17:00:00.000Z'
, 'clementine'
]
, value:
{ _id: 'c988a29740241c7d20fc7974be062ee8'
, owner: 'c988a29740241c7d20fc7974be05f67d'
}
}
, { id: 'c988a29740241c7d20fc7974be068278'
, key:
[ 'c988a29740241c7d20fc7974be05ec54'
, '2010-11-26T15:00:00.000Z'
, 'durian'
]
, value:
{ _id: 'c988a29740241c7d20fc7974be065115'
, owner: 'c988a29740241c7d20fc7974be060bb4'
}
}
, { id: 'c988a29740241c7d20fc7974be068026'
, key:
[ 'c988a29740241c7d20fc7974be05ec54'
, '2010-11-26T14:00:00.000Z'
, 'clementine'
]
, value:
{ _id: 'c988a29740241c7d20fc7974be063b6d'
, owner: 'c988a29740241c7d20fc7974be05ff71'
}
}
]
As you can see, "clementine" shows up twice.
If I change the view to emit the fruit/asset name as the second key (instead of the time), I can change the grouping depth to collapse these, but that doesn't solve my order-by-time requirement. Similarly, with the above setup, I can order by time, but I can't collapse duplicate asset names into single rows (to allow e.g. 10 assets per page).
Unfortunately, this is not a simple question to explain. Maybe this chat transcript will help a little.
Please help. I'm afraid that what I need to do is still not possible.
S
You can do this using list function. Here is an example to generate a really simple list containing all the owner fields without dupes. You can easily modify it to produce json or xml or anything you want.
Put it into your assets design doc inside the lists.nodupes and use like this:
http://admin:123#127.0.0.1:5984/follow/_design/assets/_list/nodupes/by_userid_following_reduce?group=true
function(head, req) {
start({
"headers": {
"Content-Type": "text/html"
}
});
var row;
var dupes = [];
while(row = getRow()) {
if (dupes.indexOf(row.key[2]) == -1) {
dupes.push(row.key[2]);
send(row.value[0].owner+"<br>");
}
}
}
Ordering by one field and uniquing on another isn't something the basic map reduce can do. All it can do is sort your data, and apply reduce rollups to dynamic key-ranges.
To find the latest entry for each type of fruit, you'd need to query once per fruit.
There are some ways to do this that are kinda sane.
You'll want a view with keys like [fruit_type, date], and then you can query like this:
for fruit in fruits
GET /db/_design/foo/_view/bar?startkey=["apples"]&limit=1&descending=true
This will give you the latest entry for each fruit.
The list operation could be used to do this, it would just echo the first row from each fruit's block. This would be efficient enough as long as each fruit has a small number of entries. Once there are many entries per fruit, you'll be discarding more data than you echo, so the multi-query approach actually scales better than the list approach, when you get to a large data set. Luckily they can both work on the same view index, so when you have to switch it won't be a big deal.

Resources