How to make a query inside a method of the Groovy class

Hello!
I am trying to execute a query inside a method in the Groovy class.
If I just put the code inside the script, then it works (see 1)

If I create a class and place the code inside the method, then I get an error (see 2)

I understand that I need to apparently import a service or query, but I don’t understand how.
Unfortunately, searching the documentation and the forum did not help.

Code:

{{groovy}}
class Test
{
  String testQuery()
  {
    def xwql_text = "";
    def query = services.query.xwql(xwql_text);
    pages = query.execute();
    return pages;
  }
}
def t = new Test();
t.testQuery();
{{/groovy}}

version XWiki 15.10

Tell me please.

The services variable simply does not exist in the scope of your method. So you need to pass it either as a parameter of #testQuery or in the Test class constructor to set a field with it.

Thanks tmortagne!

This code worked as it should:

{{groovy}}
import org.xwiki.script.internal.service.*
class Test
{
  DefaultScriptServiceManager services
  Test(DefaultScriptServiceManager services) {
        this.services = services
    }
  String testQuery()
  {
    def xwql_text = "";
    def query = services.query.xwql(xwql_text);
    def pages = query.execute();
    return pages;
  }
}
def t = new Test(services);
t.testQuery();
{{/groovy}}

I have a similar question about using queries in components.
I created a component based on the example from the documentation.
https://extensions.xwiki.org/xwiki/bin/view/Extension/WikiComponent%20Module

But no matter how I try, the code doesn’t work.
Now I can probably pass service to each method, but how do I pass it at the component level?

You can suggest an example of code implementing a data query inside a component method written in Groovy.

AFAIK, each wiki content executed as a component method should have the same script bindings you have in a wiki page, which includes the services binding.

I’m sorry, but I didn’t really understand. Here is the example above (where the class is created) I passed it through the service variable and then use it. How can I do the same when creating a class in a component? After all, I’m obviously not creating it. It just registers through the component manager.

My code for the component:

{{groovy}}
import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.component.annotation.Component;
import org.xwiki.component.annotation.ComponentAnnotationLoader;
import org.xwiki.script.service.ScriptService;
import com.xpn.xwiki.*;
import com.xpn.xwiki.doc.*;
import com.xpn.xwiki.objects.*;
import com.xpn.xwiki.api.*;
import org.xwiki.script.internal.service.*
  
@Component
@Named("helloWorld")
@Singleton
public class HelloWorldGroovyScriptService implements ScriptService
{
  
  public execute(XWikiDocument doc)
    import org.xwiki.script.internal.service.*;
    {
    def xwql_text = "";
    def query = services.query.xwql(xwql_text);
    query.addFilter('hidden');
    def pages = query.execute();
    return pages;
    }
}

// Note: we get the Component Manager for the current wiki since in our example we want to register our Script Service
// Component only in the current wiki. We could as well register it in the Root Component Manager for all wikis.
def componentManager = services.component.getComponentManager('wiki:' + services.wiki.currentWikiId)

// Parse the annotations of the class above to generate a Component Descriptor to register the class as a Component in the Component Manager.
def loader = new ComponentAnnotationLoader()
def descriptors = loader.getComponentsDescriptors(HelloWorldGroovyScriptService.class)

// Note: Annotations can define several descriptors (by implementing several roles) so we iterate over all of them and register the Component
for (descriptor in descriptors) {
    componentManager.registerComponent(descriptor)
}
{{/groovy}}

How do I “pass” the service variable in this case?

it turns out that the service variable should either be passed when registering the component

componentManager.registerComponent(descriptor)

or received “inside” it in some other way.

OK, your HelloWorldGroovyScriptService is actually not a wiki component at all. You are using a wiki component as a hack to inject at startup a Java component implemented in Groovy.

In this case you need to do the same as in Java, stop using services (which is strictly a script oriented API), and @Inject the components you need. See https://extensions.xwiki.org/xwiki/bin/view/Extension/Component%20Module for more about components in general and https://extensions.xwiki.org/xwiki/bin/view/Extension/Query%20Module#HFromJavacomponents for the specific part you are apparently interested in.

By the way, for this use case I would recommend using https://extensions.xwiki.org/xwiki/bin/view/Extension/Script%20Component/ (it’s an extension which needs to be installed) which makes a lot easier to implement your component in Groovy.

In this case you need to do the same as in Java, stop using services (which is strictly a script oriented API), and @Inject the components you need.

Now it’s clear. Thanks tmortagne!.

I installed the extension and made an example as in the documentation.
https://extensions.xwiki.org/xwiki/bin/view/Extension/Script%20Component/
And it works:

But as soon as I add an inject it stops working:

As if something is missing. In the documentation, I see that the example has both an inject and an import. Maybe I missed something?

The current component code:

package org.xwiki.testcomponent.script;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.component.annotation.Component;
import org.xwiki.script.service.ScriptService;

@Component
@Named("testcomponent")
@Singleton
public class TestComponentScriptService implements ScriptService
{
 @Inject
 private QueryManager queryManager;
  
 public String get()
  {
    return "toto";
  }
}

Just a small note that you shouldn’t use the org.xwiki package as it’s reserved for XWiki code and could cause namespace clashing :slight_smile:

@tmortagne would be nice to update the doc :wink:

Regarding your problem I think you’re missing the import for the QueryManager class.

Unfortunately, it still doesn’t work.
if I add to import:

import org.xwiki.query.QueryManager;

inject method is worked:

package org.xwiki.testcomponent.script;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.component.annotation.Component;
import org.xwiki.script.service.ScriptService;
import org.xwiki.query.QueryManager;

@Component
@Named("testcomponent")
@Singleton
public class TestComponentScriptService implements ScriptService
{
 @Inject
 private QueryManager queryManager;

 public String get()
  {
 Query query = this.queryManager.createQuery("");
 
return '123'; 
  }
}

but trying to create a query as in the documentation does not work.

Query query = this.queryManager.createQuery("");

Maybe somewhere there is a working example of a component created in a similar way where there is work with queries?
For example, some kind of extension or a working example in the documentation.

So far, all the examples from the documentation and all the tips do not lead to working code(

It’s Groovy code! So you need imports for all the classes you use.

Query is import org.xwiki.query.Query.

Use wildcards to make your life simpler, as in: import org.xwiki.query.*

Thanks! Everything worked. The reason was an incomplete method. I took an example from the documentation.

Query query = this.queryManager.createQuery("select doc.space, doc.name, doc.author from XWikiDocument doc, BaseObject obj where doc.fullName=obj.name and obj.className='XWiki.WikiMacroClass'");

Here is the working code:

package org.xwiki.testcomponent.script;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

import org.xwiki.component.annotation.Component;
import org.xwiki.script.service.ScriptService;
import org.xwiki.query.*

@Component
@Named("testcomponent")
@Singleton
public class TestComponentScriptService implements ScriptService
{
 @Inject
 private QueryManager queryManager;

 public String get()
  {
 def result = this.queryManager.createQuery("", Query.XWQL).execute();
 
return result ; 
  }
}

Thank you so much for your help :slight_smile:

You should have the related error in the general log.