CouchDB "Join" two documents - view

I have two documents that looks a bit like so:
Doc
{
_id: AAA,
creator_id: ...,
data: ...
}
DataKey
{
_id: ...,
credits_left: 500,
times_used: 0,
data_id: AAA
}
What I want to do is create a view which would allow me to pass the DataKey id (key=DataKey _id) and get both the information of the DataKey and the Doc.
My attempt:
I first tried embedding the DataKey inside the Doc and used a map function like so:
function (doc)
{
if (doc.type == "Doc")
{
var ids = [];
for (var i in doc.keys)
ids.push(doc.keys[i]._id);
emit(ids, doc);
}
}
But i ran into two problems:
There can be multiple DataKey's per
Doc so using startkey=[idhere...]
and endkey=[idhere..., {}] didn't
work (only worked if the key happend
to be the first one in the array).
All the data keys need to be unique, and I would prefer not making a seperate document like {_id = datakey} to reserve the key.
Does anyone have ideas how I can accomplish this? Let me know if anything is unclear.
-----EDIT-----
I forgot to mention that in my application I do not know what the Doc ID is, so I need to be able to search on the DataKey's ID.

I think what you want is
function (doc)
{
if (doc.type == "Doc")
{
emit([doc._id, 0], doc);
}
if(doc.type == "DataKey")
{
emit([doc.data_id, 1], doc);
}
}
Now, query the view with key=["AAA"] and you will see a list of all docs. The first one will be the real "Doc" document. All the rest will be "DataKey" documents which reference the first doc.
This is a common technique, called CouchDB view collation.

Related

How to query relational data in ascending order in strapi?

I have this query that works
async find(ctx) {
let { _start, _limit } = ctx.request.query;
console.log(ctx.request.query)
_limit ? 0 : (_limit = 10);
const entities = await strapi.services["course-series"].find({});
return entities.map((entity) => {
// Do I sort them here or in the url query (and how)
entity.courses = entity.courses.slice(_start, _limit);
return sanitizeEntity(entity, { model: strapi.models["course-series"] });
});
}
The idea is that I can load 10 courses from each series at first and then get the next 10...
I just realized that the first 10 I am getting are not the recent ones.
As I commented // Do I sort them here or in the url query (and how)
What version of Strapi do you use?
What does this line do strapi.services["course-series"].find({})? How did you build this find method in the service? What does it do? Does it accept params?
Personally I'd do something like that (assuming you're working with Strapi version > 4:
const entities = await strapi.entityService.findMany('api::course-series.course-series', {
fields: [/* list the course-series fields you want to populate */],
populate: {
courses: {
fields: [/* list the course fields you want to populate */],
sort: 'createdAt:desc', // You can use id, publishedAt or updatedAt here, depends on your sorting prefrences
offset: _start,
limit: _limit // I must admit I haven't tested `offset` and `limit` on the populated related field
}
}
})
// ...the rest of your code, if needed
Read more about Entity Service API here.
Doing it the way you did it, you will always first retrieve the full list of courses for each course-series, and then run costly operations like mapping (the lesser of 2 evils) and above all sorting.

How can I do a WpGraphQL query with a where clause?

This works fine
query QryTopics {
topics {
nodes {
name
topicId
count
}
}
}
But I want a filtered result. I'm new to graphql but I see a param on this collection called 'where', after 'first', 'last', 'after' etc... How can I use that? Its type is 'RootTopicsTermArgs' which is likely something autogenerated from my schema. It has fields, one of which is 'childless' of Boolean. What I'm trying to do, is return only topics (a custom taxonomy in Wordpress) which have posts tagged with them. Basically it prevents me from doing this on the client.
data.data.topics.nodes.filter(n => n.count !== null)
Can anyone direct me to a good example of using where args with a collection? I have tried every permutation of syntax I could think of. Inlcuding
topics(where:childless:true)
topics(where: childless: 'true')
topics(where: new RootTopicsTermArgs())
etc...
Obviously those are all wrong.
If a custom taxonomy, such as Topics, is registered to "show_in_graphql" and is part of your Schema you can query using arguments like so:
query Topics {
topics(where: {childless: true}) {
edges {
node {
id
name
}
}
}
}
Additionally, you could use a static query combined with variables, like so:
query Topics($where:RootTopicsTermArgs!) {
topics(where:$where) {
edges {
node {
id
name
}
}
}
}
$variables = {
"where": {
"childless": true
}
};
One thing I would recommend is using a GraphiQL IDE, such as https://github.com/skevy/graphiql-app, which will help with validating your queries by providing hints as you type, and visual indicators of invalid queries.
You can see an example of using arguments to query terms here: https://playground.wpgraphql.com/#/connections-and-arguments

CouchDB _changes-API filter

I'm trying to get a filtered _changes-API stream on CouchDB 2.1.1 to work, but I run into issues.
I only want to receive documents via the changes feed that contain the field "type" with the value "article".
According to the documentation something like this should work:
function (doc, req) {
if (doc.type && doc.type == 'article') {
return true;
}
return false;
}
I created the function above in a new view called type_article in a _design document called filters using Fauxton. When I click on the view I don't see any results there.
Now I want to retrieve the filtered changes feed from the DB using the filter as a GET-parameter:
localhost:5984/my_database/_changes?filter=filters/type_article
The response of CouchDB is
{"error":"not_found","reason":"missing json key: filters"}
Do you have an idea how I can get the filter-functionality to work?
PS: I also tried using the 'emit()' function instead of returning true and false, this returned the expected results, but when trying to query _changes the same error appeared.
Just a note, you actually can use view as a filter for _changes. In this case a document counted passed if a map function emits at least one record for it.
For example, if a design document (called "ddoc") looks something like this:
{"views":
{"type_article":
{"map":
"function(doc) {
if (doc.type && doc.type == 'article') {
emit(doc.name);
}
}"
}
}
}
Then a query would look like localhost:5984/my_database/_changes?filter=_view&view=ddoc/type_article. Note a missing _design prefix and a keyword _view for an attribute filter. Here is a link on an according documentation section: [link]
The only thing to be aware here is that this filter not actually using built view index and therefore not faster than an ordinary filter function.
I found the problem. When you create a view in the Fauxton using small + sign next to the Design Documents menu entry you can only created views. Views are different than filters.
To create a filter that works with the _changes feed click on 'Create Document' and create a document like this:
{
"_id": "_design/filters",
"filters": {
"type_article": "function (doc, req) {\n if (doc.type && doc.type == \"article\") {\n return true;\n } else { \n return false; \n}\n}"
}
}
This will create a new design document called filters with a function type_article.

Firebase 3.0 sort by value

I am using firebase 3.0, I see examples for the firebase 2.x versions, however they seem to not work in 3.0. I have a simple structure that I want to have returned to me sorted by value.
{
"Regions" : {
"All" : 0,
"Eastern & Southern Africa" : 1,
"Global" : 6,
"Himalayas" : 2,
"Mekong" : 3,
"Mesoamerica" : 5,
"West Africa" : 4
}
}
The code I am using returns the json, however it is not sorted by value, it is sorted alphabetically.
var config = {
apiKey: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
authDomain: "servir-activity-database.firebaseapp.com",
databaseURL: "https://servir-activity-database.firebaseio.com",
storageBucket: "",
};
firebase.initializeApp(config);
var theRegions;
firebase.database().ref('Regions/').orderByChild('value').on('value', function (regions) {
theRegions = regions.val();
loadRegions(theRegions);
});
function loadRegions(which)
{
$.each(which, function (i, value) {
$('#ddlRegions').append($('<option>').text(i).attr('value', value));
});
}
I realize that I can do the sort on the client side, however this seems like a simple thing that can be returned sorted and I am probably missing something really simple.
This is a really long winded answer and is way more than is needed to answer the question, but should probably be considered:
One of the life lessons Firebase teaches us is that disassociating key names from the data is usually a good idea. I have (several times) coded myself into a corner because I used what I thought was a static key name, only to find it needed to be changed later. So let me expound on that briefly:
For example, say you have a users node
Frank_Jones: Madagascar
Leroy_Jenkins: UBRS
and suppose Frank decides that he no longer wants to be using the name 'Frank', but instead wants to be called 'Puf'
In that case every single node in your entire database would then have to be updated to refer back to the newly called Puf_Jones node. Ugh.
To avoid this issue, let Firebase generate your node names with childByAutoId, and let your values be children. This is a 'randomly' created node name that is guaranteed to be discreet.
A better Firebase Structure for your data would be
Regions
-Ykjoas99joksjk
region_name: "Himalayas"
rank: 2
-Jlioksjnfjp987
region_name: "Eastern & Southern Africa"
rank: 1
-J989j99ajskmds
region_name: "West Africa"
rank: 4
The -Ykjoas99joksjk etc node names are created by Firebase.
As you can see we now have a name and rank for each region. Let's say you wanted to add average summer temperature:
-Ykjoas99joksjk
region_name: "Himalayas"
rank: 2
temp: 77F
This allows your Firebase structure to be elastic depending on your needs.
(some Swift code to follow but you'll get the idea)
To get the region names in order
let ref = myRootRef.childByAppendingPath("Regions")
ref.queryOrderedByChild("region_name").observeEventType(
.ChildAdded, withBlock : { snapshot in
print(snapshot)
})
To get the two highest ranks
ref.queryOrderedByChild("rank").queryLimitedToLast(2).observeEventType(
.ChildAdded, withBlock : { snapshot in
print(snapshot)
})
and the Swift answer to your question with your existing Firebase Structure:
regionsRef.queryOrderedByValue().observeEventType(.ChildAdded, withBlock: {
snapshot in
print(snapshot)
})
Here's my lol translation that needs to be fixed...
firebase.database().ref('Regions').orderByValue.on('value', function (snapshot) {
loadRegions(snapshot.val());
});
Thank you #Jay, your answer was just long winded enough! I had to change the code part from swift to javascript but i followed the autoid for the key method as you described. Here is the js i used to process the return data.
firebase.database().ref('Regions').orderByChild("rank").on('child_added', function (regions) {
loadRegions(regions.exportVal());
});
firebase.database().ref('Regions').orderByChild("rank").on('child_removed', function (regions) {
removeRegion(regions.exportVal());
});
function removeRegion(which)
{
$("#ddlRegions option[value="+which.rank+"]").remove();
}
function loadRegions(which)
{
var count = 0;
var RegionValue;
$.each(which, function (i, value) {
if (count % 2 == 0) {
RegionValue = value;
}
else {
$('#ddlRegions').append($('<option>').text(value).attr('value', RegionValue));
}
count++;
});
}

RethinkDB: Getting all documents that contain a string in any field

I want to perform a query that will return all the documents that contain a given string in ANY of their fields. For example, say I have a "users" table and I'm looking for all the documents that contain "john", the returned result can be:
[{"first_name": "jhon", "last_name": "watson"}, {"first_name": "elton", "last_name": "john"}, {"first_name": "sherlock", "last_name": "holmes", "best_friend": "john watson"}]
How do I do that in rethinkdb? javascript answer will do, python implementation will be better.
Unfortunately this query is made harder by the fact that ReQL doesn't have a values function like python. However it does have a keys function so let's just use that to make a values function like so:
def values(doc):
return doc.keys().map(lambda x: doc[x])
Now that we have that finding a document that contains a string in one of its keys is pretty easy:
def has_str(doc, str):
return values(doc).map(match(str)).reduce(lambda x,y: x | y)
Finally we can put it all together:
r.table("users").filter(lambda doc: has_str(doc, str))
You could in theory do this all in one big query but I really like breaking up even moderately complicated queries. The really nice thing about this approach is that if it doesn't work each function has a pretty simple set of semantics so you can debug them individually.
It looks like you can just coerce the entire document into a string, and then search on that:
r.db('database').table('table).filter(function(doc) {
return doc.coerceTo('string').match('querystring');
});
This solution doesn't feel as slick as the other provided, but runs much faster for me and provides the same results so far.
For anyone who has found themselves here trying to figure this out in javascript, this roughly translates to:
function values(doc) {
return doc.keys().map(function(key) {
return doc(key);
});
}
function contains(doc, string) {
return values(doc).map(function(val) {
return r.branch(val.match(string), true, false);
}).reduce(function(left, right) {
return left.or(right);
});
}
var query = r.db('database').table('table').filter(function(doc) {
return contains(doc, "some string");
});
query.run().then(function(results) {
console.log(results);
});
Improvements welcome!

Resources